文档章节

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

stormm
 stormm
发布于 2016/02/23 14:14
字数 775
阅读 536
收藏 2

前言:

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硬编码完成后,需要校正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删除后,重新签名,就可以安装成功。

© 著作权归作者所有

stormm
粉丝 32
博文 22
码字总数 14557
作品 0
昌平
运维
私信 提问
加载中

评论(1)

猫猫666
猫猫666
写得棒棒哒!博主,我们做了一个帮助博主推广博客的app叫同行说,只需复制文章链接即可发布给更多程序员同行们看到哦,欢迎体验哈,一起发扬分享精神~
转载分享:Android APP二次打包操作步骤介绍

看到好的技术教程就想转载一下,不喜勿喷!谢谢配合,仅供菜鸟学习研究,不要做坏事哦\(^o^)/~ 关于Android APP 二次打包现象已经屡见不鲜,为何“打包党”就吃准了Android平台,二次打包的操...

科技创造
2015/08/26
1K
2
独家分析:安卓“Janus”漏洞的产生原理及利用过程

近日,Google在12月发布的安卓系统安全公告中披露了一个名为“Janus”安卓漏洞(漏洞编号:CVE-2017-13156)。该漏洞可以让攻击者绕过安卓系统的signature scheme V1签名机制,进而直接对App...

顶象技术
2017/12/11
0
0
柔弱的APP如何自我保护,浅谈APP防御手段,使用360加固助手加固/签名/多渠道打包/应用市场发布

柔弱的APP如何自我保护,浅谈APP防御手段,使用360加固助手加固/签名/多渠道打包/应用市场发布 由于JAVA和Android的平台型,所以APP很容易被反编译,这对于我们开发者来说,是一个不想要的结...

刘桂林
2016/05/30
0
0
android apk打包基础知识

今天自己研究出apk打包,跟大家一起共勉。 aapt package -m -J src -M AndroidManifest.xml -S res -I android.jar //这句命令主要是重新自动生成R.java文件,比如增加个图片或者字符串或者增...

迷途d书童
2012/03/26
1K
3
1 长生剑 - Smali Instrumentation

0x01 长生剑 长生剑是把神奇的剑,为白玉京所配,剑名取意来自于李白的诗:“仙人抚我顶,结发受长生。”长生剑是七种武器系列的第一种武器,而笔者接下来所要介绍的调试方法也是我最早学习的...

xingjm8511
2016/05/27
80
0

没有更多内容

加载失败,请刷新页面

加载更多

uni app 零基础小白到项目实战

$emit 子组件传给父组件$ref 父组件操作子组件 公用模板 uni-app全局变量的几种实现方法 const websiteUrl = 'http'const now = Date.now || function() { return new Date().getTime......

达达前端小酒馆
24分钟前
4
0
Tomcat是如何实现异步Servlet的

前言 通过我之前的Tomcat系列文章,相信看我博客的同学对Tomcat应该有一个比较清晰的了解了,在前几篇博客我们讨论了Tomcat在SpringBoot框架中是如何启动的,讨论了Tomcat的内部组件是如何设...

木木匠
49分钟前
21
0
mysql中间件分享(Mysql-prxoy,Atlas,DBProxy,Amoeba,cobar,TDDL)

hello 各位小伙伴大家好,我是小栈君,这期我们分享关于mysql中间件的研究,也就是数据层的读写分离和负载均衡,希望能够在实际的应用中能够帮助到各位小伙伴。 下期我们将继续分享go语言的系...

IT干货栈
今天
10
0
OSChina 周一乱弹 —— 人生,还真是到处是意外

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @这次装个文艺青年吧 :#今日歌曲推荐# 分享lil peep的单曲《High School》 《High School》- lil peep 手机党少年们想听歌,请使劲儿戳(这里...

小小编辑
今天
843
13
Spring使用ThreadPoolTaskExecutor自定义线程池及实现异步调用

多线程一直是工作或面试过程中的高频知识点,今天给大家分享一下使用 ThreadPoolTaskExecutor 来自定义线程池和实现异步调用多线程。 一、ThreadPoolTaskExecutor 本文采用 Executors 的工厂...

CREATE_17
今天
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部