Android APK 逆向(macOS开发环境)
反编译
如果只做代码查看的话,可以使用dex2jar和jd-gui查看
dex2jar下载地址:
jd-gui下载地址:
使用方法:
首先将.apk文件修改成.zip文件并解压,解压文件如图
这也就是一个android工程编译出来的目录,每个文件是什么作用,不了解的可以自己去查资料了解一下,这里不做细说。
然后把下载好的dex2jar.zip解压,打开命令行进入 d2j-dex2jar.bat 文件所在目录,输入命令:
d2j-dex2jar.bat [你的dex文件路径]
能看到生成了一个.jar后缀的文jar如图
这个jar包可以通过jd-gui查看,不过先要安装jd-gui。同dex2jar的安装,下载之后解压即可。
双击打开.app程序,在文件目录中找到刚才上一步生成的jar包导入进来就可以查看反编译的代码了,如图(有些代码是混淆过的,没有mapping文件的话不能复原,只能看一下代码架构和没有混淆的代码)
如果打算做smali代码打桩的话,可以使用apktool反编译并修改
首先安装apktool,打开终端通过brew安装,打开终端执行命令安装apktool
brew install apktool
安装好以后执行
apktool d apk文件路径 -o 输出路径
得到反编译后的工程,目录如图所示:
这样我们就拿到smali文件了,可以根绝自己的需求去修改其中的代码了。
修改
由于修改是打桩是插入的,所以首先要确定代码的插入位置,例如我逆向的是一个叫GBWhatsApp的应用,要做的是把点击登录页的一个按钮,跳转到我自定义的界面。
确定登录按钮所在界面
通过命令
adb shell dumpsys activity top | grep ACTIVITY
获取当前的activity信息如图:
所以通过包名就知道当前的页面就在<com.gbwhatsapp/.registration.EULA>路径 找到工程中对应的smali文件
如果打算修改按钮跳转页面的话,只要找到对应的跳转页面,将我们自己的页面替换进去就可以了。 上一步找到了按钮所在页面,那么跳转到目标页面,查看是哪个activity。
所以通过包名就知道当前的页面就在<com.gbwhatsapp/.registration.RegisterPhone>路径
可以直接用android studio直接打开来查看smali代码,其中一个onCreate()方法,也就是说这个EULA类是Activity的子类,代码如下
建议先去简单看一下smali语法再看代码,磨刀不误砍柴工。
以下是EULA的onCreate()方法中的一段代码,其中我们能看到:
//Lcom/gbwhatsapp/registration/RegisterPhone类型的对象被赋值给v0; const-class v0, Lcom/gbwhatsapp/registration/RegisterPhone;
//v0用来初始化 Landroid/content/Intent对象v3;
new-instance v3, Landroid/content/Intent;
invoke-direct {v3, p0, v0}, Landroid/content/Intent;->(Landroid/content/Context;Ljava/lang/Class;)V
//v3作为路由跳转到RegisterPhone invoke-virtual {p0, v3},Landroid/content/Context;>startActivity(Landroid/content/Intent;)V
const-class v0, Lcom/gbwhatsapp/registration/RegisterPhone;
new-instance v3, Landroid/content/Intent;
invoke-direct {v3, p0, v0}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
const-string v0, "com.gbwhatsapp.registration.RegisterPhone.resetstate"
invoke-virtual {v3, v0, v4}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Z)Landroid/content/Intent;
invoke-virtual {p0}, Landroid/app/Activity;->getIntent()Landroid/content/Intent;
move-result-object v2
const-string v1, "com.gbwhatsapp.registration.RegisterPhone.phone_number"
invoke-virtual {v2, v1}, Landroid/content/Intent;->hasExtra(Ljava/lang/String;)Z
move-result v0
if-eqz v0, :cond_5
invoke-virtual {v2, v1}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
invoke-virtual {v3, v1, v0}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
:cond_5
const-string v1, "com.gbwhatsapp.registration.RegisterPhone.country_code"
invoke-virtual {v2, v1}, Landroid/content/Intent;->hasExtra(Ljava/lang/String;)Z
move-result v0
if-eqz v0, :cond_6
invoke-virtual {v2, v1}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
invoke-virtual {v3, v1, v0}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
:cond_6
const-string v1, "com.gbwhatsapp.registration.RegisterPhone.clear_phone_number"
invoke-virtual {v2, v1}, Landroid/content/Intent;->hasExtra(Ljava/lang/String;)Z
move-result v0
if-eqz v0, :cond_7
invoke-virtual {v2, v1, v4}, Landroid/content/Intent;->getBooleanExtra(Ljava/lang/String;Z)Z
move-result v0
invoke-virtual {v3, v1, v0}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Z)Landroid/content/Intent;
:cond_7
invoke-virtual {p0, v3}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V
invoke-virtual {p0}, Landroid/app/Activity;->finish()V
由上面的分析可知,只要把下面的代码
const-class v0, Lcom/gbwhatsapp/registration/RegisterPhone;
替换成自定义的页面代码即可。
仿照被逆向工程的包路径新建一个工程:
如何将.java文件转化成.smali文件呢?Android Studio中安装jave2smali插件,一键转化java代码到smali代码。
插件功能使用:打开java文件——Build--Compile to Samli。
例如我的代码
转换前
public class HookActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hook);
}
}
转换后
# direct methods
.method public constructor <init>()V
.registers 1
.prologue
.line 17
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
return-void
.end method
# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
.registers 3
.param p1, "savedInstanceState" # Landroid/os/Bundle;
.annotation build Landroidx/annotation/Nullable;
.end annotation
.end param
.prologue
.line 20
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
.line 21
const v0, 0x7f0b001c
invoke-virtual {p0, v0}, Lcom/gbwhatsapp/HookActivity;->setContentView(I)V
.line 22
return-void
.end method
merge自定义工程到逆向工程中
也就是把你需要打桩注入的代码按照对应包-文件-代码位置等,拷贝到原逆向工程中。
- 1.将HookActivity.smali文件拷贝到com/gbwhatsapp目录下;将HookActivity中所需的布局文件拷贝到res/layout目录下。路径如下:
<GBWA.8.70/smali/com/gbwhatsapp/HookActivity.smali>
<GBWA.8.70/res/layout/activity_hook.xml >
- 2.将HookActivity注册到manifest文件中,代码如下:
<activity android:name="com.gbwhatsapp.HookActivity"/>
- 3.将layout资源文件的ID添加到public.xml文件中:(res/value/public.xml用于将固定资源ID分配给Android资源,不同type有不同的取值范围,添加资源的时候需要避坑)找到最下面的一个layoutid,将layoutid+1分配给我们新添加的layout文件。
原工程最大layoutID为
<public type="layout" name="activity_voicenotesounds" id="0x7f0d03ef" />
添加新的layoutID,name是上文拷贝进来的资源文件名,id是最大layoutID+1
<public type="layout" name="activity_hook" id="0x7f0d03f0" />
- 4.在EULA文件中把原来的页面替换成HookActivity. 在上一步的EULA.smali文件中找到的smali代码
const-class v0, Lcom/gbwhatsapp/registration/RegisterPhone;
修改成我们merge进来的代码
const-class v0, Lcom/gbwhatsapp/HookActivity;
merge代码完成。
重打包签名安装
重新打包
apktool支持将反编译的工程再编译出apk文件,当把所有修改完成以后,执行命令
apktool b 【你的逆向工程根路径】
例如我的工程:apktool b /Users/xxx/GBWA.8.70/
执行过程如图:
编译结束后,【工程名】/dist/ 的目录下会生成一个apk如图:
这样重新打包就完成了,你修改的代码已经在生成的apk文件中了。
签名
系统安装apk的时候会对apk文件作签名校验,如果没有进行重新签名的话,会提示安装失败:
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from /data/app/vmdl58739005.tmp/base.apk: Attempt to get length of null array]
签名的话需要先生成一个.keystore密钥库文件,可以通过jdk生成
在终端执行命令查看jdk目录:
/usr/libexec/java_home -v
结果如图:
进入到java home 目录下。执行命令生成.keystore密钥库文件
keytool -genkey -v -keystore my_local_keystore.keystore -alias my_local_keystore -keyalg RSA -validity 20000 -keystore 【生成文件存放路径】
生成过程中需要填入一些信息,信息可以随便填入,并不影响密钥文件的生成,如图:
生成结束后可以执行命令查看my_local_keystore.keystore信息:
keytool -list -v -keystore /Users/dongchengpu/decompile/my_local_keystore.keystore
执行结果如图:
这样就完成了密钥库文件的生成。
同样还是在java_homem目录下,执行命令进行签名
jarsigner -keystore 【keystore文件路径】【待签名apk文件路径】 【keystore文件名】
执行过程中需要输入密钥库口令,也就是你生成keystore文件时输入的密码,执行结束如图:
这样就完成签名了!最后就剩下安装查看效果了!
执行命令安装apk:
adb install 【你的apk文件路径】
效果展示
应用名不一样是因为我顺手修改了应用名的字符串
修改前的效果
修改后的效果
备注
修改功能比较简单,主要是之前没有接触过逆向,本文记录一下逆向的整体流程,哪里有错误欢迎大佬们指正批评互相交流!