classes.dex硬編碼後重簽名安裝

前言:java

一、不少APK用apktool反編譯後,重打包失敗;shell

二、dex中smali指令硬編碼,安裝失敗;app

解決方法:工具

1、針對第一個問題,就是不反編譯APK,而是直接從APK包從用Zip工具(winzip)將classes.dex提取出來,再使用IDA等反編譯工具找到要修改指令的偏移,最後使用winhex等編輯工具修改指令,而後保存便可;ui

詳細修改指令的方法是將OPCODE直接填充到相應位置便可:編碼

原始JAVA代碼以下:code

    public static void b(String arg4, String arg5, int arg6, Throwable arg7) {
    	 Log.d(arg4, arg5);
        return;
    }

IDA中此代碼的opcode以下:ip


2、針對第二個問題,在classes.dex硬編碼完成後,須要校訂checksum和signture,使用下面的java程序修正classes.dex,get

代碼以下:input

package javaproject;


import java.security.*;
import java.util.zip.Adler32;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileOutputStream;

public class ReDEX {
    public static void main(String[] args) {
        if (args.length == 1) {
            try {
                File file = new File(args[0]);

                byte[] barr = null;
                barr = getBytesFromFile(file);

                System.out.print("Original Checksum: ");
                for(int i = 8; i<12; i+=4)
                    System.out.printf("0x%02X%02X%02X%02X ", barr[i+3], barr[i+2], barr[i+1], barr[i]);

                System.out.print("\nOriginal Signature: 0x");
                for(int i = 12; i<32; i+=4)
                    System.out.printf("%02X%02X%02X%02X ", barr[i], barr[i+1], barr[i+2], barr[i+3]);

                calcSignature(barr);
                calcChecksum(barr);

                System.out.print("\n\nNew Checksum: ");
                for(int i = 8; i<12; i+=4)
                    System.out.printf("0x%02X%02X%02X%02X ", barr[i+3], barr[i+2], barr[i+1], barr[i]);

                System.out.print("\nNew Signature: 0x");
                for(int i = 12; i<32; i+=4)
                    System.out.printf("%02X%02X%02X%02X ", barr[i], barr[i+1], barr[i+2], barr[i+3]);

                try{
                    String str = readUserInput("\nSave it(Yes or No):");
                    if (str.equalsIgnoreCase("yes")) {
                        putBytesToFile(barr, args[0]);
                        System.err.println("\nFixed.");
                        System.err.println(args[0]);
                    } else{
                        System.err.println("\nNothing");
                    }
                }
                catch(IOException except)
                {
                    except.printStackTrace();
                }
                
            }
            catch (Exception e) {
                System.err.println("File input error");
            }
        }
        else
            System.out.println("Invalid parameters");
    }


    private static String readUserInput(String prompt) throws IOException {
        System.out.print(prompt);
        InputStreamReader is_reader = new InputStreamReader(System.in);
        return new BufferedReader(is_reader).readLine();
    }

    public static byte[] getBytesFromFile(File file) throws IOException {
        InputStream is = new FileInputStream(file);

        // Get the size of the file
        long length = file.length();

        if (length > Integer.MAX_VALUE) {
            // File is too large
        }

        // Create the byte array to hold the data
        byte[] bytes = new byte[(int)length];

        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
            offset += numRead;
        }

        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file "+file.getName());
        }

        // Close the input stream and return bytes
        is.close();
        return bytes;
    }
    
    
    public static void putBytesToFile(byte[] data, String outfile) throws IOException {
        File destinationFile = new File(outfile);

        if (destinationFile.exists()) {
            System.out.println("overwrite");
        }
        
        FileOutputStream fos = new FileOutputStream(destinationFile);
        
        try {
            fos.write(data, 0, data.length);
            fos.flush(); 
            fos.close();
        } catch (IOException e) {
            System.out.println(e);
        }        
        
    }

    private static void calcSignature(byte bytes[])
    {
        MessageDigest md;
        try
        {
            md = MessageDigest.getInstance("SHA-1");
        }
        catch(NoSuchAlgorithmException ex)
        {
            throw new RuntimeException(ex);
        }
        
        md.update(bytes, 32, bytes.length - 32);
        try
        {
            int amt = md.digest(bytes, 12, 20);
            if(amt != 20)
                throw new RuntimeException((new StringBuilder()).append("unexpected digest write:").append(amt).append("bytes").toString());
        }
        catch(DigestException ex)
        {
            throw new RuntimeException(ex);
        }
    }

    private static void calcChecksum(byte bytes[])
    {
        Adler32 a32 = new Adler32();
        a32.update(bytes, 12, bytes.length - 12);
        int sum = (int)a32.getValue();
        bytes[8] = (byte)sum;
        bytes[9] = (byte)(sum >> 8);
        bytes[10] = (byte)(sum >> 16);
        bytes[11] = (byte)(sum >> 24);
    }
}

將修正好的dex直接替換進原始的APK中,從新簽名安裝會報INSTALL_PARSE_FAILED_NO_CERTIFICATES錯誤,此時使用jdk中jarsigner驗證APK中籤名問題,以下:

$jarsigner.exe -verify mod_sign.apk
jarsigner: java.lang.SecurityException: invalid SHA1 signature file digest for classes.dex

提示這種錯誤是由於原始APK中的簽名文件沒有刪除掉,將META-INF下面的*.RSA、*.SF刪除後,從新簽名,就能夠安裝成功。

相關文章
相關標籤/搜索