淺談JAVA程序破解
作者:舵手
申明:如轉(zhuǎn)載請(qǐng)保證文章的完整性以及出處
????最近對(duì)JAVA程序的破解比較感興趣,拿幾個(gè)行業(yè)軟件練了一下手,略有心得,拿出來(lái)與菜鳥(niǎo)分享!注意只是一點(diǎn)心得,
本文并不涉及具體軟件的破解。初學(xué)破解,失誤之處在所難免,敬請(qǐng)高手賜教!
????直接進(jìn)入正題,對(duì)JAVA的破可從下面幾方面入手:
一、反編譯
????工具很多,建意用GUI工具,命令行下的JAD很容易因?yàn)椴荒芊淳幾g某一個(gè)方法或某一行代碼而終止整個(gè)文件的反
編譯,但GUI的工具卻能搞定,雖然反編譯后部分代碼較難看懂,但總比看jvm指命要好得多。而且,GUI的工具多數(shù)有
批量反編譯功能,且能讓反編譯的文件直接以.java為后綴保存,也是方便之處。
二、方法調(diào)用
????安全意識(shí)強(qiáng)的開(kāi)發(fā)者會(huì)把他的程序進(jìn)行高質(zhì)量的混淆,下面就是一個(gè)例子
public?static?Object?getRemoteEJBHome(String?OOOoOo00oO0O0O0ooOoOO,?Class?OO0oOO0O0o0oO0o00oOoO)
throws?NamingException
{
try
{
????if(OoO0o0o0O0oo0oO00oOO0?==?null)
??OoO0o0o0O0oo0oO00oOO0?=?OoOOoOOO0Oo0OO0OooO0o();
????Object?OOOOOo00000OoOoO0O000?=?PortableRemoteObject.narrow(OoO0o0o0O0oo0oO00oOO0.lookup(OOOoOo00oO0O0O0ooOoOO),?OO0oOO0O0o0oO0o00oOoO);
????Object?obj?=?OOOOOo00000OoOoO0O000;
????return?obj;
}
catch(NamingException?OO0Ooo0oOO0OO0OOOoOo0)
{
????System.out.println(OO0Ooo0oOO0OO0OOOoOo0.getMessage());
????throw?OO0Ooo0oOO0OO0OOOoOo0;
}
}
這是我見(jiàn)過(guò)的最好的混淆效果,變量都是由大小寫的O和數(shù)字零組程,要看懂這樣的程序基本上是不可能的,可能有
人會(huì)想到用有意義的變量進(jìn)行替換,當(dāng)然這也是一個(gè)方法,但如果應(yīng)用所包括的class文件數(shù)以千記,那這個(gè)工作量
是相當(dāng)大的。B/S結(jié)構(gòu)的授權(quán)方式一般都是文件的形式,當(dāng)然,肯定是經(jīng)過(guò)加密的。像下面的license就是經(jīng)過(guò)了RSA
非對(duì)稱加密算法,要分析license的構(gòu)成,有明文的license就更方便了,而公鑰是直接被寫在class文件中的
24D568B6A27AEFD683BC7A1CC93D11D074FB6B982A4B6E269712773BE536B40A67F1D345654F659C66D4265F5CE8FE0494B3A
F33A8299A4F6B0E7500275A27EFF3B6D2E4983F14A9EA38A1AE3394B28A9C6D6924C15027F9B689FD9A3A689A301C4D4EB878
D75C207F68BAA352F550D8F19876FFA255864FDE8A7E5939202E9F
那么我們可以用eclipse建一個(gè)JAVA項(xiàng)目,把應(yīng)用的jar加入該項(xiàng)目的庫(kù)搜索路徑,寫一個(gè)自己的類調(diào)用解密方法,得到
明文license再分析。當(dāng)然,也可以調(diào)用其它一些方法,從調(diào)用參數(shù)和最后的返回值我們也可大概猜對(duì)該方法的作用,
對(duì)付象上面經(jīng)過(guò)高質(zhì)量混淆的代碼也比較管用。當(dāng)然,我這里只是簡(jiǎn)單的舉兩個(gè)例子,其實(shí)“方法調(diào)用”的妙用還很多,
自己慢慢琢磨吧!
三、為class添加代碼
????反編譯多數(shù)情況下也只能讓我們看看作者的思路,如果想把反編譯出來(lái)的代碼經(jīng)過(guò)修改后再編譯成class,通常
是行不通了。而且有時(shí)候必須讓程序運(yùn)行在它本身的環(huán)境才能行,否則一些類無(wú)法得到正確的初始化,“方法調(diào)用”
也就起不了什么作用。搞過(guò)java的人一定知道javassist,這個(gè)庫(kù)提供了足夠多的方法讓你直接修改class文件,而不
需要你了解字節(jié)碼的相關(guān)知識(shí),我們可以利用這個(gè)庫(kù)解決上述的問(wèn)題。下面是我寫的一個(gè)修改字節(jié)碼的類,目前還不
完善,真正要用時(shí)可能需要根據(jù)情況做一些修改。
import?java.lang.reflect.*;
import?javassist.*;
import?java.io.*;
/**
?*?<p>Title:?JAVA?字節(jié)碼修改類</p>
?*?<p>Description:?得到類的相關(guān)信息或修改該類</p>
?*?<p>Copyright:?Copyright?()?2005</p>
?*?@author?舵手
?*?@version?1.0
?*/
public?class?ModifyClass?{
??private?static?int?call_method;
??private?static?String?_class;
??private?static?ClassPool?pool;
??private?static?CtClass?cc;
??private?static?String[]?clas;
??/**
???*?修改字節(jié)碼中的方法
???*?@param?clas[0]?待修改類的方法名
???*?@param?clas[1]?修改位置定義
???*?@param?clas[2]?使用insertAt方法插放代碼時(shí)行號(hào)參數(shù)
???*?@param?clas[3]?修改內(nèi)容
???*?@return
???*/
????private?static?void?modifyMethod()
????{
????String?_method;
????_method?=?clas[0];
????????try
????????{
??????CtClass[]?param?=?new?CtClass[4]?;????????????????
??????//param[0]?=?pool.get("");
??????//param[1]?=?pool.get("");
??????//param[2]?=?pool.get("java.lang.String");
??????//param[3]?=?pool.get("java.lang.String");
??????CtMethod?cm?=?cc.getDeclaredMethod(_method);
??????if?(clas[1].toLowerCase().equals("a"))
??????{
????????//方法的尾部加入代碼
????????cm.insertAfter(clas[3]);
??????}
??????if?(clas[1].toLowerCase().equals("b"))
??????{
????????//方法的首部加入代碼
????????cm.insertBefore(clas[3]);
??????}
??????if?(clas[1].toLowerCase().equals("i"))
??????{
????????System.out.println(cm.insertAt((Integer.valueOf(clas[2]).intValue()),clas[3]));
??????}
??????cc.writeFile();
????????}
????????catch(Exception?e)
????????{
????????????e.printStackTrace();
????????}
????}
??/**
???*?在類中增加方法
???*?@param?clas[0]?源方法名稱
???*?@param?clas[1]?新方法名稱
???*?@param?clas[2]?增加類型
???*?@param?clas[3]?方法內(nèi)容
???*?@return
???*/
??private?static?void?addMethod()
????{
????String?_oldmethod;
????String?_newmethod;
????_oldmethod?=?clas[0];
????_newmethod?=?clas[1];
????????try
????????{
??????StringBuffer?newMethodBody?=?new?StringBuffer();
????????????if?(clas[2].toLowerCase().equals("c"))
????????????{
????????//add?new?Method?(copy)
????????CtMethod?oldMethod?=?cc.getDeclaredMethod(_oldmethod);
????????CtMethod?newMethod?=?CtNewMethod.copy(oldMethod,?_newmethod,?cc,?null);
????????newMethodBody.append(clas[3]);
????????newMethod.setBody(newMethodBody.toString());
????????cc.addMethod(newMethod);
????????????}
??????if?(clas[2].toLowerCase().equals("r"))
??????{
????????//add?new?Method?(create)
????????CtMethod?newMethod?=?CtNewMethod.make(clas[3],?cc);
????????cc.addMethod(newMethod);
??????}
??????cc.writeFile();
????????}
????????catch(Exception?e)
????????{
????????????e.printStackTrace();
????????}
????}
??private?static?void?getMethods(){
????CtMethod[]?cms?=?cc.getDeclaredMethods();
????System.out.println();
????System.out.println(cc.getName()+"?類的所有方法:");
????for?(int?i=0?;?i<cms.length?;?i++?)
????{
??????System.out.println(cms[i].getName());
????}
??}
??private?static?void?getFields(){
????CtField[]?cfs?=?cc.getDeclaredFields();
????System.out.println();
????System.out.println(cc.getName()+"?類的所有屬性:");
????for?(int?i=0?;?i<cfs.length?;?i++?)
????{
??????System.out.println(cfs[i].getName());
????}
??}
??
??private?static?void?delMethod(){
????try{
??????CtMethod?cm?=?cc.getDeclaredMethod(clas[0]);
??????cc.removeMethod(cm);
????}catch(Exception?e){
??????e.printStackTrace();
????}
??}
????public?static?void?main(String[]?args)?{
????StringBuffer?buf?=?new?StringBuffer(500);
????int?c;
????System.out.print("請(qǐng)輸入操作類名:");
????try{
??????while?((c?=?System.in.read())?!=?13)?{
????????buf.append((char)c);
??????}
??????_class?=?buf.toString();
??????pool?=?ClassPool.getDefault();
??????cc?=?pool.get(_class);
??????buf.delete(0,buf.length());
??????System.out.println("***********************************************************");
??????System.out.println("可供調(diào)用的方法有:");
??????System.out.println("1-modifyMethod,2-addMethod,3-getMethods,4-getFields,5-removeMethod");
??????System.out.println("***********************************************************");
??????System.out.print("請(qǐng)選擇調(diào)用方法:");
??????while?((c?=?System.in.read())?!=?13)?{
????????if?(c?==?10)
??????????continue;
????????buf.append((char)c);
??????}
??????call_method?=?Integer.parseInt(buf.toString());
??????if?(call_method?==?1)
??????{
????????System.out.println("***********************************************************");
????????System.out.println("調(diào)用?modifyMethod?方法參數(shù):");
????????System.out.println("方法名稱,插入位置,行號(hào),內(nèi)容");
????????System.out.println("***********************************************************");
????????buf.delete(0,buf.length());
????????while?((c?=?System.in.read())?!=?13)?{
??????????if?(c?==?10)
????????????continue;
??????????buf.append((char)c);
????????}
????????clas?=?(buf.toString()).split(",");
????????modifyMethod();
??????}
??????buf.delete(0,buf.length());
??????if?(call_method?==?2)
??????{
????????System.out.println("***********************************************************");
????????System.out.println("調(diào)用?addMethod?方法參數(shù):");
????????System.out.println("源方法,目標(biāo)方法,建立方式,內(nèi)容");
????????System.out.println("***********************************************************");
????????buf.delete(0,buf.length());
????????while?((c?=?System.in.read())?!=?13)?{
??????????if?(c?==?10)
????????????continue;
??????????buf.append((char)c);
????????}
????????clas?=?(buf.toString()).split(",");
????????addMethod();
??????}
??????if?(call_method?==?3)
??????{
????????getMethods();
??????}
??????if?(call_method?==?4)
??????{
????????getFields();
??????}
??????if?(call_method?==?5)
??????{
????????System.out.println("***********************************************************");
????????System.out.println("調(diào)用?removeMethod?方法參數(shù):");
????????System.out.println("方法名稱");
????????System.out.println("***********************************************************");
????????buf.delete(0,buf.length());
????????while?((c?=?System.in.read())?!=?13)?{
??????????if?(c?==?10)
????????????continue;
??????????buf.append((char)c);
????????}
????????clas?=?(buf.toString()).split(",");
????????delMethod();
??????}
????}catch(IOException?ioe)
????{
??????System.out.println();
??????ioe.printStackTrace();
??????System.exit(0);
????}
????catch(NotFoundException?nfe)
????{
??????System.out.println();
??????nfe.printStackTrace();
??????System.exit(0);
????}
????catch(NumberFormatException?nfe)
????{
??????System.out.println();
??????nfe.printStackTrace();
??????System.exit(0);
????}
????}
}
????modifyMethod方法用來(lái)在類的指定方法中插入一行或多行代碼,參數(shù)為a時(shí)表示插在方法現(xiàn)有代碼的最后面,為b時(shí)
表示插在方法現(xiàn)有代碼的最前面,為i時(shí)表時(shí)插在代碼的指定行的前面,這個(gè)行和原代碼中的行沒(méi)有關(guān)系,插入位置
要插入一次才能確定,為i時(shí)返回的值代表實(shí)際插入位置,由這個(gè)實(shí)際插入位置你可以計(jì)算i的值。在實(shí)際破解中發(fā)現(xiàn),
用該方法插入一些代碼后,會(huì)使原來(lái)反編譯的不可讀的代碼變的容易讀懂,當(dāng)然,也有可能使本來(lái)可讀性很強(qiáng)的代碼,
因?yàn)槟悴迦肓艘恍┱Z(yǔ)句而變得不可讀。我常常在關(guān)鍵方法的代碼中插入一些?System.out.println();這樣的代碼來(lái)跟蹤
程序,還有一點(diǎn)限制,你不能直接用打印輸出的方法來(lái)輸出方法體內(nèi)的局部變量,但你可以對(duì)全局變量進(jìn)行引用操作。
如果要操作局部變量,目前我所知的方法只能在該類里重建該方法,如果那位有其它的好辦法,也請(qǐng)指點(diǎn)我一下。
????addMethod方法在是類中增加一個(gè)新的方法,增加的方式有兩種,這里就不做具體介紹。
????其它方法也就不一一解釋了,有興趣的朋友可以研究一下javassist,相信你會(huì)寫出功能更強(qiáng)大的修改class文件的類庫(kù)。
四、class的修改
????在破解過(guò)程中經(jīng)常會(huì)看到rsa非對(duì)稱加密算法,公鑰往往以十六進(jìn)制存放在class文件中,(當(dāng)然,也有對(duì)公鑰加密后存
放在配置文件中的程序)以便解密已經(jīng)加密過(guò)的信息。前不久破解的一個(gè)J2EE的開(kāi)發(fā)平臺(tái)就是這樣的,license用RSA加密,
在搞懂了它的算法后,自己構(gòu)件license明文,自己再生成一對(duì)rsa的公私密鑰,用自己的私鑰對(duì)license文明進(jìn)行RSA加密,
再用十六進(jìn)制編輯器替換程序中所有的公鑰,(當(dāng)然是用你的公鑰替換他的公鑰,不然也沒(méi)法解密)一切就搞定。當(dāng)然,
我所說(shuō)的只是一個(gè)方面,有時(shí)暴破時(shí)可能還得用到一些JVM的指命,比如你想讓一個(gè)?return?false?的方法?return?ture
那你就得把相應(yīng)位置的03?AC改為04?AC,位置怎么確定就不用我說(shuō)了吧!
五、讀JVM指令
????沒(méi)有什么可以多說(shuō)的,如果要從jvm指令看懂成程,必須像熟匯編一樣熟悉jvm指令集,還得有一個(gè)工具把class翻譯成
jvm指令代碼,總不能用十六進(jìn)制編輯器讀代碼嗎?如果真是,那你就太牛了。我這里介紹?bcel?這個(gè)工具,它可以把class
解釋為jvm指令集并存為html文件,結(jié)果就像下面:
0??getstatic????????????System.out?Ljava/io/PrintStream;?
3??ldc??????????????????"is?one"?
5??invokevirtual????????java.io.PrintStream.println?(Ljava/lang/String;)V(String):void?
8??getstatic?????????????System.out?Ljava/io/PrintStream;?
11??ldc??????????????????"is?two"?
13??invokevirtual????????java.io.PrintStream.println?(Ljava/lang/String;)V(String):void?
16??getstatic?????????????System.out?Ljava/io/PrintStream;?
19??ldc??????????????????"is?three"?
21??invokevirtual????????java.io.PrintStream.println?(Ljava/lang/String;)V(String):void?
24??getstatic?????????????System.out?Ljava/io/PrintStream;?
27??ldc??????????????????"is?four"?
29??invokevirtual????????java.io.PrintStream.println?(Ljava/lang/String;)V(String):void?
32??return?
????這是一個(gè)方法的全部指令,熟悉jvm指令集的話就已經(jīng)能讀懂它在做什么了
????發(fā)現(xiàn)有關(guān)JAVA程序破解的文章不是很多,所以本人粗淺的談?wù)摿艘幌翵AVA程序破解時(shí)所用到的一些方法,當(dāng)然,還有很多
憑經(jīng)驗(yàn)才能找到的靈感,無(wú)法一一列舉,本文質(zhì)在拋磚引玉,望高手能寫一些技術(shù)含量更高的文章供我們這些菜鳥(niǎo)學(xué)習(xí)。