classes.dex硬编码后重签名安装

前言:

1、很多APK用apktool反编译后,重打包失败;

2、dex中smali指令硬编码,安装失败;

解决方法:

一、针对第一个问题,就是不反编译APK,而是直接从APK包从用Zip工具(winzip)将classes.dex提取出来,再使用IDA等反编译工具找到要修改指令的偏移,最后使用winhex等编辑工具修改指令,然后保存即可;

详细修改指令的方法是将OPCODE直接填充到相应位置即可:

原始JAVA代码如下:

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

IDA中此代码的opcode如下:

classes.dex硬编码后重签名安装_第1张图片


二、针对第二个问题,在classes.dex硬编码完成后,需要校正checksum和signture,使用下面的java程序修正classes.dex,

代码如下:

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删除后,重新签名,就可以安装成功。

你可能感兴趣的:(classes.dex硬编码后重签名安装)