|
論壇說明 |
歡迎您來到『史萊姆論壇』 ^___^ 您目前正以訪客的身份瀏覽本論壇,訪客所擁有的權限將受到限制,您可以瀏覽本論壇大部份的版區與文章,但您將無法參與任何討論或是使用私人訊息與其他會員交流。若您希望擁有完整的使用權限,請註冊成為我們的一份子,註冊的程序十分簡單、快速,而且最重要的是--註冊是完全免費的! 請點擊這裡:『註冊成為我們的一份子!』 |
|
主題工具 | 顯示模式 |
2006-02-13, 12:24 PM | #1 (permalink) |
榮譽會員
|
淺談JAVA程序破解
淺談JAVA程序破解
淺談JAVA程序破解 作者:舵手 申明:如轉載請保證文章的完整性以及出處 最近對JAVA程序的破解比較感興趣,拿幾個行業軟體練了一下手,略有心得,拿出來與菜鳥分享!注意只是一點心得, 本文並不涉及具體軟體的破解。初學破解,失誤之處在所難免,敬請高手賜教! 直接進入正題,對JAVA的破可從下面幾方面入手: 一、反編譯 工具很多,建意用GUI工具,指令行下的JAD很容易因為不能反編譯某一個方法或某一行程式碼而終止整個文件的反 編譯,但GUI的工具卻能搞定,雖然反編譯後部分程式碼較難看懂,但總比看jvm指命要好得多。而且,GUI的工具多數有 批量反編譯功能,且能讓反編譯的文件直接以.java為後面儲存,也是方便之處。 二、方法使用 安全意識強的開發者會把他的程序進行高品質的混淆,下面就是一個例子 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; } } 這是我見過的最好的混淆效果,變數都是由大小寫的O和數位零組程,要看懂這樣的程序基本上是不可能的,可能有 人會想到用有意義的變數進行取代,當然這也是一個方法,但如果套用所包括的class文件數以千記,那這個工作量 是相當大的。B/S結構的使用權方式一般都是文件的形式,當然,肯定是經過加密的。像下面的license就是經過了RSA 非對稱加密算法,要分析license的構成,有明文的license就更方便了,而公鑰是直接被寫在class文件中的 24D568B6A27AEFD683BC7A1CC93D11D074FB6B982A4B6E269712773BE536B40A67F1D345654F659C66D4265F5CE8FE0494B3A F33A8299A4F6B0E7500275A27EFF3B6D2E4983F14A9EA38A1AE3394B28A9C6D6924C15027F9B689FD9A3A689A301C4D4EB878 D75C207F68BAA352F550D8F19876FFA255864FDE8A7E5939202E9F 那麼我們可以用eclipse建一個JAVA項目,把套用的jar加入該項目的庫搜尋路徑,寫一個自己的類使用解密方法,得到 明文license再分析。當然,也可以使用其它一些方法,從使用參數和最後的返回值我們也可大概猜對該方法的作用, 對付象上面經過高品質混淆的程式碼也比較管用。當然,我這裡只是簡單的舉兩個例子,其實「方法使用」的妙用還很多, 自己慢慢琢磨吧! 三、為class增加程式碼 反編譯多數情況下也只能讓我們看看作者的思法,如果想把反編譯出來的程式碼經過修改後再編譯成class,通常 是行不通了。而且有時候必須讓程序執行在它本身的環境才能行,否則一些類無法得到正確的啟始化,「方法使用」 也就起不了什麼作用。搞過java的人一定知道javassist,這個庫提供了足夠多的方法讓你直接修改class文件,而不 需要你瞭解字元碼的相關知識,我們可以利用這個庫解決上述的問題。下面是我寫的一個修改字元碼的類,目前還不 完善,真正要用時可能需要根據情況做一些修改。 import java.lang.reflect.*; import javassist.*; import java.io.*; /** * Title: JAVA 字元碼修改類 * Description: 得到類的相關資訊或修改該類 * Copyright: Copyright () 2005 * @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; /** * 修改字元碼中的方法 * @param clas[0] 待修改類的方法名 * @param clas[1] 修改位置定義 * @param clas[2] 使用insertAt方法插放程式碼時行號參數 * @param clas[3] 修改內容 * @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]).int類型()),clas[3])); } cc.writeFile(); } catch(Exception e) { e.printStackTrace(); } } /** * 在類中增加方法 * @param clas[0] 源方法名稱 * @param clas[1] 新方法名稱 * @param clas[2] 增加檔案類型 * @param clas[3] 方法內容 * @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.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.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("請輸入操作類名:" ; 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("可供使用的方法有:" ; System.out.println("1-modifyMethod,2-addMethod,3-getMethods,4-getFields,5-removeMethod" ; System.out.println("***********************************************************" ; System.out.print("請選項使用方法:" ; 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("使用 modifyMethod 方法參數:" ; 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("," ; modifyMethod(); } buf.delete(0,buf.length()); if (call_method == 2) { System.out.println("***********************************************************" ; System.out.println("使用 addMethod 方法參數:" ; 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("," ; addMethod(); } if (call_method == 3) { getMethods(); } if (call_method == 4) { getFields(); } if (call_method == 5) { System.out.println("***********************************************************" ; System.out.println("使用 removeMethod 方法參數:" ; 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方法用來在類的指定方法中插入一行或多行程式碼,參數為a時表示插在方法現有程式碼的最後面,為b時 表示插在方法現有程式碼的最前面,為i時表時插在程式碼的指定行的前面,這個行和原程式碼中的行沒有關係,插入位置 要插入一次才能確定,為i時返回的值代表實際插入位置,由這個實際插入位置你可以計算i的值。在實際破解中發現, 用該方法插入一些程式碼後,會使原來反編譯的不可讀的程式碼變的容易讀懂,當然,也有可能使本來可讀性很強的程式碼, 因為你插入了一些語句而變得不可讀。我常常在關鍵方法的程式碼中插入一些 System.out.println();這樣的程式碼來跟蹤 程序,還有一點限制,你不能直接用列印輸出的方法來輸出方法體內的局部變數,但你可以對全局變數進行引用操作。 如果要操作局部變數,目前我所知的方法只能在該類裡重建該方法,如果那位有其它的好辦法,也請指點我一下。 addMethod方法在是類中增加一個新的方法,增加的方式有兩種,這裡就不做具體介紹。 其它方法也就不一一解釋了,有興趣的朋友可以研究一下javassist,相信你會寫出功能更強大的修改class文件的類庫。 四、class的修改 在破解程序中經常會看到rsa非對稱加密算法,公鑰往往以十六進制存放在class文件中,(當然,也有對公鑰加密後存 放在組態文件中的程序)以便解密已經加密過的訊息。前不久破解的一個J2EE的開發平台就是這樣的,license用RSA加密, 在搞懂了它的算法後,自己構件license明文,自己再產生一對rsa的公私密鑰,用自己的私鑰對license文明進行RSA加密, 再用十六進制編輯器取代程序中所有的公鑰,(當然是用你的公鑰取代他的公鑰,不然也沒法解密)一切就搞定。當然, 我所說的只是一個方面,有時暴破時可能還得用到一些JVM的指命,比如你想讓一個 return false 的方法 return ture 那你就得把相應位置的03 AC改為04 AC,位置怎麼確定就不用我說了吧! 五、讀JVM指令 沒有什麼可以多說的,如果要從jvm指令看懂成程,必須像熟彙編一樣熟悉jvm指令集,還得有一個工具把class翻譯成 jvm指令程式碼,總不能用十六進制編輯器讀程式碼嗎?如果真是,那你就太棒了。我這裡介紹 bcel 這個工具,它可以把class 解釋為jvm指令集並存為html文件,結果就像下面: 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 這是一個方法的全部指令,熟悉jvm指令集的話就已經能讀懂它在做什麼了 |
__________________ |
|
送花文章: 3,
|
向 psac 送花的會員:
|
longlie (2007-10-21)
感謝您發表一篇好文章 |
2006-08-24, 10:43 AM | #2 (permalink) |
榮譽會員
|
java環境變數
java環境變數 我正在學java,看到很多博客上都有關於java環境變數的設置,我也不厭其煩的重寫一變,借此瞭解一下整個系統的環境變數。 JAVA_HOME,CLASSPATH,PATH. 只有這三個java環境變數。 JAVA_HOME指向的是JDK的安裝路徑,如C:\j2sdk1.4.2_09,在這路徑下你應該能夠找到bin、lib等目錄。當然,你願意放哪裡,就放哪裡。我的是放在c盤根目錄 JAVA_HOME=C:\j2sdk1.4.2_09; PATH環境變數,目的是為了指向JDK的bin目錄,這裡面放的是各種編譯執行命令。 我的設置是: PATH=C:\j2sdk1.4.2_09\bin;C:\j2sdk1.4.2_09\jre\bin; 需要說明,系統中本身就有PATH環境變數,只要把C:\j2sdk1.4.2_09\bin;C:\j2sdk1.4.2_09\jre\bin;直接放到後面即可,中間有分號間隔。 如果你的JAVA_HOME是別的目錄,就對照著該吧。 CLASSPATH最重要。 CLASSPATH=.;C:\j2sdk1.4.2_09\lib\dt.jar;C:\j2sdk1.4.2_09\lib\tools.jar;這時我的設置。這是類的路徑。前面加上點和分號,意為首先在當前目錄查找,以後你自己編寫類的時候自然明白這點。 那麼為什麼要設置環境變數,以前編寫c語言的時候怎麼不設置呢? 由於WINDOWS預定的搜索順序,先搜索當前目錄的,再搜索系統目錄的,再搜索PATH環境變數設定的。你在編寫java程式時,在一個指定目錄,這裡沒有編譯執行命令,而系統目錄裡面,也沒有編譯執行命令。所以放在環境變數裡面, 從這裡你應該可以看出,環境變數是幹什麼用的了。簡單說就是告訴操作系統到那裡去找指定的文件。你要是把系統目錄給改了,看你用dos命令還好不好使。 配置完後,在命令提示字元下,鍵入java -version,如果出現java的一些訊息,說明配置成功。 |
送花文章: 3,
|
向 psac 送花的會員:
|
longlie (2007-10-21)
感謝您發表一篇好文章 |