android 反编译_Android 反编译实战
❝
文中相关工具下载链接:https://pan.baidu.com/s/1_bknFSnsYxLUNJ3WTulEFA 提取码:4qo8
❞
我的所有原创Android知识体系 https://github.com/xfhy/Android-Notes ,已打包整理到GitHub.努力打造一系列适合初中高级工程师能够看得懂的优质文章,欢迎star~
1. 反编译基操
1.1 借鉴code
一般来说,如果只是想借鉴一下友商的code,我们只需要拿到对方的apk,拖到jadx里面就行.jadx能查看apk的xml布局和java代码.jadx有时候会出现部分class反编译失败的情况,这时可以试试Bytecode-Viewer,它也能反编译, 而且还能反编译出jadx不能反编译的class.但是如果apk是已加固了的,那么jadx是不能查看代码的.这时需要脱壳,然后再进行反编译.
1.2 修改执行逻辑
如果是想修改程序的执行逻辑,则需要修改smali代码.
如何拿smali代码? 这时需要用到apktool,使用命令:apktool d xx.apk
即可将apk逆向完成,拿到smali代码.这里如果反编译失败了且报错org.jf.dexlib2.dexbacked.DexBackedDexFile$NotADexFile: Not a valid dex magic value: cf 77 4c c7 9b 21 01 cd
,则试试apktool d xx.apk -o xx --only-main-classes
这条命令.
然后用VS Code打开,这里最好在VS Code里面装一个Smali
插件,用于在VS Code里面支持smali语法,高亮之类的.完成之后大概是这个样子:
环境倒是OK了,回到正题,我们需要修改执行逻辑.在此之前,我们最好先简单学习一下smali的基本语法,详情见我之前写过的文章反编译基础知识.
修改好逻辑之后,我们需要将这些代码重新打包成apk,此时需要用到apktool,执行:apktool b xx
.执行完成之后,输出的apk会在xx/dist
目录下.它打包出来的是没有签名的apk,需要签名才能安装.
签名需要用到autosign这个工具包,使用命令java -jar signapk.jar testkey.x509.pem testkey.pk8 debug.apk debug_signed.apk
2. 加日志
有时候,你可能需要在修改原有执行逻辑之后,在代码里面加点日志,方便查看打出来的包逻辑是否正确.这里我摸索出一个简单的方式打日志,写一个日志打印工具类,然后将这个工具类转成smali文件,然后放入apk反编译出来的smali代码文件夹中, 之后就可以在这个项目的任何smali中使用这个工具类了.下面详细介绍一下:
2.1 写日志打印工具类LogUtil
这个日志打印工具类是为了外界方便调用的,所以需要让外界调用的时候尽量简单.下面是我简单实现的工具类,tag都是我定义好了的,免得外面再定义一次(麻烦).
public class LogUtil {
public static void logNoTrace(String str) { Log.d("xfhy888", str); }
public static void test() { logNoTrace("大撒大撒大撒"); }
}
2.2 打印调用栈
上面的工具类目前只能打印普通的日志,但是有时我们想在打印日志的同时输出这个地方的调用栈,此时我们再加个方法扩展一下.
public static void log(String str) { Log.d("xfhy888", str);
Throwable throwable = new Throwable(); StackTraceElement[] stackElements = throwable.getStackTrace(); StringBuilder stringBuilder = new StringBuilder(); if (stackElements != null) { for (StackTraceElement stackElement : stackElements) { stringBuilder.append(stackElement.getClassName()).append(" "); stringBuilder.append(stackElement.getFileName()).append(" "); stringBuilder.append(stackElement.getMethodName()).append(" "); stringBuilder.append(stackElement.getLineNumber()).append("\n"); } } Log.d("xfhy888", stringBuilder.toString()); }
在log方法中我们手工构建了一个Throwable,然后通过其getStackTrace方法即可得到调用栈信息,通过Log打印出来.效果如下:
12817-12817/com.xfhy.demo D/xfhy888: com.xfhy.LogUtil LogUtil.java log 10com.xfhy.startactivitydemo.MainActivity$1 MainActivity.java onClick 45android.view.View View.java performClick 6724android.view.View View.java performClickInternal 6682android.view.View View.java access$3400 797android.view.View$PerformClick View.java run 26472android.os.Handler Handler.java handleCallback 873android.os.Handler Handler.java dispatchMessage 99android.os.Looper Looper.java loop 233android.app.ActivityThread ActivityThread.java main 7210java.lang.reflect.Method Method.java invoke -2com.android.internal.os.RuntimeInit$MethodAndArgsCaller RuntimeInit.java run 499com.android.internal.os.ZygoteInit ZygoteInit.java main 956
2.3 将工具类转smali
在Android Studio里面写好这个工具类之后,装一个java2smali
插件.然后选中LogUtil文件,再依次点击Build->Compile to Smali
,即可将LogUtil.java转成smali.下面是我转好的
.class public Lcom/xfhy/LogUtil;.super Ljava/lang/Object;.source "LogUtil.java"
# direct methods.method public constructor ()V .registers 1 .prologue .line 5 invoke-direct {p0}, Ljava/lang/Object;->()V return-void.end method.method public static log(Ljava/lang/String;)V .registers 9 .param p0, "str" # Ljava/lang/String; .prologue .line 8 const-string v4, "xfhy888" invoke-static {v4, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I .line 10 new-instance v3, Ljava/lang/Throwable; invoke-direct {v3}, Ljava/lang/Throwable;->()V .line 11 .local v3, "throwable":Ljava/lang/Throwable; invoke-virtual {v3}, Ljava/lang/Throwable;->getStackTrace()[Ljava/lang/StackTraceElement; move-result-object v1 .line 12 .local v1, "stackElements":[Ljava/lang/StackTraceElement; new-instance v2, Ljava/lang/StringBuilder; invoke-direct {v2}, Ljava/lang/StringBuilder;->()V .line 13 .local v2, "stringBuilder":Ljava/lang/StringBuilder; if-eqz v1, :cond_52 .line 14 array-length v5, v1 const/4 v4, 0x0 :goto_17 if-ge v4, v5, :cond_52 aget-object v0, v1, v4 .line 15 .local v0, "stackElement":Ljava/lang/StackTraceElement; invoke-virtual {v0}, Ljava/lang/StackTraceElement;->getClassName()Ljava/lang/String; move-result-object v6 invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; move-result-object v6 const-string v7, " " invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 16 invoke-virtual {v0}, Ljava/lang/StackTraceElement;->getFileName()Ljava/lang/String; move-result-object v6 invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; move-result-object v6 const-string v7, " " invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 17 invoke-virtual {v0}, Ljava/lang/StackTraceElement;->getMethodName()Ljava/lang/String; move-result-object v6 invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; move-result-object v6 const-string v7, " " invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 18 invoke-virtual {v0}, Ljava/lang/StackTraceElement;->getLineNumber()I move-result v6 invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; move-result-object v6 const-string v7, "\n" invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 14 add-int/lit8 v4, v4, 0x1 goto :goto_17 .line 21 .end local v0 # "stackElement":Ljava/lang/StackTraceElement; :cond_52 const-string v4, "xfhy888" invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; move-result-object v5 invoke-static {v4, v5}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I .line 22 return-void.end method.method public static logNoTrace(Ljava/lang/String;)V .registers 2 .param p0, "str" # Ljava/lang/String; .prologue .line 25 const-string v0, "xfhy888" invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I .line 26 return-void.end method
有了编译好的smali文件,还需要放到反编译项目的对应包名里面,我这里的是com/xfhy/
,那我就得放到这个目录下.
2.4 使用工具类
这里我随便写个方法测试一下,java代码如下:
public void test() { for (int i = 0; i 10; i++) { System.out.println(i); }}
它所对应的smali代码如下:
.method public test()V .registers 3
.prologue .line 29 const/4 v0, 0x0
.local v0, "i":I :goto_1 const/16 v1, 0xa
if-ge v0, v1, :cond_d
.line 30 sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V
.line 29 add-int/lit8 v0, v0, 0x1
goto :goto_1
.line 32 :cond_d return-void.end method
我在方法的一开始就打印一句日志,首先加registers个数+1,因为需要新定义一个变量来存字符串,然后再调用LogUtil的静态方法打印这个字符串.
.method public test()V .registers 4
const-string v2, "test method"
invoke-static {v2}, Lcom/xfhy/LogUtil;->log(Ljava/lang/String;)V
.prologue .line 29 const/4 v0, 0x0
.local v0, "i":I :goto_1 const/16 v1, 0xa
if-ge v0, v1, :cond_d
.line 30 sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V
.line 29 add-int/lit8 v0, v0, 0x1
goto :goto_1
.line 32 :cond_d return-void.end method
3. 调试smali
我们不能直接调试反编译拿到的java代码,而是只能调试反编译拿到的smali代码.当然,调试的时候,需要懂一些smali的基本语法,这样的话,基本能看懂程序在干嘛.
3.1 让App可以调试
首先是让App可以调试
- 可以修改AndroidManifest.xml中的debuggable改为true(具体操作:先用apktool反编译,再修改AndroidManifest,再打包签名,运行到手机上);
- 也可以使用XDebug 让所有进程处于可以被调试的状态;
3.2 如何调试?
首先是在Android Studio里装一个smalidea的插件,我上面分享的网盘地址里面有.我试了下,smalidea是不支持最新版的Android Studio的.我去查了下,smalidea最后一个版本是0.05, 最后更新时间是2017-03-31。确实有点老了,我看18年年末的时候有人在博客中提到了这个插件,于是我想了下,同时期的Android Studio肯定可以用这个插件. 在Android Studio官网一顿乱串之后发现, 官网提供了历史版本的下载地址. 最后下载了一个2018年10月11日的Android 3.2.1,装上插件试了下->可行->完美.
把apktool反编译好的文件夹导入Android Studio,把所有smali开头的文件夹都标记一下Sources Root(标记方法: 文件夹右键,Mark Directory as -> Sources Root).然后找到你需要调试的类,打好断点.
打开需要调试的App,然后打开Android Device Monitor(在SDK\tools
里面).打开Monitor的时候需要关闭Android Studio.
查看该App对应的端口是多少,记录下来.重新打开Android Studio,编辑Edit Configurations
,点击Add New Configuration
,添加之后再修改一下端口号就行,这里的端口号填上面Monitor看到的那个端口号.
Configuration添加好之后,点击Debug按钮即可进行调试.
熟悉的界面,熟悉的调试方式,开始愉快的调试吧,起飞~
android 反编译_Android 反编译实战相关推荐
- 三、Android系统内核编译及刷机实战 (修改反调试标志位)
前言 在 二.Android系统源码编译及刷机实战 一文中,我们成功编译了Android 4.4.4_r1源码并刷入系统了 Nexus 5 设备,下面是设置界面的信息.上面显示的内核版本信息是3.4. ...
- Android逆向分析(一) - 反编译看看手Q口令红包的实现原理
原文:http://blog.zhaiyifan.cn/2016/02/09/android-reverse-1/ 本系列文章是<Android软件安全与逆向分析>的实践笔记(一些工具的版 ...
- jadx重新打包_Android反编译看看手Q口令红包的实现原理
首篇作为开始,先讲讲简单的反编译.反编译通常有几种目的:互相学习.借来用用.嘿嘿(干你,又分为小干干类似微信红包,和大干干改别人的apk帮他上架). 因为没带kvm回来,mbpr屏幕太小,所以下文环境 ...
- Android逆向分析(1) 反编译看看手Q口令红包的实现原理
前言 本篇文章是作者MarkZhai的逆向分析系列的第一篇,已授权发布,并计划之后该系列的更新会第一时间发布在本公号上,敬请关注! 原文 本系列文章是<Android软件安全与逆向分析>的 ...
- 《编译与反编译技术实战 》一2.3 编译器的设计与实现概述
本节书摘来自华章出版社<编译与反编译技术实战 >一书中的第2章,第2.3节,庞建民 主编 ,刘晓楠 陶红伟 岳 峰 戴超 编著,更多章节内容可以访问云栖社区"华章计算机" ...
- 《编译与反编译技术实战》——第1章 实践的环境与工具 1.1 实践环境概述
本节书摘来自华章计算机<编译与反编译技术实战>一书中的第1章,第1.1节,作者 刘晓楠 陶红伟 岳峰 戴超,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 第1 ...
- 《编译与反编译技术实战》——2.1节编译器、解释器及其工作方式
本节书摘来自华章社区<编译与反编译技术实战>一书中的第2章,第2.1节编译器.解释器及其工作方式,作者刘晓楠 陶红伟 岳 峰 戴超,更多章节内容可以访问云栖社区"华章社区&quo ...
- 【Android 应用开发】 Android APK 反编译 混淆 反编译后重编译
反编译工具 : 总结了一下 linux, windows, mac 上的版本, 一起放到 CSDN 上下载; -- CSDN 下载地址 : http://download.csdn.net/detai ...
- 《编译与反编译技术实战》——第2章编译器实践概述
本节书摘来自华章社区<编译与反编译技术实战>一书中的第2章编译器实践概述,作者刘晓楠 陶红伟 岳 峰 戴超,更多章节内容可以访问云栖社区"华章社区"公众号查看 第2章 ...
最新文章
- smartsql和mysql_SmartSql 常见问题
- 二叉树初始化_Java实现二叉树
- Java线程详解(9)-并发协作
- JAVA File方法各类文件复制操作
- PHP执行一个http请求
- 随便玩玩之PostgreSQL(第一章)PostgreSQL简介
- Spring Boot和Spring数据JPA集成
- excel模糊搜索_Excel进阶篇:星号*用法,学会这些功能的,都按时加班了
- 计算机类对口升学都可以升啥专业,计算机专业对口升学专业试题.doc
- python两列相乘_如何将pandas中具有不同索引的两列相乘?
- 在mysql中 11div4_雷林鹏分享:MySQL 运算符
- php批量生成优惠券,PHP自动批量生成会员卡号程序
- 保密基本知识试题(2017年版)(共281题)
- 装机必备的浏览器推荐,干净好用,选这4款不会出错
- ndk编译 toolchains/llvm/prebuilt/windows-x86_64/bin/clang++.exe
- c++语言程序中,main()函数必须放在程序开始的部分,C++多选题(附答案)
- 【Rust日报】2022-09-11 Shuttle 创建和部署带有ShuttleSerenity的 Discord 机器人!
- 神州租车打造城市绿色出行 低碳理念与公益责任并行
- Flashplayer11 And AIR3游戏应用介绍视频
- 什么是敏感型货物,被海关扣押之后如何处理?