objective-c runtime安全措施之三:反汇编(inline编译方式)
4229 点击·0 回帖
![]() | ![]() | |
![]() | 《O'Reilly.Hacking.and.Securing.ios.Applications>>读书笔记 反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑) 方法1:采用inline函数 原理:设置inline属性、static属性,可以使得编译后生成的目标代码在反汇编成汇编代码时,不容易阅读,弄清其中的逻辑,因为inline会导致汇编代码中是直接将inline函数的函数体拷贝到main中,而不是清晰的call调用;static属性,则会让编译生成的二进制代码中,没有清晰的符号表,同样使得攻击者很难通过逆向弄清楚程序逻辑。 例子1. non-inline函数和其反汇编代码 #include <stdlib.h> int is_session_valid(int session_id) {//session_id为偶数返回1,为奇数返回0 if (session_id % 2 == 0) { return 1; } else { return 0; } } int main( ) { int session = 3; if (! is_session_valid(session)) return EXIT_FAILURE; /* * Do something else */ if (! is_session_valid(session)) return EXIT_FAILURE; /* * Do something else */ if (! is_session_valid(session)) return EXIT_FAILURE; return EXIT_SUCCESS; } 编译$ gcc -o securitycheck securitycheck.c 使用otool工具反汇编该程序,为了方便阅读start函数已经被移除了。 $ otool -tV securitycheck _is_session_valid: 0000000100000e70 pushq %rbp 0000000100000e71 movq %rsp,%rbp 0000000100000e74 movl %edi,0xfc(%rbp) 0000000100000e77 movl 0xfc(%rbp),%eax 0000000100000e7a andl $0x01,%eax 0000000100000e7d cmpl $0x00,%eax 0000000100000e80 jne 0x100000e8b 0000000100000e82 movl $0x00000001,0xf4(%rbp) 0000000100000e89 jmp 0x100000e92 0000000100000e8b movl $0x00000000,0xf4(%rbp) 0000000100000e92 movl 0xf4(%rbp),%eax 0000000100000e95 movl %eax,0xf8(%rbp) 0000000100000e98 movl 0xf8(%rbp),%eax 0000000100000e9b popq %rbp 0000000100000e9c ret 0000000100000e9d nopl (%rax) _main: 0000000100000ea0 pushq %rbp 0000000100000ea1 movq %rsp,%rbp 0000000100000ea4 subq $0x10,%rsp 0000000100000ea8 movl $0x00000003,0xf4(%rbp) 0000000100000eaf movl 0xf4(%rbp),%eax 0000000100000eb2 movl %eax,%edi 0000000100000eb4 callq _is_session_valid 0000000100000eb9 movl %eax,%ecx 0000000100000ebb cmpl $0x00,%ecx 0000000100000ebe jne 0x100000ec9 0000000100000ec0 movl $0x00000001,0xf8(%rbp) 0000000100000ec7 jmp 0x100000f04 0000000100000ec9 movl 0xf4(%rbp),%eax 0000000100000ecc movl %eax,%edi 0000000100000ece callq _is_session_valid 0000000100000ed3 movl %eax,%ecx 0000000100000ed5 cmpl $0x00,%ecx 0000000100000ed8 jne 0x100000ee3 0000000100000eda movl $0x00000001,0xf8(%rbp) 0000000100000ee1 jmp 0x100000f04 0000000100000ee3 movl 0xf4(%rbp),%eax 0000000100000ee6 movl %eax,%edi 0000000100000ee8 callq _is_session_valid 0000000100000eed movl %eax,%ecx 0000000100000eef cmpl $0x00,%ecx 0000000100000ef2 jne 0x100000efd 0000000100000ef4 movl $0x00000001,0xf8(%rbp) 0000000100000efb jmp 0x100000f04 0000000100000efd movl $0x00000000,0xf8(%rbp) 0000000100000f04 movl 0xf8(%rbp),%eax 0000000100000f07 movl %eax,0xfc(%rbp) 0000000100000f0a movl 0xfc(%rbp),%eax 0000000100000f0d addq $0x10,%rsp 0000000100000f11 popq %rbp 0000000100000f12 ret 如你所知,is_sesssion_valid函数和main函数的定义是非常清晰的。攻击者是非常容易通过动态调试设置断点的方法修改is_session_valid函数的返回值来绕过检查。 $ gdb -q ./securitycheck Reading symbols for shared libraries .. done (gdb) break is_session_valid Breakpoint 1 at 0x100000e77 (gdb) commands Type commands for when breakpoint 1 is hit, one per line. End with a line saying just "end". >return 1 >continue >end (gdb) r Starting program: /Users/jonz/securitycheck Reading symbols for shared libraries +........................ done Breakpoint 1, 0x0000000100000e77 in is_session_valid () Breakpoint 1, 0x0000000100000e77 in is_session_valid () Breakpoint 1, 0x0000000100000e77 in is_session_valid () Program exited normally. (gdb) 程序在第一次调用is_session_valid时不会退出,因为调试器将is_session_valid函数值改成了1. 例子2:inline函数和其反汇编代码 #include <stdlib.h> inline int is_session_valid(int session_id) { if (session_id % 2 == 0) { return 1; } else { return 0; } } int main( ) { int session = 3; if (! is_session_valid(session)) return EXIT_FAILURE; /* * Do something else */ if (! is_session_valid(session)) return EXIT_FAILURE; /* * Do something else */ if (! is_session_valid(session)) return EXIT_FAILURE; return EXIT_SUCCESS; } 编译, -finline-functions告诉编译器尽可能的采用inline编译方式;-Winline告诉编译器在不能使用inline编译方式编译时报错 $ gcc -o securitycheck securitycheck.c -finline-functions -Winline 使用otool工具反汇编该程序 $ otool -tV securitycheck _is_session_valid: 0000000100000e20 pushq %rbp 0000000100000e21 movq %rsp,%rbp 0000000100000e24 movl %edi,0xfc(%rbp) 0000000100000e27 movl 0xfc(%rbp),%eax 0000000100000e2a andl $0x01,%eax 0000000100000e2d cmpl $0x00,%eax 0000000100000e30 jne 0x100000e3b 0000000100000e32 movl $0x00000001,0xf4(%rbp) 0000000100000e39 jmp 0x100000e42 0000000100000e3b movl $0x00000000,0xf4(%rbp) 0000000100000e42 movl 0xf4(%rbp),%eax 0000000100000e45 movl %eax,0xf8(%rbp) 0000000100000e48 movl 0xf8(%rbp),%eax 0000000100000e4b popq %rbp 0000000100000e4c ret 0000000100000e4d nopl (%rax) _main: 0000000100000e50 pushq %rbp 0000000100000e51 movq %rsp,%rbp 0000000100000e54 movl $0x00000003,0xd0(%rbp) 0000000100000e5b movl 0xd0(%rbp),%eax 0000000100000e5e movl %eax,0xe4(%rbp) 0000000100000e61 movl 0xe4(%rbp),%eax 0000000100000e64 andl $0x01,%eax 0000000100000e67 cmpl $0x00,%eax 0000000100000e6a jne 0x100000e75 0000000100000e6c movl $0x00000001,0xdc(%rbp) 0000000100000e73 jmp 0x100000e7c 0000000100000e75 movl $0x00000000,0xdc(%rbp) 0000000100000e7c movl 0xdc(%rbp),%eax 0000000100000e7f movl %eax,0xe0(%rbp) 0000000100000e82 movl 0xe0(%rbp),%eax 0000000100000e85 cmpl $0x00,%eax 0000000100000e88 jne 0x100000e93 0000000100000e8a movl $0x00000001,0xd4(%rbp) 0000000100000e91 jmp 0x100000f0a 0000000100000e93 movl 0xd0(%rbp),%eax 0000000100000e96 movl %eax,0xfc(%rbp) 0000000100000e99 movl 0xfc(%rbp),%eax 0000000100000e9c andl $0x01,%eax 0000000100000e9f cmpl $0x00,%eax 0000000100000ea2 jne 0x100000ead 0000000100000ea4 movl $0x00000001,0xf4(%rbp) 0000000100000eab jmp 0x100000eb4 0000000100000ead movl $0x00000000,0xf4(%rbp) 0000000100000eb4 movl 0xf4(%rbp),%eax 0000000100000eb7 movl %eax,0xf8(%rbp) 0000000100000eba movl 0xf8(%rbp),%eax 0000000100000ebd cmpl $0x00,%eax 0000000100000ec0 jne 0x100000ecb 0000000100000ec2 movl $0x00000001,0xd4(%rbp) 0000000100000e91 jmp 0x100000f0a 0000000100000e93 movl 0xd0(%rbp),%eax 0000000100000e96 movl %eax,0xfc(%rbp) 0000000100000e99 movl 0xfc(%rbp),%eax 0000000100000e9c andl $0x01,%eax 0000000100000e9f cmpl $0x00,%eax 0000000100000ea2 jne 0x100000ead 0000000100000ea4 movl $0x00000001,0xf4(%rbp) 0000000100000eab jmp 0x100000eb4 0000000100000ead movl $0x00000000,0xf4(%rbp) 0000000100000eb4 movl 0xf4(%rbp),%eax 0000000100000eb7 movl %eax,0xf8(%rbp) 0000000100000eba movl 0xf8(%rbp),%eax 0000000100000ebd cmpl $0x00,%eax 0000000100000ec0 jne 0x100000ecb 0000000100000ec2 movl $0x00000001,0xd4(%rbp) 0000000100000ec9 jmp 0x100000f0a 0000000100000ecb movl 0xd0(%rbp),%eax 0000000100000ece movl %eax,0xf0(%rbp) 0000000100000ed1 movl 0xf0(%rbp),%eax 0000000100000ed4 andl $0x01,%eax 0000000100000ed7 cmpl $0x00,%eax 0000000100000eda jne 0x100000ee5 0000000100000edc movl $0x00000001,0xe8(%rbp) 0000000100000ee3 jmp 0x100000eec 0000000100000ee5 movl $0x00000000,0xe8(%rbp) 0000000100000eec movl 0xe8(%rbp),%eax 0000000100000eef movl %eax,0xec(%rbp) 0000000100000ef2 movl 0xec(%rbp),%eax 0000000100000ef5 cmpl $0x00,%eax 0000000100000ef8 jne 0x100000f03 0000000100000efa movl $0x00000001,0xd4(%rbp) 0000000100000f01 jmp 0x100000f0a 0000000100000f03 movl $0x00000000,0xd4(%rbp) 0000000100000f0a movl 0xd4(%rbp),%eax 0000000100000f0d movl %eax,0xd8(%rbp) 0000000100000f10 movl 0xd8(%rbp),%eax 0000000100000f13 popq %rbp 0000000100000f14 ret 新的反汇编代码有非常多的不同, is_session_valid函数不会在main函数中被调用了,而是在main函数中将is_session_valid的汇编代码拷贝了3次 例子3:static inline函数和其反汇编代码 #include <stdlib.h> static int is_session_valid(int session_id) __attribute__((always_inline)); int is_session_valid(int session_id) { if (session_id % 2 == 0) { return 1; } else { return 0; } } ... 反汇编 $ otool -tV securitycheck securitycheck: (__TEXT,__text) section start: 0000000100000e40 pushq $0x00 0000000100000e42 movq %rsp,%rbp 0000000100000e45 andq $0xf0,%rsp 0000000100000e49 movq 0x08(%rbp),%rdi 0000000100000e4d leaq 0x10(%rbp),%rsi 0000000100000e51 movl %edi,%edx 0000000100000e53 addl $0x01,%edx 0000000100000e56 shll $0x03,%edx 0000000100000e59 addq %rsi,%rdx 0000000100000e5c movq %rdx,%rcx 0000000100000e5f jmp 0x100000e65 0000000100000e61 addq $0x08,%rcx 0000000100000e65 cmpq $0x00,(%rcx) 0000000100000e69 jne 0x100000e61 0000000100000e6b addq $0x08,%rcx 0000000100000e6f callq _main 0000000100000e74 movl %eax,%edi 0000000100000e76 callq 0x100000f46 ; symbol stub for: _exit 0000000100000e7b hlt 0000000100000e7c nop 0000000100000e7d nop 0000000100000e7e nop 0000000100000e7f nop _main: 0000000100000e80 pushq %rbp 0000000100000e81 movq %rsp,%rbp 0000000100000e84 movl $0x00000003,0xd0(%rbp) 0000000100000e8b movl 0xd0(%rbp),%eax 0000000100000e8e movl %eax,0xe4(%rbp) 0000000100000e91 movl 0xe4(%rbp),%eax 0000000100000e94 andl $0x01,%eax 0000000100000e97 cmpl $0x00,%eax ... 因为声明了static 属性,反汇编代码中没有了符号表说明,因此使得攻击者难以通过反汇编代码弄清楚程序的逻辑。方法3中将讲述另一种从二进制代码中隐藏符号表的方法,特别是在不能将函数声明为static时比较适用。 作者 danqingd | |
![]() | ![]() |