2020年春节红包第四题wp

查看 128|回复 10
作者:梦游枪手   
准备工具
IDA Pro,GDA,能真机调试的Android机一台
GDA分析
先把apk拉到GDA,找到MainActivity的onClick方法。


QQ截图20200209135516.png (102.57 KB, 下载次数: 3)
下载附件
2020-2-9 17:52 上传

主要逻辑代码在so文件里面。
IDA分析调试
直接把libxtian.so丢到IDA分析,但没找到checkSn函数,说明函数是动态注册的。so有混淆,无法静态分析,只能真机调试了,真机调试的步骤网上有很多,这里就略过了。
动态注册Native的函数为RegisterNatives,启动调试,IDA停在JNI_OnLoad函数后,转到 _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi,下断后运行。


QQ截图20200209141708.png (83.01 KB, 下载次数: 2)
下载附件
2020-2-9 17:53 上传

跟随R2寄存器,就能找到checkSn函数的地址了。


QQ截图20200209141926.png (20.11 KB, 下载次数: 2)
下载附件
2020-2-9 17:54 上传

重命名为checkSn并下断,在真机那边输入uid和假码,点验证,IDA停在checkSn函数上,说明就是这里了。
现在面对最大的一个问题:混淆。


QQ截图20200209143858.png (32.5 KB, 下载次数: 2)
下载附件
2020-2-9 17:55 上传

看起来是一个自写的类似OLLVM的控制流混淆。因为流程混淆有直接操作PC寄存器的代码,所以IDA的F5报废,字符串也都是运行时才会解密,无法下手。


QQ截图20200209144134.png (35.32 KB, 下载次数: 1)
下载附件
2020-2-9 17:55 上传

楼主不会去混淆,而且为了这个红包题专门去分析混淆手段也太累了,就想顶着混淆解决。
这时候就可以用IDA的trace功能,用它抓取程序执行过的所有指令,不过这个红包题可以不用抓所有指令,可以使用function tracing,只抓取函数调用。


QQ截图20200209152748.png (2.02 KB, 下载次数: 2)
下载附件
2020-2-9 17:55 上传

但不知为何,trace会跑飞,抓取不到所有的函数调用,所以我只好弄了个IDC脚本来单步。
static main(void)
{
    do
    {
        step_over();
        wait_for_next_event(STEP,-1);
    }while(PC!=0xEEDAFC2C);//停在何处
}
注意要设置脚本停止在checkSn函数返回处,设置断点防止跑飞。


QQ截图20200209154954.png (29.36 KB, 下载次数: 2)
下载附件
2020-2-9 17:56 上传

IDA停在checkSn后,打开function tracing,跑一次脚本,等停在checkSnretn,按esc后退,找到跑飞处,在下面那句代码下断。清除trace记录后,在checkSn函数头再跑一次。


QQ截图20200209155235.png (32.91 KB, 下载次数: 1)
下载附件
2020-2-9 17:56 上传

分析checkSn的函数调用
抓到函数调用后,关闭function tracing,在抓取到的指令上下断,跟踪分析,由于我已经分析过一次了,所以指令上面有注释。


QQ截图20200209160730.png (17.71 KB, 下载次数: 1)
下载附件
2020-2-9 17:57 上传

在最后一条BLX上,也就是getpwdlen上下断,停下后步入,注意观察。


QQ截图20200209161134.png (71.2 KB, 下载次数: 3)
下载附件
2020-2-9 17:57 上传

R6寄存器存放着GetStringUTFLength函数的地址,说明这里确实是取字符串长度的,至于是uid还pwd用排除法即可。
走出API调用后,回到checkSn继续往下跟,可以找到检查pwd的长度的代码。


QQ截图20200209162057.png (71.06 KB, 下载次数: 2)
下载附件
2020-2-9 17:58 上传

pwd长度不足15,所以直接返回了,我们修改下假码,继续抓取,逐个分析即可。


QQ截图20200209163941.png (26.08 KB, 下载次数: 3)
下载附件
2020-2-9 17:58 上传

现在看图里的trace也能知道大致流程了,checkSn获取输入的uid和pwd,调用check函数,判断是否pwd符合uid。修改check函数的返回值为1并运行,Toast显示成功,说明正确。
现在把分析目标转到check函数,check函数内也有混淆,修改上面的脚本为PC!=0xEEDAF362并下断,并使用上面的步骤抓取所有函数调用。


QQ截图20200209203630.png (99.22 KB, 下载次数: 2)
下载附件
2020-2-9 20:36 上传

分析check函数
我的IDA在check函数无法使用function tracing,app会崩溃,原因未知,所以我只能改成instruction tracing了。抓取的指令太多就不展示出来了,我会放在附件里。
这里说下check函数的分析思路和主要流程。
在trace文件里找出所有BLX,下断步入,如果是调用API,则跟踪出API名,如果是调用so本身的函数,那就直接在LR的地址下断运行。不要忘记记录参数和返回值。
如果对每个调用的内部函数都trace一遍,读代码,我觉得要花太多时间,所以记录参数和返回值,凭经验直接猜算法,如果猜不到再trace指令。
check函数首先调用gettimeofday,然后将timeval除以1800000,也就是30分钟。


QQ截图20200209215546.png (86.92 KB, 下载次数: 3)
下载附件
2020-2-9 21:58 上传

然后将uid和结果拼接在一起,算出一个32字节的hash,存放在栈顶地址处。


QQ截图20200210020601.png (98.52 KB, 下载次数: 3)
下载附件
2020-2-10 02:06 上传

这个hash算法我没能还原出来,本来以为是sm3,但是验算了几次都不对,跟也跟不出个头绪,就直接过了,还好不影响解出题目。
之后取出hash的17-20位。


QQ截图20200210020926.png (68.85 KB, 下载次数: 2)
下载附件
2020-2-10 02:09 上传

与uid拼接到一起,等待最后比较。


QQ截图20200210021302.png (3.97 KB, 下载次数: 2)
下载附件
2020-2-10 02:13 上传

接着对pwd进行base64解码,码表是修改过的,图里写的sm3就是上面的hash,忘了改注释。


QQ截图20200210021532.png (98.89 KB, 下载次数: 2)
下载附件
2020-2-10 02:16 上传

解码后,再进行RC4解密,key为52pojie2020xtian


QQ截图20200210022022.png (90.17 KB, 下载次数: 2)
下载附件
2020-2-10 02:20 上传

逐位异或0x20。


QQ截图20200210022121.png (50.72 KB, 下载次数: 3)
下载附件
2020-2-10 02:22 上传



QQ截图20200210022346.png (57.6 KB, 下载次数: 2)
下载附件
2020-2-10 02:24 上传

最后与uid+hash比较。


QQ截图20200210022616.png (90.15 KB, 下载次数: 3)
下载附件
2020-2-10 02:27 上传

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)
        c1 = ord(s[i + 1])
        c0 = ord(s[i + 2])
        res.append(b64table[(c2 >> 2) & 0x3f])
        res.append(b64table[((c2 & 0x3) > 4) & 0x0f)])
        res.append(b64table[((c1 & 0x0f) > 6) & 0x03)])
        res.append(b64table[c0 & 0x3f])
    i += 3
    if leftover == 1:
        c2 = ord(s)
        res.append(b64table[(c2 >> 2) & 0x3f])
        res.append(b64table[(c2 & 0x3) > 2) & 0x3f])
        res.append(b64table[((c2 & 0x3) > 4) & 0x0f)])
        res.append(b64table[(c1 & 0x0f) > 4) & 0x03)))
        res.append(chr(((c2 & 0x0f) > 2) & 0x0f)))
        res.append(chr(((c1 & 0x03) > 4) & 0x03)))
        else:
            c3, c2, c1 = b64table.index(s[end]), b64table.index(s[end + 1]), b64table.index(s[end + 2])
            res.append(chr(((c3 > 4) & 0x03)))
            res.append(chr(((c2 & 0x0f) > 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 = chr(ord(c) ^ 0x20)
    return ''.join(c)
enc = 'lu_BURGbkz3qtwLXBkYm'
flag = '0325008b4f37de1'
print wuaidecrypt(enc)
print wuaiencrypt(flag)
等大佬解出hash算法再做keygen吧,这个80CB的红包还是不好拿啊。


Screenshot_2020-02-10-02-29-04-258_com.wuaipojie..png (172.04 KB, 下载次数: 2)
下载附件
2020-2-10 03:06 上传

最后
前两天手贱把trace文件删掉了,今天重新trace check函数,IDA花式崩,花了不少时间。剁手剁手。
附上trace和idb,trc后缀的可以在IDA里加载,另外idb貌似有点问题,附加题目会崩溃,注意一下。

idb和trace.zip
(1.48 MB, 下载次数: 98)
2020-2-10 02:50 上传
点击文件名下载附件
下载积分: 吾爱币 -1 CB

下载次数, 函数

hlrlqy   

牛叉,80cb确实太少了,写分享再送200。
史迪仔   

手撕ollvm的大佬,佩服佩服
这一手trace用的属实惊艳
灰灰。   

虽然看不懂,但还是看看多学习学习
qwesyw2008   

很强,还从没用过ida的trace
fwmsuper   

看不懂,但是大佬nb
李梦洁   

感谢楼主辛苦分享
李梦洁   

感谢楼主辛苦分享
jefel   

感谢楼主分享
kone153   

高手过拆!!
您需要登录后才可以回帖 登录 | 立即注册