反汇编知识点总结
“水至清则无鱼,人至察则无徒”蕴含了做人的道理,它告诉我们水如果太清了,鱼就无法生存;对别人要求太严格了,就没有伙伴或朋友。所以,我们在为人处事方面如果不是原则性的问题,就不要太较真,那样显得我们太个色,会失去很多朋友或同伴。偶尔“难得糊涂”一次也不错。
今天把我之前反汇编so库或bin文件过程中总结的一些经验分享给大家,其中有些是我总结的,有些是我的同事总结的,我一起做了个汇总,分享出来,希望对那些使用IDA反汇编底层库或二进制可执行文件的同事起到一个帮助作用。现在这些知识多少有些模糊了,但对当时的我们来说,可以说句句是干货,每个点都是经过真枪实弹的检验,在实际战场上总结出来的,对于反汇编人员应该是比较有帮助的。
1、类似LDR R2, [R1,#-0xC]指令表示调用寄存器R1代表的类的size()函数;
如果之后还带 CMP R2, #1,表示是将结果和0做比较。
2、调用函数时。
如果函数调用之前的R0值为一个类变量,则代表调用的是一个类成员函数。
例如,
MOV R0, R7 BL _ZN7android7String87toUpperEv ; android::String8::toUpper(void)
表示调用String8的toUpper成员函数。
3、R0,R1,R2,R3在C语言中中一般作为参数传递给函数,但在C++中例外,R0有可能是类本身,参数从R1开始算起;R0还有可能为调用函数的返回值做准备,R1表示类本身,
例如,
.text:00010B74 ADD R4, SP, #0xC0 ; 局部变量,用来保存返回值 .text:00010B78 MOV R1, R7 ; R7 = const Parcel& data .text:00010B7C ADD R5, R5, #0x1C ; 类成员变量stMemory ServieMem2; .text:00010B80 MOV R0, R4 .text:00010B84 BL _ZNK7android6Parcel12readString16Ev ; `android::Parcel::readString16(void)
即是调用data(R1)的readString16函数放到局部变量SP+#0xC0(R0)指向的位置.
4、注意SUBS和其他指令的结合使用。
例如
SUBS R9, R9, #1 BCS loc_12448
SUBS表示R9=R9-1后是否是设置了carry标志位,当R9小于0时,才会设置此标志位,所以当R9<0时,才会执行BCS指令。
5、当寄存器中存放值为String8时,当它与1比较时,
如, CMP R1,1 转换为c,则表示R1与0比较。
6、确认函数是否为类成员函数
如果把类本身作为参数,做为R0并传递给调用的函数,则该调用函数一定是类成员函数。否则调用函数就是一个全局函数。
7、 类的成员偏移
假如R4为类本身,则R4+4为类的this指针,R4+8...之后则为类的成员函数
8、函数调用
如果调用函数为虚函数,会利用到LR,PC指针,并通过寄存器移位操作来完成。
如下所示:
LDR LR,PC LDR PC,[R4,-0xC]
如果调用函数为内联函数,并且为系统自带,会直接通过寄存器移位操作来完成,而不会通过LR,PC指针。如果调用自己写的内联函数,可能会直接将反汇编代码添加到调用该函数处。
如String16有个内联函数size(),R5中存放的为一个String16类型的变量:
LDR R3,[R5,-0xC] -> R5.size();
如果调用函数为其他函数,就会直接出现调用函数的名称,不会通过偏移来实现。
9、LDR R3,[R5,-0xC]
如果在遇到寄存器偏移-0xC的时候,可以考虑是不是调用的是寄存器存放变量的size()函数;(在很多时候偏移-0xC,调用的为size(),length()等情况,其中size()更多)
10、在反汇编时,会出现一个start的函数,其实它就是main函数开始的地方。
11、当一个寄存器中存放一个很长16进制数时,如LDR R5,=0xFFFFDBA8,这时应想到这应该是一个字符串。
在IDA中可以通过View-> Open subviews->Strings(快捷键为Shift+F12)来打开Strings窗口
计算字符串所在地址公式: address = 函数基地址(GLOBAL_OFFSET_TABLE) - 16进制数的反(~0xFFFFDBA8)通过计算出的地址,在Strings窗口中的Address列,查找比较,如果有一样的值,则列子R5中存放的就是找到行中String项
12、在一个反汇编函数中,如果在函数结束的最后会给R0赋值,则代表函数有返回值,且返回值为赋值给R0的数据的类型。否则函数为一个void类型函数,无返回值。
13、 如果反汇编中出现了_Znwj ;operator new(int)的行,则肯定在这地方new了一个对象。
14、 sp指针的定义和释放情况
(1) 定义sp
15、 定义一个结构体作为一个局部变量时,需考虑存储这个结构体的基地址。偏移范围内的值,是这个结构体中的成员。
16、 假如类中有一个Vector
R4+0x54指向mVector中存放的DSVector所在内存块的首个元素的地址。
R4+0x58为mVector函数中的size()函数,即mVector.size();
17、如果给一个寄存器传了一个非常大的数值并最终作为一个参数,这时应考虑该数是不是宏定义的一个数值或几个宏定义值的和
如:调用waitpid时,给第三个参数传了一个40000002;
此时调用waitpid(pid,NULL,__WALL|WUNTRACED);//其中__WALL宏定义为40000000,WUNTRACED宏定义为2;
18、如果出现循环,而操作数为基地值加上index个指定值(index指定值)的时候,则可能为Vector[n]的操作或一个容器中逐个元素的操作,其中指定值为元素的大小,也就是出现了MLA R1,R1,R2,R3,(即R1 = R1R2+R3),应考虑是不是有可能为类似数组一样的for循环操作。
19、TST AND两种与的区别。
TST: TST R12,#0xA -> (R12&0xA) == 0 (此处在&值旁必须加上括号) AND: ANDS R12,#0xA -> R12&0xA
两个都是与,但是加个" == 0"判断,会使用TST,不加则会使用ANDS
20、当寄存器存储一个字符串的偏移值时,则寄存器实际存储的为该字符串偏移值位置的数据。
如下情况, ##
LDR R9,=(aSecloader+8 - 0x14DA8) //其中 aSecloader为“secloader”
从而可以得知寄存器R9中存放的数据为 "r",即aSecloader第8位的值。
21、当定义一个char buf[44] ={0}时,会自动调用memset函数对buf进行清空操作。
22、对于char buf[100],给指定位置赋值使用的是STRB,而不是STR
23、在类中,定义了一个成员变量,假定R4存放类本身,该成员变量是R4+0x3C,且该成员变量的大小占20位。从而,R4+0x3C代表该成员变量,而在R4偏移0x3C~0x50之间的值时,是调用成员变量的属性或函数。
24、在涉及到char*的操作的时候,一定需注意: ##
ADD R5,R4,#5 最终得到的还是一个char指针
LDR R5,[R4,#5] 最终得到的char的字符
25、判断条件的处理
LE,LT,GT,GE 属于带符号数比较大小. CS/HS,CC/LO,HI,LS 属于无符号数比较大小,即为带unsigned类型的数的比较
以上是反汇编的一个经验总结,目的是通过反汇编将其转换成C/C++代码。其中会涉及到C/C++的知识,汇编指令等,如果没有做过这方面的工作,看起来可能和天书一样,不过没关系,大家可以收藏,没准哪天会需要呢,哈哈。
本公众号将以推送Android各种技术干货或碎片化知识,以及整理老司机日常工作中踩过的坑涉及到的经验知识为主,也会不定期将正在学习使用的新技术总结出来进行分享。每天一点干货小知识把你的碎片时间充分利用起来。