iOS 逆向开发 文章汇总
目录
- 一、cmp(Compare)比较指令
- 二、循环
- 三、Switch
3.1 分支较少的Switch、3.2 分支较多的Switch、3.3 Switch建表查询总结、3.4 Switch总结
一、cmp(Compare)比较指令
CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。
一般CMP做完判断后会进行跳转,后面通常会跟上B指令!
- BL 标号:跳转到标号处执行
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
- B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转
- B.LT 标号:比较结果是*小于,执行标号,否则不跳转
- B.LE 标号:比较结果是*小于等于(less than or equal to),执行标号,否则不跳转
- B.LO 标号:比较结果是无符号小于,执行标号,否则不跳转
- B.LS 标号:比较结果是无符号小于等于,执行标号,否则不跳转
- B.EQ 标号:比较结果是等于,执行标号,否则不跳转
- B.NE 标号:比较结果是*不等于,执行标号,否则不跳转
b.?? 后面跟着的是else中的代码
int g = 12;
void func(int a, int b) {
if (a > b) {
g = a;
} else {
g = b;
}
}
int main(int argc, char * argv[]) {
func(1, 2);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
在Hopper
中可以看到if判断中的代码:
_func:
0000000100001f00 sub sp, sp, #0x10 ; CODE XREF=_main+32
0000000100001f04 str w0, [sp, #0xc]
0000000100001f08 str w1, [sp, #0x8]
0000000100001f0c ldr w8, [sp, #0xc]
0000000100001f10 ldr w9, [sp, #0x8]
// 比较w8, w9
0000000100001f14 cmp w8, w9
0000000100001f18 b.le loc_100001f30(le:小于等于--跳到else中-->因此下面是大于)
if (w8 > w9)
0000000100001f1c ldr w8, [sp, #0xc]
0000000100001f20 adrp x9, #0x100009000
0000000100001f24 add x9, x9, #0x478 ; _g
0000000100001f28 str w8, x9
0000000100001f2c b loc_100001f40
else
loc_100001f30:
0000000100001f30 ldr w8, [sp, #0x8] ; CODE XREF=_func+24
0000000100001f34 adrp x9, #0x100009000
0000000100001f38 add x9, x9, #0x478 ; _g
0000000100001f3c str w8, x9
loc_100001f40:
0000000100001f40 add sp, sp, #0x10 ; CODE XREF=_func+44
0000000100001f44 ret
; endp
cmp执行的是减法、修改标记寄存器
汇编最后没有通过x0寄存器
或上个栈中的地址
存储返回值,那么函数就没有返回值
二、循环
do-while循环:
int main(int argc, char * argv[]) {
int nSum = 0;
int i = 0;
do {
nSum = nSum + 1;
I++;
} while (i < 100);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
_main:
0000000100001f80 sub sp, sp, #0x40
0000000100001f84 stp x29, x30, [sp, #0x30]
0000000100001f88 add x29, sp, #0x30
0000000100001f8c stur wzr, [x29, #-0x4]
0000000100001f90 stur w0, [x29, #-0x8]
0000000100001f94 stur x1, [x29, #-0x10]
0000000100001f98 stur wzr, [x29, #-0x14]
0000000100001f9c str wzr, [sp, #0x18]
loc_100001fa0:
0000000100001fa0 ldur w8, [x29, #-0x14] ; CODE XREF=_main+64
0000000100001fa4 add w8, w8, #0x1
0000000100001fa8 stur w8, [x29, #-0x14]
0000000100001fac ldr w8, [sp, #0x18]
0000000100001fb0 add w8, w8, #0x1
0000000100001fb4 str w8, [sp, #0x18]
0000000100001fb8 ldr w8, [sp, #0x18]
0000000100001fbc cmp w8, #0x64
0000000100001fc0 b.lt loc_100001fa0
.....
while循环:
int main(int argc, char * argv[]) {
int nSum = 0;
int i = 0;
while (i < 100) {
nSum = nSum + 1;
I++;
};
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
0000000100001f7c sub sp, sp, #0x40
0000000100001f80 stp x29, x30, [sp, #0x30]
0000000100001f84 add x29, sp, #0x30
0000000100001f88 stur wzr, [x29, #-0x4]
0000000100001f8c stur w0, [x29, #-0x8]
0000000100001f90 stur x1, [x29, #-0x10]
0000000100001f94 stur wzr, [x29, #-0x14]
0000000100001f98 str wzr, [sp, #0x18]
loc_100001f9c:
0000000100001f9c ldr w8, [sp, #0x18] ; CODE XREF=_main+68
0000000100001fa0 cmp w8, #0x64
0000000100001fa4 b.ge loc_100001fc4
0000000100001fa8 ldur w8, [x29, #-0x14]
0000000100001fac add w8, w8, #0x1
0000000100001fb0 stur w8, [x29, #-0x14]
0000000100001fb4 ldr w8, [sp, #0x18]
0000000100001fb8 add w8, w8, #0x1
0000000100001fbc str w8, [sp, #0x18]
0000000100001fc0 b loc_100001f9c
loc_100001fc4:
....
for循环和while循环一样。
三、Switch
3.1 分支较少的Switch
void funcA(int a) {
switch (a) {
case 1:
printf("--1--");
break;
case 2:
printf("--2--");
break;
case 3:
printf("--3--");
break;
default:
printf("--none--");
break;
}
}
int main(int argc, char * argv[]) {
funcA(1);
return 0;
}
汇编如下:
004--选择`funcA:
0x10292df84 <+0>: sub sp, sp, #0x20 ; =0x20
0x10292df88 <+4>: stp x29, x30, [sp, #0x10]
0x10292df8c <+8>: add x29, sp, #0x10 ; =0x10
// 参数入栈
0x10292df90 <+12>: stur w0, [x29, #-0x4]
-> 0x10292df94 <+16>: ldur w8, [x29, #-0x4]
// 和1比较
0x10292df98 <+20>: cmp w8, #0x1 ; =0x1
0x10292df9c <+24>: str w8, [sp, #0x8]
0x10292dfa0 <+28>: b.eq 0x10292dfc8 ; ---> case1
0x10292dfa4 <+32>: b 0x10292dfa8 ; ---> 和2比较
// 和2比较
0x10292dfa8 <+36>: ldr w8, [sp, #0x8]
0x10292dfac <+40>: cmp w8, #0x2 ; =0x2
0x10292dfb0 <+44>: b.eq 0x10292dfd8 ; ---> case2
0x10292dfb4 <+48>: b 0x10292dfb8 ; ---> 和3比较
// 和3比较
0x10292dfb8 <+52>: ldr w8, [sp, #0x8]
0x10292dfbc <+56>: cmp w8, #0x3 ; =0x3
0x10292dfc0 <+60>: b.eq 0x10292dfe8 ; ---> case3
0x10292dfc4 <+64>: b 0x10292dff8 ; ---> default
//case 1:
0x10292dfc8 <+68>: adrp x0, 2
0x10292dfcc <+72>: add x0, x0, #0xdfd ; =0xdfd
0x10292dfd0 <+76>: bl 0x10292e384 ; symbol stub for: printf
0x10292dfd4 <+80>: b 0x10292e004 ; <+128> at main.m:30:1
//case 2:
0x10292dfd8 <+84>: adrp x0, 2
0x10292dfdc <+88>: add x0, x0, #0xe03 ; =0xe03
0x10292dfe0 <+92>: bl 0x10292e384 ; symbol stub for: printf
0x10292dfe4 <+96>: b 0x10292e004 ; <+128> at main.m:30:1
//case 3:
0x10292dfe8 <+100>: adrp x0, 2
0x10292dfec <+104>: add x0, x0, #0xe09 ; =0xe09
0x10292dff0 <+108>: bl 0x10292e384 ; symbol stub for: printf
0x10292dff4 <+112>: b 0x10292e004 ; <+128> at main.m:30:1
//default
0x10292dff8 <+116>: adrp x0, 2
0x10292dffc <+120>: add x0, x0, #0xe0f ; =0xe0f
0x10292e000 <+124>: bl 0x10292e384 ; symbol stub for: printf
0x10292e004 <+128>: ldp x29, x30, [sp, #0x10]
0x10292e008 <+132>: add sp, sp, #0x20 ; =0x20
0x10292e00c <+136>: ret
3.2 分支较多的Switch
添加case:
case 4:
printf("--4--");
break;
004--选择`funcA:
-> 0x104621f64 <+0>: sub sp, sp, #0x20 ; =0x20
0x104621f68 <+4>: stp x29, x30, [sp, #0x10]
0x104621f6c <+8>: add x29, sp, #0x10 ; =0x10
// 参数入栈
0x104621f70 <+12>: stur w0, [x29, #-0x4]
0x104621f74 <+16>: ldur w8, [x29, #-0x4]
// 参数-1(减去最小的case),subs会影响状态寄存器
0x104621f78 <+20>: subs w8, w8, #0x1 ; =0x1
0x104621f7c <+24>: mov x9, x8 ; 将x9=x8(注意是x8,不是上面的w8)
0x104621f80 <+28>: ubfx x9, x9, #0, #32 ; 将x9高32位清零
0x104621f84 <+32>: cmp x9, #0x3 ; 和3(最大case与最小case的差值)比较
0x104621f88 <+36>: str x9, [sp]
0x104621f8c <+40>: b.hi 0x104621fe8 ; (参数-最小case和差值比较)hi:无符号大于--->default
0x104621f90 <+44>: adrp x8, 1
0x104621f94 <+48>: add x8, x8, #0x0 ; 这两行得到x8=0x104622000即函数结束后的地址
0x104621f98 <+52>: ldr x11, [sp]
0x104621f9c <+56>: ldrsw x10, [x8, x11, lsl #2] ; 在表中找出偏移值
0x104621fa0 <+60>: add x9, x8, x10 ; x8加上偏移值(负数)得到下面case或default的地址
0x104621fa4 <+64>: br x9 ; 直接跳转到计算出的case地址
//case 1:
0x104621fa8 <+68>: adrp x0, 2
0x104621fac <+72>: add x0, x0, #0xdfd ; =0xdfd
0x104621fb0 <+76>: bl 0x104622384 ; symbol stub for: printf
0x104621fb4 <+80>: b 0x104621ff4 ; --->跳出Switch
//case 2:
0x104621fb8 <+84>: adrp x0, 2
0x104621fbc <+88>: add x0, x0, #0xe03 ; =0xe03
0x104621fc0 <+92>: bl 0x104622384 ; symbol stub for: printf
0x104621fc4 <+96>: b 0x104621ff4 ; --->跳出Switch
//case 3:
0x104621fc8 <+100>: adrp x0, 2
0x104621fcc <+104>: add x0, x0, #0xe09 ; =0xe09
0x104621fd0 <+108>: bl 0x104622384 ; symbol stub for: printf
0x104621fd4 <+112>: b 0x104621ff4 ; --->跳出Switch
//case 4:
0x104621fd8 <+116>: adrp x0, 2
0x104621fdc <+120>: add x0, x0, #0xe0f ; =0xe0f
0x104621fe0 <+124>: bl 0x104622384 ; symbol stub for: printf
0x104621fe4 <+128>: b 0x104621ff4 ; --->跳出Switch
// default
0x104621fe8 <+132>: adrp x0, 2
0x104621fec <+136>: add x0, x0, #0xe15 ; =0xe15
0x104621ff0 <+140>: bl 0x104622384 ; symbol stub for: printf
0x104621ff4 <+144>: ldp x29, x30, [sp, #0x10]
0x104621ff8 <+148>: add sp, sp, #0x20 ; =0x20
0x104621ffc <+152>: ret
ubfx命令解读:ubfx x9, x9, #0, #32
将x9中第#0位到第 #32位取出来赋值给x9其余位用0补足-----即高32位清零。
ldrsw命令解读:ldrsw x10, [x8, x11, lsl #2]
第一步:x11, lsl #2--->x11左移两位(即乘以4:表中是4个字节为单位的)
第二步:x8 加上 第一步的结果 (x8指向函数结束后的地址,保存了几个负数)
第三步:取出[]中地址的值保存到x10--->得到偏移值
br x9:跳转到x9寄存器中保存的地址,不改变lr(x30)寄存器
br x9等价于:b [x9]
验证ubfx清零的是高32位:
查看函数结束后的地址中的数据表
4个负数表示差值3+1(如果case是连续的表中就没有default的偏移值,如果不连续表中有一个是default的偏移值???)
x8即负数表的地址加上表的值即能得到相应case的地址
Switch 中case和default分支的代码段是连续的
3.3 Switch建表查询总结
- 先将参数减去最小case -->x9
- x9和差值(最大case-最小case)比较,如果无符号大于--->default
- 通过表,直接计算出分支地址
3.4 Switch总结
- 假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用建表查询结构,相当于if。
- 在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节),以
空间换取时间
。 - 分支数量大于3但各个分支常量的
差值较大
的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是可能会编译成类似于if,else的结构。