如何破解已簽名JAR包

本文版權歸贛州兆鑫軟件全部,原創未經許可禁止轉載!
本文僅做學習交流目的,提倡軟件正版,反對盜版!html


工欲善其事必先利其器

作事要先掌握大局

  1. 將已簽名包轉化爲未簽名包;
  2. 定位關鍵代碼位置;
  3. 修改class文件字節碼;
  4. 替換class文件從新打包;

不行動總也不能成功

1.將已簽名包轉化爲未簽名包

若是你用到的Jar文件使用了簽名,它會保證裏面的每一個class文件不能被修改,因此即便你成功修改了class文件中的字節碼,獲得的Jar也是沒法運行的。這些通過簽名的Jar包的META-INF文件夾中通常包含了*.SF和相應的*.RSA文件。這些文件記錄Jar包中每一個文件的簽名信息,以保證代碼不被篡改。java

使用下面的方法能夠從新生成一個未簽名Jar包。參考自stackoverflow做者Houtman的回答oracle

// 使用JDK編譯代碼
javac JarUnsigner.java

// 執行JarUnsigner,若是是同一個文件夾
java -cp . JarUnsigner <inJar> <outJar>

附上源代碼,省得回答被刪jvm

// JarUnsigner.java
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class JarUnsigner {

  private static final String MANIFEST = "META-INF/MANIFEST.MF";

  public static void main(String[] args){

    if (args.length!=2){
      System.out.println("Arguments: <infile.jar> <outfile.jar>");
      System.exit(1);
    }
    String infile = args[0];
    String outfile = args[1];
    if ((new File(outfile)).exists()){
      System.out.println("Output file already exists:" + outfile);
      System.exit(1);
    }
    try{
      ZipFile zipFile = new ZipFile(infile);
      final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outfile));
      for (Enumeration e = zipFile.entries(); e.hasMoreElements();) {
        ZipEntry entryIn = (ZipEntry) e.nextElement();

        if (! exclude_file( entryIn.getName() ) ) {

          /* copy the entry as-is */
          zos.putNextEntry( new ZipEntry( entryIn.getName() ));
          InputStream is = zipFile.getInputStream(entryIn);
          byte[] buf = new byte[1024];
          int len;
          while ((len = (is.read(buf))) > 0) {
            zos.write(buf, 0, len);
          }
          zos.closeEntry();

        } else {

          if (MANIFEST.equals(entryIn.getName())){
            /* if MANIFEST, adjust the entry */
            zos.putNextEntry(new ZipEntry(MANIFEST));

            // manifest entries until first empty line. i.e. the 'MainAttributes' section
            // (this method is used so to keep the formatting exactly the same)
            InputStream mIS = zipFile.getInputStream(entryIn);
            BufferedReader in = new BufferedReader(new InputStreamReader(mIS));
            String line = in.readLine();
            byte[] mNL = "\n".getBytes("UTF-8");
            while( line != null && !line.trim().isEmpty() ) {
              zos.write( line.getBytes("UTF-8"));
              zos.write( mNL );
              line = in.readLine();
            }
            zos.write( mNL );
            zos.closeEntry();

          }else{
            /* else: Leave out the Signature files */
          }

        }

      }
      zos.close();
      System.out.println("Successfully unsigned " + outfile);

    }catch(IOException ex){
      System.err.println("Error for file: " + infile);
      ex.printStackTrace();
      System.exit(1);
    }
  }

  /**
   * Exclude .SF signature file
   * Exclude .RSA and DSA (signed version of .SF file) 
   * Exclude SIG-  files  (unknown sign types for signed .SF file)
   * Exclude Manifest file
   * @param filename
   * @return 
   */
  public static boolean exclude_file(String filename){
    return filename.equals("META-INF/MANIFEST.MF") ||
           filename.startsWith("META-INF/SIG-") || 
           filename.startsWith("META-INF/") && ( filename.endsWith(".SF") || filename.endsWith(".RSA") || filename.endsWith(".DSA") );
  }

}


2. 定位代碼關鍵位置

舉一個須要輸入序列號才能試用的庫文件的例子,可是爲了保護Jar包做者權益,對包名進行打碼了。經過Jar包的說明書,能夠知道如何使用它,就知道是什麼地方輸入序列號啦,要否則是個正常人也無法用對吧。例如ide

// 文檔說這樣子能夠驗證序列號
authentication.User("333");
authentication.Serial("94306-56191-128286-2967422");

rock & roll

  1. 使用JD反編譯Jar包,而後Save All Sources,具體步驟這裏省略;
  2. 從IDEA新建一個工程,把反編譯的源代碼放到源目錄,Jar包多是通過混淆過的,不過這個關係不大,咱們字節碼都能改,還怕看不懂混淆?繼續往下走
  3. 右擊authentication這個類文件,選擇Find Usages,快捷鍵通常是Ctrl+G,看到有個叫作p.java的文件用到,作了一些判斷操做Blabla,看屏幕截圖1:

屏幕截圖1

  1. 點開這個p.java類搜索到的地方,能夠看到,它在比較一個返回結果,而後給出不一樣的錯誤提示,這個結果是由一個t.a(三個參數)的方法計算獲得,而且看得出來當計算結果的a屬性值爲0時就是Licence OK!,看屏幕截圖2:

屏幕截圖2

  1. 好,咱們再來看t.java這個文件的a方法又有什麼東東(按住Ctrl鍵,鼠標點擊那個t.a),咱們只要保證他獲得的結果的a屬性等於0就行了,能夠看到只有一種狀況下a屬性纔會等於0,其餘時候都是等於-1的,到這一步IDEA的使命就完成了,看屏幕截圖3:

屏幕截圖3

結果

經過上面5步(取決於不一樣的包,不必定都是5步),咱們知道了須要改動t類裏面的a方法,讓它裏面操做屬性a時,值爲-1的都改成0就能成功。工具

3. 修改class文件字節碼

接下來就要使用dirtyJOE軟件來定位並修改字節碼了,做者也試過Class Editor, Java Bytecode Editor都因年久失修,沒改爲功。經過查閱JVM文檔咱們知道給整數賦值有幾種指令,這裏就說兩種:學習

  • iconst_<i> i能夠爲 m1,0,1,2,3,4,5,分別設置的值爲-1,0,1,2,3,4,5,指令16進制字節碼分別是02設置-1,03設置0,04設置1,以此類推
  • bipush <i> i範圍能夠是0-255,指令16進制字節碼是10,好比代碼裏面有的21就是bipush 21,16字節碼錶示爲10 15

let's go

  1. 使用dirtyJOE打開t.class文件,切換到 Methods 頁上,以下圖所示:

image.png

  1. 雙擊咱們須要修改的方法(同名時經過比對方法簽名來區分),進入編輯界面,圖中的兩個指令的組合就是將值-1賦值給t$a.a屬性,雙擊圖中的iconst_ml,字節碼是02,改成03,而後回車便可,可使用Ctrl+F查找多處進行修改:

image.png

  1. 關閉編輯窗口,保存修改。

warning

要保證字節碼的數量不增多,也不減小,由於類和類之間代碼調用跟字節位置關係密切。否則會致使修改的class文件沒法使用。原來一行是1個字節的,繼續用1個字節,2個的就繼續兩個,3個的繼續保持三個。this

4. 替換class文件從新打包

這是最簡單的一步,將修改好的class文件,使用zip壓縮工具替換掉便可。若是是你Windows用戶不推薦使用WinRaR,能夠將jar文件(第一步生成的未簽名包)重命名爲zip文件,而後選擇用Windows資源管理器打開,將修改的class文件複製粘貼進去。idea

相關文章
相關標籤/搜索