准备工具
IDA Pro,GDA,能真机调试的Android机一台
GDA分析
先把apk拉到GDA,找到MainActivity的onClick方法。
主要逻辑代码在so文件里面。
IDA分析调试
直接把libxtian.so丢到IDA分析,但没找到checkSn函数,说明函数是动态注册的。so有混淆,无法静态分析,只能真机调试了,真机调试的步骤网上有很多,这里就略过了。
动态注册Native的函数为RegisterNatives,启动调试,IDA停在JNI_OnLoad函数后,转到 _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi,下断后运行。
跟随R2寄存器,就能找到checkSn函数的地址了。
重命名为checkSn并下断,在真机那边输入uid和假码,点验证,IDA停在checkSn函数上,说明就是这里了。
现在面对最大的一个问题:混淆。
看起来是一个自写的类似OLLVM的控制流混淆。因为流程混淆有直接操作PC寄存器的代码,所以IDA的F5报废,字符串也都是运行时才会解密,无法下手。
楼主不会去混淆,而且为了这个红包题专门去分析混淆手段也太累了,就想顶着混淆解决。
这时候就可以用IDA的trace功能,用它抓取程序执行过的所有指令,不过这个红包题可以不用抓所有指令,可以使用function tracing,只抓取函数调用。
但不知为何,trace会跑飞,抓取不到所有的函数调用,所以我只好弄了个IDC脚本来单步。
static main(void)
{
do
{
step_over();
wait_for_next_event(STEP,-1);
}while(PC!=0xEEDAFC2C);//停在何处
}
注意要设置脚本停止在checkSn函数返回处,设置断点防止跑飞。
IDA停在checkSn后,打开function tracing,跑一次脚本,等停在checkSnretn,按esc后退,找到跑飞处,在下面那句代码下断。清除trace记录后,在checkSn函数头再跑一次。
分析checkSn的函数调用
抓到函数调用后,关闭function tracing,在抓取到的指令上下断,跟踪分析,由于我已经分析过一次了,所以指令上面有注释。
在最后一条BLX上,也就是getpwdlen上下断,停下后步入,注意观察。
R6寄存器存放着GetStringUTFLength函数的地址,说明这里确实是取字符串长度的,至于是uid还pwd用排除法即可。
走出API调用后,回到checkSn继续往下跟,可以找到检查pwd的长度的代码。
pwd长度不足15,所以直接返回了,我们修改下假码,继续抓取,逐个分析即可。
现在看图里的trace也能知道大致流程了,checkSn获取输入的uid和pwd,调用check函数,判断是否pwd符合uid。修改check函数的返回值为1并运行,Toast显示成功,说明正确。
现在把分析目标转到check函数,check函数内也有混淆,修改上面的脚本为PC!=0xEEDAF362并下断,并使用上面的步骤抓取所有函数调用。
分析check函数
我的IDA在check函数无法使用function tracing,app会崩溃,原因未知,所以我只能改成instruction tracing了。抓取的指令太多就不展示出来了,我会放在附件里。
这里说下check函数的分析思路和主要流程。
在trace文件里找出所有BLX,下断步入,如果是调用API,则跟踪出API名,如果是调用so本身的函数,那就直接在LR的地址下断运行。不要忘记记录参数和返回值。
如果对每个调用的内部函数都trace一遍,读代码,我觉得要花太多时间,所以记录参数和返回值,凭经验直接猜算法,如果猜不到再trace指令。
check函数首先调用gettimeofday,然后将timeval除以1800000,也就是30分钟。
然后将uid和结果拼接在一起,算出一个32字节的hash,存放在栈顶地址处。
这个hash算法我没能还原出来,本来以为是sm3,但是验算了几次都不对,跟也跟不出个头绪,就直接过了,还好不影响解出题目。
之后取出hash的17-20位。
与uid拼接到一起,等待最后比较。
接着对pwd进行base64解码,码表是修改过的,图里写的sm3就是上面的hash,忘了改注释。
解码后,再进行RC4解密,key为52pojie2020xtian
逐位异或0x20。
最后与uid+hash比较。
check函数的流程就走完了。
编写注册机
没还原出hash算法,所以不能写出注册机,因为pwd时效只有30分钟。
只能在JNI_OnLoad+43B2处复制对应当前时间的结果,再加密。附python代码一份
import hashlib
from Crypto.Cipher import ARC4
b64table = 'AzSxleoQp02MtvisIZUF8ThRaEL9Nd57qG6DfOkW4JHXmYjwV1Pn3uycrCgbKB-_='
decryptkey = '52pojie2020xtian'
def RC4(data, key):
rc41 = ARC4.new(key)
encrypted = rc41.encrypt(data)
return encrypted
def b64encode(s):
res = []
leftover = len(s) % 3
for i in range(0, len(s) - leftover, 3):
c2 = ord(s[i])
c1 = ord(s[i + 1])
c0 = ord(s[i + 2])
res.append(b64table[(c2 >> 2) & 0x3f])
res.append(b64table[((c2 & 0x3) << 4) | ((c1 >> 4) & 0x0f)])
res.append(b64table[((c1 & 0x0f) << 2) | ((c0 >> 6) & 0x03)])
res.append(b64table[c0 & 0x3f])
i += 3
if leftover == 1:
c2 = ord(s[i])
res.append(b64table[(c2 >> 2) & 0x3f])
res.append(b64table[(c2 & 0x3) << 4])
res.append(b64table[-1])
res.append(b64table[-1])
elif leftover == 2:
c2 = ord(s[i])
c1 = ord(s[i + 1])
res.append(b64table[(c2 >> 2) & 0x3f])
res.append(b64table[((c2 & 0x3) << 4) | ((c1 >> 4) & 0x0f)])
res.append(b64table[(c1 & 0x0f) << 2])
res.append(b64table[-1])
return ''.join(res)
def b64decode(s):
res = []
end = len(s)
if s[-1] == b64table[-1]:
end -= 4
for i in range(0, end, 4):
c3, c2, c1, c0 = b64table.index(s[i]), b64table.index(
s[i + 1]), b64table.index(s[i + 2]), b64table.index(s[i + 3])
res.append(chr(((c3 << 2)) | ((c2 >> 4) & 0x03)))
res.append(chr(((c2 & 0x0f) << 4) | ((c1 >> 2) & 0x0f)))
res.append(chr(((c1 & 0x03) << 6) | (c0 & 0x03f)))
if end < len(s):
if s[-2] == b64table[-1]:
c3, c2 = b64table.index(s[end]), b64table.index(s[end + 1])
res.append(chr(((c3 << 2)) | ((c2 >> 4) & 0x03)))
else:
c3, c2, c1 = b64table.index(s[end]), b64table.index(s[end + 1]), b64table.index(s[end + 2])
res.append(chr(((c3 << 2)) | ((c2 >> 4) & 0x03)))
res.append(chr(((c2 & 0x0f) << 4) | ((c1 >> 2) & 0x0f)))
return ''.join(res)
def wuaiencrypt(message):
r = ''
for i in list(message):
r += chr(ord(i) ^ 0x20)
r = RC4(r, decryptkey)
return b64encode(r)
def wuaidecrypt(message):
c = list(b64decode(message))
c = list(RC4(b''.join(c), decryptkey))
for i in range(len(c)):
c[i] = chr(ord(c[i]) ^ 0x20)
return ''.join(c)
enc = 'lu_BURGbkz3qtwLXBkYm'
flag = '0325008b4f37de1'
print wuaidecrypt(enc)
print wuaiencrypt(flag)
等大佬解出hash算法再做keygen吧,这个80CB的红包还是不好拿啊。
最后
前两天手贱把trace文件删掉了,今天重新trace check函数,IDA花式崩,花了不少时间。剁手剁手。
附上trace和idb,trc后缀的可以在IDA里加载,另外idb貌似有点问题,附加题目会崩溃,注意一下。
idb和trace.zip
(1.48 MB, 下载次数: 98)