iOS逆向-04:if 、循环、switch
int g = 12;
int func(int a,int b){
printf("boy");
int c = a + g + b;
return c;
}
int main(int argc, char * argv[]) {
func(10, 20);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
image.png
adrp
adrp(Address Page)
内存地址按页寻址
取常量
adrp x0, 1
这句代码的操作如下:
将1的值左移12位得到0x1000
将0x1048ae12c的低12位清零得到0x1048ae000
将0x1000和0x1048ae000相加得到0x1048af000
add x0, x0, #0xf89
0x1048af000 + 0xf89 = 0x1048aff89
此时x0的值为0x1048aff89,这个地址里面存的也就是"boy"
一个页的大小是4096,而0xFFF为4095,加上1就是0x1000(即4096),所以是1左移12位即可得到一个页的首地址(注:macOS的pageSize是 4k(0x1000),而iPhone的pageSize是16k(0x4000),但是16仍是4的倍数,adrp兼容者mac和iPhone,所以此时定位的仍然是一页数据)
全局变量也是一样
image.pngHopper反汇编
image.png image.pngimage.png
全局变量g = 0xc 也就是12
也可以通过mach-o来查看
image.png image.png
还原代码
int gl = 12;
int funcC(int a, int b) {
// 0000000100006118 sub sp, sp, #0x20 ; CODE XREF=_main+32
// 000000010000611c stp x29, x30, [sp, #0x10]
// 0000000100006120 add x29, sp, #0x10
// 0000000100006124 stur w0, [x29, #-0x4]
// 0000000100006128 str w1, [sp, #0x8]
// 000000010000612c adrp x0, #0x100007000 ; argument #1 for method imp___stubs__printf
// 0000000100006130 add x0, x0, #0xf89 ; "boy"
// 0000000100006134 bl imp___stubs__printf
printf("boy");
// 0000000100006138 ldur w8, [x29, #-0x4]
int w8 = a;
// 000000010000613c adrp x9, #0x10000d000
// 0000000100006140 add x9, x9, #0x570 ; _g
int x9 = gl;
// 0000000100006144 ldr w10, x9
// 0000000100006148 add w8, w8, w10
int w10 = x9;
w8 = w8 + w10;
// 000000010000614c ldr w10, [sp, #0x8]
w10 = b;
// 0000000100006150 add w8, w8, w10
w8 += w10;
// 0000000100006154 str w8, [sp, #0x4]
// 0000000100006158 ldr w8, [sp, #0x4]
// 000000010000615c mov x0, x8
int w0 = w8;
// 0000000100006160 ldp x29, x30, [sp, #0x10]
// 0000000100006164 add sp, sp, #0x20
return w0;
}
继续精简还原
从下往上还原
int funcC(int a, int b) {
printf("boy");
int w8 = a;
int x9 = gl;
int w10 = x9;
w8 = w8 + w10;
// w10 = b;
// w8 += w10;
// int w0 = w8;
// return w0;
return w8+b;//上面几句还原
}
依次类推得到最终结果
int gl = 12;
int funcC(int a, int b) {
printf("boy");
return a+gl+b;
}
if 语句
我们看看下面代码的汇编
int g = 12;
void func(int a,int b){
if (a > b) {
g = a;
}else{
g = b;
}
}
image.png
通过上面可以看出if else 判断就是 通过cmp + 标号实现的,大于小于都是通过cmp相减。
cmp(Compare)比较指令
CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。
一般CMP做完判断后会进行跳转,后面通常会跟上B指令!
- BL 标号:跳转到标号处执行
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
- B.EQ 标号:比较结果是等于,执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
- B.LT 标号:比较结果是小于,执行标号,否则不跳转
- B.LE 标号:比较结果是小于等于,执行标号,否则不跳转
- B.NE 标号:比较结果是不等于,执行标号,否则不跳转
- B.LS 标号:比较结果是无符号小于等于,执行标号,否则不跳转
- B.LO 标号:比较结果是无符号小于,执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
- B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转
循环
for (int i = 0; i < 100; i++) {
nSum = nSum + 1;
}
image.png
Switch
1、假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用此结构,相当于if。
2、各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构。
3、在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节)。
1 .当有三个case时,可以看到汇编和if else if 是一样的
image.png image.png