Android 逆向实践
前言
五一放假回来,人有点颓废了,最近对Android的逆向有点兴趣,研究了下,顺便写篇文章记录下吧。
工具下载
apktool
apktool可以对APK反编译和重新打包,经过反编译后我们可以改里面的资源,或者改里面的smali字节码,通过重新打包后修改能生效,但是得重新签名。
这里我们用自己创建个apktool.bat文件,里面填上脚本内容,apktool.bat比直接使用apktool.jar方便一点,而且能判断反编译结束。
jadx-gui
apktool可以反编译和重新打包,但是没办法直接查看Java代码,要理解里面的逻辑就需要用到jd-gui或者jadx-gui了,jd-gui只是一个jar包,jadx-gui基于它,但是用起来更方便。
jadx-gui可以得到Java代码,里面还能对资源代码等全局搜索,方法点击能跳转,不太方便的就是它不能直接修改,只适用于阅读,修改还是得apktool。
dex2jar
实际上用上面两个工具就可以完成代码阅读及修改了,dex2jar是用来将apk或者dex文件转成jar的,后面也能将jar转回dex文件。
我试了下,用来解包multidex的APP好像不太好使,看网上别人说是支持的,我最后还是没用到它。
APK解包
首先要逆向,我们就要对apk进行解包,工具选择apktool,使用命令:
apktool.bat d filename.apk
这里我们最好把我们上面的一些工具放到环境变量的path里面去,使用工具就不用输入完整路径了。
这里就用我们公司的破打卡软件为例,输入命令后,等待一段时间,看到“Press any key to continue”就解包结束了:
这时候就可以关闭shell窗口了,提示“processes are running in session”可以不用管,点OK就行:
接下来就可以看到解包的目录了:
这就看需要了,改点图片和资源,去res和asset里面改就行了,要改源码就麻烦点了,后面再讲。
APK源码阅读
源码阅读需要用到jadx-gui,直接打开软件,选择APK打开就行了(不是上一步解包的文件夹),转一会就可以看到源码了:
和Android studio打开apk差不多,不同的是AS只能看字节码,而这里可以看Java代码。
APP代码修改
反编译一个APP的目标当然是改它嘛,下面就来看看一些常见的修改。
资源文件修改
字符串(res/value-xx/string)、图片(asset、res/drawable-xx、minmap)、布局(res/layout)之类的直接到文件里面改,和开发Android目录差不多。
比如想改个版本号,打开apktool.yml文件,拉到最下面就能改了:
比如有些老代码,源码都丢了,但是签名文件还有,就能这样升级了。
打开调试模式
一般release的包是没法调试的,如果有需求可以在AndroidManifest.xml里面修改:
<application android:debuggable="false" android:allowNativeHeapPointerTagging="false"
...
找到application这两个属性,改成true,就可以打开调试模式了,后面我们在smali里面插入Log也就能显示了。
绕过签名验证
有时候APP会增加签名校验,说白了就是获取本应用的签名和预设的签名文件的MD5值或者SHA值对比,如果不一样那就闪退。
这是很好的一个切入口,我们到jadx-gui里面ctrl + shift + F,全局搜索这句话
知道这条string的资源名,就能继续搜索代码所在位置了:
这个代码意图够明显了吧,而且没有混淆,稍微动一动就能绕过了,不过注意做一个遵纪守法的好公民!改别人软件是不道德,甚至违法的!
smali字节码操作
虽然不能做违法操作,但是学习下如何操作smali字节码,还是很有意义的。
smali字节码简单学习
我叫它smali字节码还是不对的,它应该叫Dalvik 字节码,说白了就是Dalvik/AndroidRuntime这个虚拟机要执行的字节码,虽然它们也是JVM,但是却又不完完全全是
下面就实践下。
修改方法
虽然jadx-gui里面会提示一个类来自哪个dex文件里面,但是还是比较麻烦,我们可以使用IDE来打开这个解包文件夹:
/* loaded from: classes7.dex */
public class WelcomeActivity extends com.weaver.platform.BaseActivity
比如使用Visual Studio打开来,通过Ctrl + P就能跳转到文件了,虽然用Android Studio配合双击shift也行,但是AS用起来太卡了。
对于一个方法内的代码,我们可以根据行数随便删除,不过注意下返回值,下面是smali字节码删除前后:
.method private isTwickPack()Z
.registers 6
const/4 v0, 0x0
.line 418
:try_start_1
invoke-virtual {p0}, Lweaver/fw/com/WelcomeActivity;->getApplicationInfo()Landroid/content/pm/ApplicationInfo;
// ...
.line 461
invoke-virtual {p0}, Ljava/lang/Exception;->printStackTrace()V
:cond_85
return v0
.end method
删除部分代码后:
.method private isTwickPack()Z
.locals 1
const/4 v0, 0x0
return v0
.end method
实际就是要注意下返回的变量,这里“const/4 v0, 0x0”,就是创建了一个变量,里面赋值false并返回。
修改变量
比如说我们有个类的私有变量,想给它赋个初值:
private String proLocationData;
那么只要找到它的构造方法,在里面设置就行:
.method public constructor <init>()V
.locals 6
// ...
const-string v1, "{"type":"wgs84","errCode":0,"errMsg":"get LocationAddress Success"}"
.line 16681
iput-object v1, p0, Lcom/weaver/platform/fragment/WebViewFragment;->proLocationData:Ljava/lang/String;
要修改局部变量也同理,先创建一个常量,再赋值过去就行。不过要注意下,如果string内部有中文,要转换下。
打印日志
说实话,日志函数实际就是一个静态方法,用起来只要传递两个变量进去就行了,但是要注意下把对象toString再输出,举个例子:
val jSONObject = JSONObject()
jSONObject.put("type", "wgs84")
Log.d("TAG", jSONObject.toString())
对于的smali字节码如下:
.line 24
new-instance v0, Lorg/json/JSONObject;
invoke-direct {v0}, Lorg/json/JSONObject;-><init>()V
.line 25
.local v0, "jSONObject":Lorg/json/JSONObject;
const-string v1, "type"
const-string v2, "wgs84"
invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
.line 26
const-string v1, "TAG"
invoke-virtual {v0}, Lorg/json/JSONObject;->toString()Ljava/lang/String;
move-result-object v2
invoke-static {v1, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
其实忽略jSONObject的toString过程,我们只要实现Log的invoke-static操作就可以了。
一些技巧
这里给一些我摸索出来的技巧吧,希望有所帮助
利用adb命令确定页面
一般要修改一个页面,首先就要确定这个页面所在的文件,可以通过adb命令实现:
adb shell
dumpsys activity top | grep ACTIVITY
找到对应的smali文件去修改就行了。
利用AS编写代码来替换
这个smali字节码看起来真的头疼,如果不知道怎么改的话,可以在另一个APP里面编写代码,然后打包下,用AS打开APK,参考里面的写法就行。
利用Visual Studio插件验证语法
Visual Studio里面有smali语法的插件,可以帮忙验证写法有没有问题。
利用代码行号定位
在jadx-gui里面我们可以看到代码对应的行数,这就能让我们快速的定位smali文件里面的位置,配合Java的方法签名,还是能较快理解代码的。
匿名函数说明
在看smali文件的时候,经常会碰到各种匿名函数,就很烦,数量很多,找起来麻烦,虽然用Visual Studio的ctrl+P可以快速跳转文件,但是这些个access方法还是很累人:
// 在WebViewFragment$82中拿到WebViewFragment的locationUtil变量,并执行它的destroyLocation方法
.line 6005
iget-object p1, p0, Lcom/weaver/platform/fragment/WebViewFragment$82;->this$0:Lcom/weaver/platform/fragment/WebViewFragment;
invoke-static {p1}, Lcom/weaver/platform/fragment/WebViewFragment;->access$5600(Lcom/weaver/platform/fragment/WebViewFragment;)Lcom/weaver/platform/util/LocationUtil;
move-result-object p1
invoke-virtual {p1}, Lcom/weaver/platform/util/LocationUtil;->destroyLocation()V
还要配合搜索功能,在WebViewFragment.smali里面搜索5600,看看他是干嘛的:
.method static synthetic access$5600(Lcom/weaver/platform/fragment/WebViewFragment;)Lcom/weaver/platform/util/LocationUtil;
.locals 0
.line 378
iget-object p0, p0, Lcom/weaver/platform/fragment/WebViewFragment;->locationUtil:Lcom/weaver/platform/util/LocationUtil;
return-object p0
.end method
.method static synthetic access$5602(Lcom/weaver/platform/fragment/WebViewFragment;Lcom/weaver/platform/util/LocationUtil;)Lcom/weaver/platform/util/LocationUtil;
.locals 0
.line 378
iput-object p1, p0, Lcom/weaver/platform/fragment/WebViewFragment;->locationUtil:Lcom/weaver/platform/util/LocationUtil;
return-object p1
.end method
实际就是getter和setter方法嘛,只不过,阅读起来很影响体验。
APK重新打包
上面修改号自己想要的东西后,我们就可以,给它打包回去了,还是用apktool,命令如下:
apktool.bat b file-dir
经过一段时间,就能在dist目录找到修改后的APK,注意这里的APK并没有签名,这时候我们随便找个签名工具给它签上就OK了,我这用的爱加密的签名工具,也可以用360的签名工具,这里就详细说了,下班了。
小结
花了点时间,对Android逆向进行了一点点实践,学习了下几个工具的使用、smali文件的修改等等,还是有点收获吧!
如果你看到了这里,觉得文章写得不错就给个赞呗?
更多Android进阶指南 可以扫码 解锁更多Android进阶资料
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )