KGB-Messenger:静态分析、Frida动态调试、Apktool回编译的复习

本文以一个开源的Android Crackme–KGB-Messenger,作为实验对象,把最近学习的Frida动态调试和Apktool回编译流程复习一下。

步骤参考自非虫-Android软件安全权威指南以及apk文件反编译、回编译和签名之完全教程

本次实验使用开源的Android逆向Crackme KGB-Messenger

安装apktool

  1. 下载 Windows版本的 wrapper script (右键链接另存为 apktool.bat)
  2. 下载 apktool (下载列表)
  3. 重命名上步下载的 jar 包为 apktool.jar
  4. apktool.batapktool.jar移动到同一个目录下 (不运行apktool.bat) 并将此文件夹添加进环境变量
  5. 使用 apktool -version 验证是否配置成功,出现版本号则说明配置成功

逆向分析

该Crackme还是比较入门的,先拖进Jeb静态逆向分析,结合frida动态调试把flag找出来。

先是一个环境检查,要求设备的所属地区为Russia,且用户名为RkxBR3s1N0VSTDFOR180UkNIM1J9Cg==(解码为FLAG{57ERL1NG_4RCH3R}),不然直接弹窗提示错误信息。直接Hook绕过。

1
2
3
4
5
6
7
var System = Java.use("java.lang.System");
System.getProperty.overload("java.lang.String").implementation = function(argStr){
return "Russia";
}
System.getenv.overload('java.lang.String').implementation = function(str){
return "RkxBR3s1N0VSTDFOR180UkNIM1J=9Cg==";
}

接下来验证用户名和密码。
通过伪代码可以看到,用户名得是codenameduchess,且指定了密码的md5值。
把md5值去掉开头的6位和末尾的8位,然后到somd5去查,得到明文为guest
输入合法的用户名和密码,LoginActivity.i()会Toast出第二个FLAG{GOOG13_PRO}

image-20210822194617036

image-20210822194622824

接下来来到聊天界面MessengerActivity

MessengerActivity.onSendMessage()作为发送的监听函数,大致反应了最后一关的逻辑:
先后发送两句话,分别满足this.a(v1.toString()).equals(this.p)this.b(v1.toString()).equals(this.r)的要求,然后在this.i()方法中,基于这两句话构造出最后一个flag。
this.a()是一个可逆的加密算法,this.b()不可逆,需要爆破然后手动补全,代码如下:

image-20210822195538116

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pp = "V@]EAASB\u0012WZF\u0012e,a$7(&am2(3.\u0003"
p = list(pp)
lp = len(p)

for ind in range(0, lp//2):
tmp = p[ind]
p[ind] = chr(ord(p[lp-ind-1]) ^ 65)
p[lp - ind -1] = chr(ord(tmp) ^ 50)

print(''.join(p))

rr = "\u0000dslp}oQ\u0000 dks$|M\u0000h +AYQg\u0000P*!M$gQ\u0000"
r = list(rr)
lr = len(r)

for ind in range(0, lr//2):
tmp = r[ind]
r[ind] = r[lr-ind-1]
r[lr-ind-1] = tmp

for ind in range(0, lr):
for c in range(0,128):
if(c>>(ind%8) ^ c == ord(r[ind])):
print(chr(c), end='')
break
# 输出为
# Boris, give me the password
# ay I *PEASE* hve the assword 手动补全为 May I *PEASE* have the Password?

最终得到第三个flag:FLAG{p455w0rd_P134SE}

字符串00截断

MessengerActivity.r="\u0000dslp}oQ\u0000 dks$|M\u0000h +AYQg\u0000P*!M$gQ\u0000",该字符串以00开头,frida在处理时应该是默认截断了。
因此通过frida的console.log打印为空,事实上,通过JS代码的调用都不能获取到该字符串的真实值。
这个问题在frida的issue下面有所讨论,但是官方貌似还没修。

解包修改

先用apktool反编译出smali代码

1
apktool d kgb-messenger.apk -o kgb

使用VScode打开kgb目录,配合支持smali高亮的插件,就可以便捷修改。

先过开屏校验-地区和用户,伪代码如下:

image-20210822171222492

这里的检查结果对之后的运行没有影响,只决定是否发生activitiy之间的跳转。
所以粗暴处理,直接把红框中代码对应的smali全部删除。
当然,smali代码的排布跟java层的代码有所区别,并非线性对应,需要结合寄存器操作划定删除的部分。
我直接把setcontentview()之后、包括调用a.a.a.a.a.a()之间的代码全部注释,然后把末尾跳转到goto_0的语句改成return-void

image-20210822171549896

笔者也试过仅修改v.equals()的返回值校验,即两个if-nez语句,不过没有成功。

image-20210822112707403

过用户名和密码检查

LoginActivity中,把用户名和密码保存在this.n和this.o两个成员变量中,之后在调用成员变量进行校验。
所以直接在获取之后、调用校验之前,添加两行代码把this.n和this.o强行修改为合法值。
这样一来,即使不输入任何值,也能成功登录。

image-20210822172249232

image-20210822172642033

获取最终flag

来到MessengerActivity
修改的思路同前:修改equals()的返回值校验进入两个if语句分支,然后在合适的时机把对应的成员变量修改为合法值,只需要随便输入一句话,就能获得flag。

image-20210822173624384 image-20210822173621209

回编

修改完smali代码就需要回编译得到apk,仍然可以使用apktool回编。

1
apktool b .\kgb\

在dist目录下找到回编得到的apk并安装,发现安装报错,因为这个apk还没有被签名。

image-20210822113407609

先用keytool来生成一份自签名证书,然后使用jarsigner来签名apk文件。
这两个程序都在jdk中,有环境变量就能直接使用。(Windows Terminal 可能有编码问题,可以在cmd中进行)

1
2
3
4
5
6
# 根据提示设置密码并填入开发者信息,最后输入y确定
keytool -genkey -alias key.store -keyalg RSA -validity 20000 -keystore key.store
# 开始签名,需要根据提示输入证书的密码
jarsigner -verbose -keystore key.store -signedjar apk-after-sign.apk apk-before-sign.apk key.store
# 安装已签名apk到设备
adb install apk-after-sign.apk

运行软件,通过环境检查,不输入用户名和密码也能直接进入聊天界面,相较于之前,现在随便发一句话就能获得flag。

image-20210822174958584

Welcome to my other publishing channels