背景
在AppStore中有一款网络小说类的APP,这里就不提App名称了,毕竟是破解别人的应用。现将该小说的txt文件通过抓包、爬虫等途径搞定后,才发现该txt文件是不可读的,它里面的内容顺序是经过程序按照特定算法打乱了,这里给出一个样本文件。那么就要通过逆向工程得出正确的文本内容了。
LLDB
使用lldb与debugserver对应用可执行文件进行动态分析,具体的使用步骤这里就不在详细说明了,这里给出一个参考链接,文章说的很详细。
还要提的一点是,xcode8中的lldb是有BUG的,它会导致断点后显示出来的汇编代码与ida显示的代码不一致,具体参考下图:
如果你也碰到了类似的BUG,请换用xcode5带的lldb即可。
乱序算法
通过逆向分析,我们能够定位到算法的具体位置-[PageViewController analysisChapterContent]。这里给出几段关键的汇编代码:
//这段代码主要是定位txt文件最后一部分正常顺序文件的起始点,并将其保存到var_84内存中
loc_43DCE ; CODE XREF: -[PageViewController analysisChapterContent]+162�j
__text:00043DCE MOV R0, #(selRef_length - 0x43DDA)
__text:00043DD6 ADD R0, PC ; selRef_length
__text:00043DD8 LDR R1, [R0] ; "length"
__text:00043DDA MOV R0, R4
__text:00043DDC STR R1, [SP,#0xE0+var_78]
__text:00043DDE BLX.W _objc_msgSend
__text:00043DE2 MOV R8, R0
__text:00043DE4 MOV R0, #0xBA2E8BA3
__text:00043DEC UMULL.W R0, R2, R8, R0
__text:00043DF0 MOV R0, #(selRef_substringFromIndex_ - 0x43DFC)
__text:00043DF8 ADD R0, PC ; selRef_substringFromIndex_
__text:00043DFA LDR R1, [R0] ; "substringFromIndex:"
__text:00043DFC LSRS R0, R2, #3
__text:00043DFE STR R1, [SP,#0xE0+var_CC]
__text:00043E00 ADD.W R0, R0, R0,LSL#2
__text:00043E04 LSLS R2, R0, #1
__text:00043E06 MOV R0, R4
__text:00043E08 STR R2, [SP,#0xE0+var_84]
__text:00043E0A BLX.W _objc_msgSend
从这段代码里大致能分析出定位起始点的算法:
(文本长度)* 0xBA2E8BA3 = AB;//这里A表示64位结果的前32位
起始点 = (A >> 3) * 5 * 2
但至于这种算法的原理是什么,为什么为这样,0xBA2E8BA3是什么东西,我就搞不明白了,哪位大牛能给我细说下吗?转换成下面的OC代码:
-(NSUInteger)calculateEnd:(NSUInteger)length
{
uint32_t strand = 0xba2e8ba3;
uint64_t mul = (uint64_t)strand * (uint64_t)length;
uint64_t and = 0xffffffff00000000;
uint64_t one = mul & and;
uint32_t result = (uint32_t)(one >> 32);
uint32_t point = (result >> 3) * 10;
return point;
}
另外一段关键汇编代码,主要用于从txt文件起点开始分10段文本各自拼接内容,每一段有各自的拼接算法,进而形成可读的内容。
__text:00043ED2 loc_43ED2 ; CODE XREF: -[PageViewController analysisChapterContent]+372�j
__text:00043ED2 MOV R0, R4
__text:00043ED4 MOV R1, R8
__text:00043ED6 MOV R2, R10
__text:00043ED8 MOVS R3, #1
__text:00043EDA BLX.W _objc_msgSend
__text:00043EDE MOV R2, R0
__text:00043EE0 LDR R0, [SP,#0xE0+var_74]
__text:00043EE2 MOV R1, R6
__text:00043EE4 BLX.W _objc_msgSend
__text:00043EE8 ADD.W R2, R10, #1
__text:00043EEC MOV R0, R4
__text:00043EEE MOV R1, R8
__text:00043EF0 MOVS R3, #1
__text:00043EF2 BLX.W _objc_msgSend
__text:00043EF6 MOV R2, R0
__text:00043EF8 LDR R0, [SP,#0xE0+var_5C]
__text:00043EFA MOV R1, R6
__text:00043EFC BLX.W _objc_msgSend
__text:00043F00 ADD.W R2, R10, #2
__text:00043F04 MOV R0, R4
__text:00043F06 MOV R1, R8
__text:00043F08 MOVS R3, #1
__text:00043F0A BLX.W _objc_msgSend
__text:00043F0E MOV R2, R0
__text:00043F10 LDR R0, [SP,#0xE0+var_68]
__text:00043F12 MOV R1, R5
__text:00043F14 MOVS R3, #0
__text:00043F16 BLX.W _objc_msgSend
__text:00043F1A ADD.W R2, R10, #3
__text:00043F1E MOV R0, R4
__text:00043F20 MOV R1, R8
__text:00043F22 MOVS R3, #1
__text:00043F24 BLX.W _objc_msgSend
__text:00043F28 MOV R2, R0
__text:00043F2A LDR R0, [SP,#0xE0+var_60]
__text:00043F2C MOV R1, R5
__text:00043F2E MOVS R3, #0
__text:00043F30 BLX.W _objc_msgSend
__text:00043F34 ADD.W R2, R10, #4
__text:00043F38 MOV R0, R4
__text:00043F3A MOV R1, R8
__text:00043F3C MOVS R3, #1
__text:00043F3E BLX.W _objc_msgSend
__text:00043F42 MOV R2, R0
__text:00043F44 LDR R0, [SP,#0xE0+var_7C]
__text:00043F46 MOV R1, R5
__text:00043F48 MOVS R3, #0
__text:00043F4A BLX.W _objc_msgSend
__text:00043F4E ADD.W R2, R10, #5
__text:00043F52 MOV R0, R4
__text:00043F54 MOV R1, R8
__text:00043F56 MOVS R3, #1
__text:00043F58 BLX.W _objc_msgSend
__text:00043F5C MOV R2, R0
__text:00043F5E MOV R0, R11
__text:00043F60 MOV R1, R5
__text:00043F62 MOVS R3, #0
__text:00043F64 BLX.W _objc_msgSend
__text:00043F68 ADD.W R2, R10, #6
__text:00043F6C MOV R0, R4
__text:00043F6E MOV R1, R8
__text:00043F70 MOVS R3, #1
__text:00043F72 BLX.W _objc_msgSend
__text:00043F76 MOV R2, R0
__text:00043F78 LDR R0, [SP,#0xE0+var_80]
__text:00043F7A MOV R1, R6
__text:00043F7C BLX.W _objc_msgSend
__text:00043F80 ADD.W R2, R10, #7
__text:00043F84 MOV R0, R4
__text:00043F86 MOV R1, R8
__text:00043F88 MOVS R3, #1
__text:00043F8A BLX.W _objc_msgSend
__text:00043F8E MOV R2, R0
__text:00043F90 LDR R0, [SP,#0xE0+var_70]
__text:00043F92 MOV R1, R5
__text:00043F94 MOVS R3, #0
__text:00043F96 BLX.W _objc_msgSend
__text:00043F9A ADD.W R2, R10, #8
__text:00043F9E MOV R0, R4
__text:00043FA0 MOV R1, R8
__text:00043FA2 MOVS R3, #1
__text:00043FA4 BLX.W _objc_msgSend
__text:00043FA8 MOV R2, R0
__text:00043FAA LDR R0, [SP,#0xE0+var_6C]
__text:00043FAC MOV R1, R6
__text:00043FAE BLX.W _objc_msgSend
__text:00043FB2 ADD.W R2, R10, #9
__text:00043FB6 MOV R0, R4
__text:00043FB8 MOV R1, R8
__text:00043FBA MOVS R3, #1
__text:00043FBC BLX.W _objc_msgSend
__text:00043FC0 MOV R2, R0
__text:00043FC2 LDR R0, [SP,#0xE0+var_64]
__text:00043FC4 MOV R1, R6
__text:00043FC6 BLX.W _objc_msgSend
__text:00043FCA ADD.W R10, R10, #0xA
__text:00043FCE LDR R0, [SP,#0xE0+var_84]
__text:00043FD0 CMP R10, R0
__text:00043FD2 BLT.W loc_43ED2
__text:00043FD6
__text:00043FD6 loc_43FD6
这段代码可以等价为下面的OC代码:
//end表示的是上段代码中获取到的正常顺序起始点
int max = end / 10;
for (int time = 0; time < max; time++) {
for (int index = 0; index < 10; index++) {
NSUInteger len = 1;
NSUInteger location = index + time * 10;
NSRange range = NSMakeRange(location, len);
NSString* character = [content substringWithRange:range];
if (index == 0 || index == 1 || index == 6 || index == 8 || index == 9) {
NSMutableString* line = [lineArray objectAtIndex:index];
[line appendString:character];
}else if (index == 2 || index == 3 || index == 4 || index == 5 || index == 7){
NSMutableString* line = [lineArray objectAtIndex:index];
[line insertString:character atIndex:0];
}
}
}
最终将获取到的所有正常顺序的字符串拼接到一起,便能够正确的解析整个txt文本,产生可读的小说内容,打印LOG如下:
最后的话
代码虽然不多,但汇编代码看起来有点头大,花了两天时间,脑子很混乱,进而文章写的也有点乱了。如果有同学对这方面感兴趣,可以私下聊聊。