0x8.so逆向深化及IDA静态分析-3

so逆向深化及IDA静态分析-3 : IDA静态分析之so逆向进阶实战


本文介绍一个CackMe的分析过程,不再是单纯的分析逻辑,还包括还原算法。


本文样本链接:https://pan.baidu.com/s/1lKNATw_PLhYQyUT-BBbUrw  提取码:xmu1 
<该样本为个人编写,如果引用请提前告知,谢谢>

0x1.java层分析
我们首先打开样本,了解一下情况,方便确认突破口。
     

左侧图为打开后的界面,右侧为输入任意密码并提交后的界面,此时我们可以定位首个关键字 “密码错误”。
接下来我们将样本拖拽到AndroidKiller之中逆向,然后文本转Unicode,搜索该关键字: 


搜索到关键字后,我们可以借助其java源码协助分析,也可以直接分析smali。下面以java源码为例:
在Android Killer中点击java源码对应的图标,打开jd-GUI查看该样本的java源码。


根据上图步骤一步步定位到了关键函数setCode,我们进一步追踪。


最终定位到native函数setCode,并确定其所在的so文件为“libJniSoDemo.so”。

0x2.so层分析
定位到so文件的native函数后,我们开始分析so文件(以此路径为例>SoDemo_killer>Project>lib>armeabi-v7a),
将其拖入IDA后,在Exports一栏找到setCode函数


双击查看函数中的ARM指令如下:


接下来我们看下这段ARM指令(具体指令可查询之前共享的指令集或者百度):
.text:00000EA4                 EXPORT Java_com_kanxue_sodemo_jnisodemo_setCode
.text:00000EA4 Java_com_kanxue_sodemo_jnisodemo_setCode
.text:00000EA4                                         ; DATA XREF: LOAD:000001BC↑o
.text:00000EA4 ; __unwind {
.text:00000EA4                 MOVS            R0, #0                                         // R0=0
.text:00000EA6                 CMP             R3, #3                                         // 比较R3与3
.text:00000EA8                 BNE             locret_EEA                                         // 如果R3≠3,跳转到locret_EEA
.text:00000EAA                 SUB.W           R1, R2, #0x1F4                                         // R1=R2-500
.text:00000EAE                 CMP             R1, #0x63 ; 'c'                                         // 比较R1和99
.text:00000EB0                 BHI             locret_EEA                                         // 如果R1大于99,跳转到locret_EEA
.text:00000EB2                 MOV             R0, #0x66666667                                         // R0=0x66666667
.text:00000EBA                 SMMUL.W         R0, R1, R0                                         // R0=R1*R0
.text:00000EBE                 MOV             R1, #0x51EB851F                                         // R1=0x51EB851F
.text:00000EC6                 SMMUL.W         R1, R2, R1                                         // R1=R2*R1
.text:00000ECA                 ASRS            R3, R0, #2                                         // R3=(R0>>2)
.text:00000ECC                 ADD.W           R12, R3, R0,LSR#31                                         // R12=R3+(R0>>31)
.text:00000ED0                 ASRS            R0, R1, #5                                         // R0=(R1>>5)
.text:00000ED2                 ADD.W           R1, R0, R1,LSR#31                                         // R1=R0+(R1>>31)
.text:00000ED6                 SUBS            R3, R1, #3                                         // R3=R1-3
.text:00000ED8                 MOVS            R0, #0                                         // R0=0
.text:00000EDA                 CMP             R12, R3                                         // 比较R12和R3
.text:00000EDC                 IT NE                                         // IT(If-Then) NE(Not-Equal) 如果不相等则执行下一句
.text:00000EDE                 BXNE            LR                                         // 承接上一句的条件,函数返回
.text:00000EE0                 SUBW            R2, R2, #0x203                                         // R2=R2-515
.text:00000EE4                 CMP             R2, R1                                         // 比较R2和R1
.text:00000EE6                 IT EQ                                         // 如果相等则执行下一句
.text:00000EE8                 MOVEQ           R0, #1                                         // 承接上一句的条件,R0=1
.text:00000EEA
.text:00000EEA locret_EEA                              ; CODE XREF: Java_com_kanxue_sodemo_jnisodemo_setCode+4↑j
.text:00000EEA                                         ; Java_com_kanxue_sodemo_jnisodemo_setCode+C↑j
.text:00000EEA                 BX              LR                                         // 函数返回
.text:00000EEA ; End of function Java_com_kanxue_sodemo_jnisodemo_setCode
可参考伪代码插件,按下F5,看到C代码如下:


在函数中,我们只看到了a3和a4,显然a3和a4是我们传入给setCode的两个参数。
参考jd-GUI中的java源码,我们可以得知两个参数是什么。


在setCode中,首先判断了位数是否为3,即 a4 == 3
然后判断了第一位是否为5,即 (a3-500)≤ 99
不同时满足以上两个,则返回0,即 密码有三位,且第一位是5。
后面 if ( (a3 - 500) / 10 == a3 / 100 - 3 && a3 - 515 == a3 / 100 )
则是判断第二位是2,第三位是0,得出密码为520。

以上是算法分析,如果是单纯逻辑分析的话,可以修改逻辑一直返回1,或者修改判断条件,本文重点是算法分析,此处不再赘述。

0x3.算法还原
在算出520的时候,我们已经对算法进行了还原。主要是几个固定条件:
① 密码长度 a4 = 3 ;
② (a3 - 500) ≤ 99 ;
③ (a3 - 500) / 10 = a3 / 100 - 3 ;
④  a3 - 515 = a3 / 100 。

以上算法比较简单就不需要单独写程序来算出密码了,对于比较复杂的运算我们需要编写程序算出密码,也就是所谓的注册机。

0x4.总结
在静态分析中,我们通常从java层入手,顺着逻辑追踪到so层,再对so层进行分析,结合java层的函数找出我们需要的算法,
必要的时候根据密码验证算法进行逆运算,导出密码。