• 常用破解教程仅供参考,仅供学习用途


    破解过程大致如下
    
    // 反编译插件
    // 修改注册逻辑代码
    // 字节码写入
    // 重新打包
    // 替换jar包

    示例:Iedis破解思路 

    Iedis是IDEA上的一个收费redis插件,java编写的,既然是java写的,收费这些自然是很容易绕过的。由于Java太容易被反编译,作者还是做了些代码混淆和字符串加密。

    利用JD-GUI反编译出来的代码片段

    可以看出类名和字符串都被混淆和加密了,从代码上很难去定位和分析他的注册流程。

    package com.seventh7.widget.iedis.config;
    
    import com.intellij.icons.AllIcons.General;
    import com.intellij.openapi.actionSystem.AnActionEvent;
    import com.intellij.openapi.ui.Messages;
    
    class i
      extends e
    {
      private static final String[] ib;
      private static final String[] jb;
      
      i(n paramn)
      {
        super(a(20539, 52876), a(20539, 52876), AllIcons.General.Remove, a(20537, 55341), paramn);
      }
      
      void a(AnActionEvent paramAnActionEvent, P paramP)
      {
        String str1 = String.format(a(20538, 25199), new Object[] { paramP.f() });
        String str2 = a(20536, 20012);
        try
        {
          if (Messages.showOkCancelDialog(a(), str1, str2, Messages.getQuestionIcon()) == 0) {
            com.seventh7.widget.iedis.d.e.a().a(a(), paramP.b());
          }
        }
        catch (RuntimeException localRuntimeException)
        {
          throw d(localRuntimeException);
        }
      }

    破解的2个思路

    • 还原代码中的所有加密字符串,根据字符串的内定位到相关的代码,利用javassist修改class文件,将文件替换掉原来的文件
    • 逆向出他的认证算法,然后做个注册机之类的。iedis是采用服务器认证的,每次启动都要去服务器查询激活,所以注册机不适合。但是我们可以本地架设一个认证服务。

    架设认证服务器还是比较简单的,下面还是主要研究一下第一种思路。

    还原字符串

    从那些混淆的代码去定位软件的运行逻辑很难下手,但是我们可以换个思路,将软件运行过程中字符串都打印出来,这样我们基本上就可以得到一份软件的运行日志,对java程序进行运行时插入语句看似很麻烦,其实JVM默认就支持javaagent,写个javaagent即可达到效果,javaagent的使用可以参考《javaagent-的使用》

    1. 编写javaagent程序
    /**
     * 给iedis的加密字符串函数 插入打印代码
     */
    public class IedisTransformer implements ClassFileTransformer {
    
    	private final static String IDEA_LIB="/Applications/IntelliJ IDEA.app/Contents/lib/*";
    	private final static String IDEIS_LIB="/Users/liaojiacan/Library/Application Support/IntelliJIdea2017.2/Iedis/lib/*";
    
    	public IedisTransformer() {
    		try {
    			ClassPool.getDefault().appendClassPath(IDEA_LIB);
    			ClassPool.getDefault().appendClassPath(IDEIS_LIB);
    		} catch (NotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    
    	@Override
    	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    
    		if(className.startsWith("com/seventh7/widget/iedis")){
    			try {
    				CtClass clazz = ClassPool.getDefault().makeClass(new ByteArrayInputStream(classfileBuffer));
    				CtMethod[] methods = clazz.getDeclaredMethods();
    				CtClass string = ClassPool.getDefault().getCtClass(String.class.getName());
    				for(CtMethod method :methods){
    
    					if(method.getLongName().startsWith("com.seventh7.widget.iedis.a.p.f")){
    						System.out.println("Inject :: SUCCESS!");
    						method.insertBefore("if(true){return true;} ");
    						continue;
    					}
    
    					if(method.getReturnType().equals(string)){
    						String name = method.getLongName();
    						System.out.println("transform the iedis method:"+name);
    						method.insertAfter("System.out.println("--------------------");" +
    									" System.out.println(""+name+""); " +
    									" System.out.println(java.util.Arrays.toString($args)); " +
    									" System.out.println("return:"+$_);");
    					}
    				}
    
    				return clazz.toBytecode();
    			} catch (IOException e) {
    				e.printStackTrace();
    			} catch (NotFoundException e) {
    				e.printStackTrace();
    			} catch (CannotCompileException e) {
    				e.printStackTrace();
    			}
    		}
    
    		return null;
    	}
    }
    1. 修改System.out,把所有的print打印到我们指定的文件中 /tmp/system.out
    2. public class Main {
      	public static void premain(String agentOps, Instrumentation inst) {
      		PrintStream out = null;
      		try {
      			out = new PrintStream("/tmp/system.out");
      		} catch (FileNotFoundException e) {
      			e.printStackTrace();
      		}
      		System.setOut(out);
      		System.setErr(out);
      
      		if ("iedis".equals(agentOps)){
      			inst.addTransformer(new IedisTransformer());
      		}else if("injectPrint".equals(agentOps)) {
      			inst.addTransformer(new InjectPrintTransformer());
      		}else {
      			inst.addTransformer(new SimpleTransformer());
      		}
      	}
      
      	public static void main(String[] args) {
      		System.out.println(helloWorld());
      	}
      
      	public static String helloWorld(){
      		return "This is a javaagent!";
      	}
      }
    3. 配置idea启动配置,加入我们的javaagent
    #修改idea.vmoptions文件加入下面一行配置
    -javaagent:/Users/liaojiacan/Workspace/tools/decomplie/javaagent/javaagent-1.0-SNAPSHOT.jar=iedis
    -Xms128m
    -Xmx750m
    -XX:ReservedCodeCacheSize=240m
    -XX:+UseCompressedOops
    -Dfile.encoding=UTF-8
    -XX:+UseConcMarkSweepGC
    -XX:SoftRefLRUPolicyMSPerMB=50
    -ea
    -Dsun.io.useCanonCaches=false
    -Djava.net.preferIPv4Stack=true
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:-OmitStackTraceInFastThrow
    -Xverify:none
    
    -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
    -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
    -Xbootclasspath/a:../lib/boot.jar

    启动Idea 后我们可以在/tmp/system.out中可以看到这些关键的日志

    --------------------
    com.seventh7.widget.iedis.L.a(java.lang.String)
    return:{"trailing":true,"daysLeft":9,"popup":true,"activated":false}
    --------------------
    com.seventh7.widget.iedis.B.a()
    return:186b474e0ffffffb70ffffff96680ffffffc0240ffffff89456b0fffffffa320ffffffa70ffffff92
    --------------------
    com.seventh7.widget.iedis.x.a(byte[])
    return:MTg2YjQ3NGUwZmZmZmZmYjcwZmZmZmZmOTY2ODBmZmZmZmZjMDI0MGZmZmZmZjg5NDU2YjBmZmZm
    ZmZmYTMyMGZmZmZmZmE3MGZmZmZmZjkyOjI=
    --------------------
    com.seventh7.widget.iedis.L.a(java.lang.String)
    return:{"trailing":true,"daysLeft":9,"popup":true,"activated":false}
    --------------------
    
    com.seventh7.widget.iedis.L.a(java.lang.String)
    [https://www.codesmagic.com/q2?t=MTg2YjQ3NGUwZmZmZmZmYjcwZmZmZmZmOTY2ODBmZmZmZmZjMDI0MGZmZmZmZjg5NDU2YjBmZmZmZmZmYTMyMGZmZmZmZmE3MGZmZmZmZjkyOjI=]
    return:{"trailing":true,"daysLeft":9,"popup":true,"activated":false}
    --------------------
    com.seventh7.widget.iedis.a.p.b(int,int)
    [-13938, -6118]
    return:trailing
    --------------------
    com.seventh7.widget.iedis.a.p.b(int,int)
    [-13937, -25088]
    return:daysLeft
    --------------------
    com.seventh7.widget.iedis.a.p.b(int,int)
    [-13939, 7216]
    return:popup
    

    从上面的日志可以看出一些关键点:

    • https://www.codesmagic.com/q2?t= 是注册的服务器

    • com.seventh7.widget.iedis.a.o 这个类是很关键的类

    • 认证服务器返回的认证结果为
      {“trailing”:true,“daysLeft”:9,“popup”:true,“activated”:false}

    查看反编译的代码,可以看出这个类是一个抽象类,他的唯一子类是com.seventh7.widget.iedis.a.p,根据外面获取到的运行日志,大概可以推断出 f这个方法是认证的方法。

    package com.seventh7.widget.iedis.a;
    
    import com.seventh7.widget.iedis.b.d.a;
    import java.util.Map;
    import java.io.IOException;
    import com.seventh7.widget.iedis.L;
    
    class p extends o
    {
        private static final String[] kb;
        private static final String[] lb;
        
        //基本上可以推断出 这个就是认证的方法,最直接的方法就是直接return true
        @Override
        protected boolean f() throws IOException {
            //this.d() 是调用https://www.codesmagic.com/q2去注册的
            //Map的返回值{"trailing":true,"daysLeft":9,"popup":true,"activated":false}
            final Map d = this.d();
            //trailing
            final boolean booleanValue = L.a(d, b(-13938, -6118));
            //this.d()执行后的异常信息。
            final a[] b = av.b();
            //daysLeft
            final int b2 = L.b(d, b(-13937, -25088));
            //popup
            final boolean booleanValue2 = L.a(d, b(-13939, 7216));
            boolean booleanValue3 = false;
            Label_0104: {
                Label_0074: {
                    boolean b3;
                    try {
                        b3 = (booleanValue3 = booleanValue);
                        if (b != null) {
                            break Label_0104;
                        }
                        if (b3) {
                            break Label_0074;
                        }
                        break Label_0074;
                    }
                    catch (IOException ex) {
                        throw b(ex);
                    }
                    try {
                        if (b3) {
                            this.a(b2, booleanValue2);
                            return false;
                        }
                    }
                    catch (IOException ex2) {
                        throw b(ex2);
                    }
                }
                //actived
                booleanValue3 = L.a(d, b(-13940, 8507));
            }
            final boolean b4 = booleanValue3;
            
            //如果已经过了试用,就检测激活
            Label_0122: {
                boolean b5;
                try {
                    final boolean b6;
                    b5 = (b6 = b4);
                    // b = av.b()
                    if (b != null) {
                        return b6;
                    }
                    if (!b5) {
                        break Label_0122;
                    }
                    return true;
                }
                catch (IOException ex3) {
                    throw b(ex3);
                }
                try {
                    if (!b5) {
                        this.c();
                        return false;
                    }
                }
                catch (IOException ex4) {
                    throw b(ex4);
                }
            }
            return true;
        }
        
        private static IOException b(final IOException ex) {
            return ex;
        }
    
      
    }
    

    从上面的分享结果可以看出,有两种破解思路

    • 方法一 修改 com.seventh7.widget.iedis.a.p.f 永远return true

    • 方法二 搭建一个认证服务器,本地替换host,认证服务器返回的结果为

    { "trailing": false, "popup": true, "activated": true, "daysLeft": 0 }
    

    方法一的实现

    public class IedisCracker {
    
    	private final static String IDEA_LIB="/Applications/IntelliJ IDEA.app/Contents/lib/*";
    	private final static String IDEIS_LIB="/Users/liaojiacan/Library/Application Support/IntelliJIdea2017.2/Iedis/lib/*";
    
    	public static void main(String[] args) {
    		try {
    			ClassPool.getDefault().appendClassPath(IDEA_LIB);
    			ClassPool.getDefault().appendClassPath(IDEIS_LIB);
    
    			CtClass clazz = ClassPool.getDefault().getCtClass("com.seventh7.widget.iedis.a.p");
    
    			CtMethod[] mds = clazz.getDeclaredMethods();
    			for(CtMethod method : mds){
    				if(method.getLongName().startsWith("com.seventh7.widget.iedis.a.p.f")){
    					System.out.println("Inject :: SUCCESS!");
    					try {
    						method.insertBefore("if(true){return true;} ");
    					} catch (CannotCompileException e) {
    						e.printStackTrace();
    					}
    					continue;
    				}
    			}
    
    			clazz.writeFile("/tmp/p.class");
    
    		} catch (NotFoundException e) {
    			e.printStackTrace();
    		} catch (CannotCompileException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }

    示例二:

     Javassist实现的破解IDEA MybatisPlugin修改字节码工具,仅供学习用途。

    https://github.com/An0nymous0/MybatisPlugin-Crack-Javassist 

    ps:

    javaagent 的使用:

    javaagent 是类似一个JVM的插件,利用JVM提供的Instrumentation API实现获取或者修改加载到JVM中的类字节码。

    编写一个javagent的jar的方式如下:

    1.实现一个ClassFileTransformer

    public class SimpleTransformer implements ClassFileTransformer {
    
    
    	@Override
    	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    		System.out.println(className);
    		System.out.println(protectionDomain.toString());
    		return new byte[0];
    	}
    }
    
     

    2.实现一个Premain-Class

    public class Main {
    	public static void premain(String agentOps, Instrumentation inst) {
    		inst.addTransformer(new SimpleTransformer());
    	}
    
    	public static void main(String[] args) {
    		System.out.println("This is a javaagent!");
    	}
    }
    

    3.MANIFEST.MF配置

    Manifest-Version: 1.0
    Premain-Class: com.github.liaojiacan.Main
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    Can-Set-Native-Method-Prefix: true
    

    4.运行命令

    java -javaagent:agent.jar -jar app.jar
    

    代码和assembly的打包配置可以参考

    github

  • 相关阅读:
    DRF 配置
    RESTful 设计方法(学习总结用)
    Chrome 开发者工具
    html中注释的php代码被解析
    正则
    局域网域名访问php项目
    jQuery 操作from表单数据序列化
    jQuery操作Table tr td方法
    鼠标焦点在input的某个位置上,点击一个button 在input光标处的增加文字
    858. Mirror Reflection
  • 原文地址:https://www.cnblogs.com/candlia/p/11919868.html
Copyright © 2020-2023  润新知