LOFTER for ipad —— 让兴趣,更有趣

点击下载 关闭

LOFTER-网易轻博

objective-c

2656浏览    165参与
碳基体

objective-c runtime安全措施之三:反汇编( -funroll-loops 编译选项

《O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑)

方法4:使用编译器的 -funroll-loops 选项 完全展开循环结构

原理: -funroll-loops编译选项使得程序中的循环步骤完全展开。这样会增加汇编代码的长度,并且攻击者需要隔离出每个循环,进行单独的修改,从而增大了攻击难度。

问题:在linux环境下编译可以完全展开整个循环逻辑,但是在苹果编译器中目前是不能...

《O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑)

方法4:使用编译器的 -funroll-loops 选项 完全展开循环结构

原理: -funroll-loops编译选项使得程序中的循环步骤完全展开。这样会增加汇编代码的长度,并且攻击者需要隔离出每个循环,进行单独的修改,从而增大了攻击难度。

问题:在linux环境下编译可以完全展开整个循环逻辑,但是在苹果编译器中目前是不能完全展开,不知道新版xcode中的gcc是否可以。

int main( ) {

int i = 0;

for(i=0; i<1000; ++i) {

puts("This is my loop of secure session checks");

}

}

例子1:在mac os x上使用 –funroll-loops编译选项

$ gcc -o main main.c -funroll-loops -funroll-all-loops

$ otool -tV main

_main:

0000000100000ef0 pushq %rbp

0000000100000ef1 movq %rsp,%rbp

0000000100000ef4 subq $0x10,%rsp

0000000100000ef8 movl $0x00000000,0xf8(%rbp)

0000000100000eff xorb %al,%al

0000000100000f01 leaq 0x00000054(%rip),%rcx

0000000100000f08 movq %rcx,%rdi

0000000100000f0b callq 0x100000f30 ; symbol stub for: _puts

0000000100000f10 movl 0xf8(%rbp),%eax

0000000100000f13 addl $0x01,%eax

0000000100000f16 movl %eax,0xf8(%rbp)

0000000100000f19 movl 0xf8(%rbp),%eax

0000000100000f1c cmpl $0x09,%eax

0000000100000f1f jle 0x100000eff

0000000100000f21 movl 0xfc(%rbp),%eax

0000000100000f24 addq $0x10,%rsp

0000000100000f28 popq %rbp

0000000100000f29 ret

即使使用了–funroll-loops选项,循环也未

例子2:在linux 上使用 –funroll-loops编译选项

080483f0 <main>:

80483f0: 55 push %ebp

80483f1: 89 e5 mov %esp,%ebp

80483f3: 83 e4 f0 and $0xfffffff0,%esp

80483f6: 53 push %ebx

80483f7: 31 db xor %ebx,%ebx

80483f9: 83 ec 1c sub $0x1c,%esp

80483fc: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi

8048400: 83 c3 08 add $0x8,%ebx

8048403: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

804840a: e8 09 ff ff ff call 8048318 <puts@plt>

804840f: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

8048416: e8 fd fe ff ff call 8048318 <puts@plt>

804841b: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

8048422: e8 f1 fe ff ff call 8048318 <puts@plt>

8048427: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

804842e: e8 e5 fe ff ff call 8048318 <puts@plt>

8048433: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

804843a: e8 d9 fe ff ff call 8048318 <puts@plt>

804843f: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

8048446: e8 cd fe ff ff call 8048318 <puts@plt>

804844b: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

8048452: e8 c1 fe ff ff call 8048318 <puts@plt>

8048457: c7 04 24 40 85 04 08 movl $0x8048540,(%esp)

804845e: e8 b5 fe ff ff call 8048318 <puts@plt>

8048463: 81 fb e8 03 00 00 cmp $0x3e8,%ebx

8048469: 75 95 jne 8048400 <main+0x10>

804846b: 83 c4 1c add $0x1c,%esp

804846e: 5b pop %ebx

804846f: 89 ec mov %ebp,%esp

8048471: 5d pop %ebp

8048472: c3 ret

来源:碳基体

碳基体

objective-c runtime安全措施之三:反汇编(strip)

《O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑)

方法3:使用strip命令除去目标文件中的指定符号

原理:使用strip命令去掉符号表中的指定符号

下面的代码是用来检查是否有debugger的存在

#include <unistd.h>

#include <sys/types.h>

#include <sys/sysctl.h>

#include...

《O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑)

方法3:使用strip命令除去目标文件中的指定符号

原理:使用strip命令去掉符号表中的指定符号

下面的代码是用来检查是否有debugger的存在

#include <unistd.h>

#include <sys/types.h>

#include <sys/sysctl.h>

#include <string.h>

#include <stdio.h>

int check_debugger( )

{

size_t size = sizeof(struct kinfo_proc);

struct kinfo_proc info;

int ret, name[4];

memset(&info, 0, sizeof(struct kinfo_proc));

name[0] = CTL_KERN;

name[1] = KERN_PROC;

name[2] = KERN_PROC_PID;

name[3] = getpid();

if (ret = (sysctl(name, 4, &info, &size, NULL, 0))) {

return ret; /* sysctl() failed for some reason */

}

return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0;

}

int main( ) {

int i = 0, f;

do {

if (check_debugger())

puts("Eek! I'm being debugged!");

else

puts("I'm doing something really secure here!!");

++i;

sleep(5);

} while(i<10);

}

 

例子1:正常使用nm导出符号表信息

当符号表被导出,check_debugger和main函数的符号和地址对攻击者是清晰可见的

$ nm main

00003038 S _NXArgc

0000303c S _NXArgv

00003044 S ___progname

00002dd8 t __dyld_func_lookup

00001000 A __mh_execute_header

00002de4 T _check_debugger

00003040 S _environ

U _exit

U _getpid

00002ef4 T _main

U _memset

U _puts

U _sysctl

00003034 d dyld__mach_header

00002db8 t dyld_stub_binding_helper

00002d6c T start

这些符号在二进制反汇编代码中可以看到

_check_debugger:

00002de4 e92d4090 push {r4, r7, lr}

00002de8 e28d7004 add r7, sp, #4 @ 0x4

00002dec e24ddf8f sub sp, sp, #572 @ 0x23c

00002df0 e3cdd007 bic sp, sp, #7 @ 0x7

00002df4 e59f00e8 ldr r0, [pc, #232] @ 0x2ee4

00002df8 e28d1040 add r1, sp, #64 @ 0x40

...

_main:

00002ef4 e92d4080 push {r7, lr}

00002ef8 e1a0700d mov r7, sp

00002efc e24dd018 sub sp, sp, #24 @ 0x18

00002f00 e59f0070 ldr r0, [pc, #112] @ 0x2f78

00002f04 e5070008 str r0, [r7, #-8]

00002f08 e59f0068 ldr r0, [pc, #104] @ 0x2f78

00002f0c e58d0008 str r0, [sp, #8]

...

例子2:使用strip后导出符号表信息

$ strip main

$ nm main

00001000 A __mh_execute_header

U _exit

U _getpid

U _memset

U _puts

U _sysctl

攻击者不知道函数出现在地址空间的哪里,甚至完全不会知道check_debugger函数的存在。为了找出具体程序逻辑,攻击者不得不查看上千行汇编代码,如下所示:

00002d6c e59d0000 ldr r0, [sp]

00002d70 e28d1004 add r1, sp, #4 @ 0x4

00002d74 e2804001 add r4, r0, #1 @ 0x1

00002d78 e0812104 add r2, r1, r4, lsl #2

00002d7c e3cdd007 bic sp, sp, #7 @ 0x7

00002d80 e1a03002 mov r3, r2

00002d84 e4934004 ldr r4, [r3], #4

00002d88 e3540000 cmp r4, #0 @ 0x0

00002d8c 1afffffc bne 0x2d84

00002d90 e59fc018 ldr ip, [pc, #24] @ 0x2db0

00002d94 e08fc00c add ip, pc, ip

00002d98 e59cc000 ldr ip, [ip]

00002d9c e12fff3c blx ip

00002da0 e59fc00c ldr ip, [pc, #12] @ 0x2db4

00002da4 e08fc00c add ip, pc, ip

00002da8 e59cc000 ldr ip, [ip]

00002dac e12fff1c bx ip

00002db0 00000280 andeq r0, r0, r0, lsl #5

00002db4 00000274 andeq r0, r0, r4, ror r2

00002db8 e52dc004 push {ip} @ (str ip, [sp, #-4]!)

00002dbc e59fc00c ldr ip, [pc, #12] @ 0x2dd0

00002dc0 e79fc00c ldr ip, [pc, ip]

00002dc4 e52dc004 push {ip} @ (str ip, [sp, #-4]!)

00002dc8 e59fc004 ldr ip, [pc, #4] @ 0x2dd4

00002dcc e79ff00c ldr pc, [pc, ip]

00002dd0 0000026c andeq r0, r0, ip, ror #4

00002dd4 0000022c andeq r0, r0, ip, lsr #4

00002dd8 e59fc000 ldr ip, [pc, #0] @ 0x2de0

00002ddc e79ff00c ldr pc, [pc, ip]

00002de0 00000004 andeq r0, r0, r4

00002de4 e92d4090 push {r4, r7, lr}

00002de8 e28d7004 add r7, sp, #4 @ 0x4

00002dec e24ddf8f sub sp, sp, #572 @ 0x23c

00002df0 e3cdd007 bic sp, sp, #7 @ 0x7

00002df4 e59f00e8 ldr r0, [pc, #232] @ 0x2ee4

00002df8 e28d1040 add r1, sp, #64 @ 0x40

00002dfc e28d202c add r2, sp, #44 @ 0x2c

00002e00 e59f30e0 ldr r3, [pc, #224] @ 0x2ee8

00002e04 e59fc0e0 ldr ip, [pc, #224] @ 0x2eec

00002e08 e59fe0e0 ldr lr, [pc, #224] @ 0x2ef0

00002e0c e58de22c str lr, [sp, #556]

00002e10 e58d1028 str r1, [sp, #40]

00002e14 e58d1024 str r1, [sp, #36]

00002e18 e3a01000 mov r1, #0 @ 0x0

00002e1c e58d2020 str r2, [sp, #32]

00002e20 e3a02f7b mov r2, #492 @ 0x1ec

00002e24 e58d001c str r0, [sp, #28]

00002e28 e59d0024 ldr r0, [sp, #36]

00002e2c e58dc018 str ip, [sp, #24]

00002e30 e58d3014 str r3, [sp, #20]

00002e34 eb000057 bl 0x2f98 @ symbol stub for: _memset

00002e38 e59d0024 ldr r0, [sp, #36]

00002e3c e58d0230 str r0, [sp, #560]

00002e40 e59d0014 ldr r0, [sp, #20]

00002e44 e58d002c str r0, [sp, #44]

00002e48 e59d0018 ldr r0, [sp, #24]

00002e4c e58d0030 str r0, [sp, #48]

00002e50 e59d0014 ldr r0, [sp, #20]

00002e54 e58d0034 str r0, [sp, #52]

00002e58 eb00004b bl 0x2f8c @ symbol stub for: _getpid

00002e5c e58d0038 str r0, [sp, #56]

00002e60 e59d0020 ldr r0, [sp, #32]

00002e64 e59d1028 ldr r1, [sp, #40]

00002e68 e3a02000 mov r2, #0 @ 0x0

00002e6c e1a0300d mov r3, sp

00002e70 e5832004 str r2, [r3, #4]

00002e74 e5832000 str r2, [r3]

00002e78 e58d1010 str r1, [sp, #16]

00002e7c e3a01004 mov r1, #4 @ 0x4

00002e80 e28d3f8b add r3, sp, #556 @ 0x22c

00002e84 e59d2010 ldr r2, [sp, #16]

00002e88 eb000048 bl 0x2fb0 @ symbol stub for: _sysctl

00002e8c e58d003c str r0, [sp, #60]

00002e90 e59d003c ldr r0, [sp, #60]

00002e94 e59d101c ldr r1, [sp, #28]

00002e98 e1500001 cmp r0, r1

00002e9c 1a000000 bne 0x2ea4

00002ea0 ea000002 b 0x2eb0

00002ea4 e59d003c ldr r0, [sp, #60]

00002ea8 e58d0234 str r0, [sp, #564]

00002eac ea000006 b 0x2ecc

00002eb0 e5dd0051 ldrb r0, [sp, #81]

00002eb4 e2000008 and r0, r0, #8 @ 0x8

00002eb8 e1a001a0 lsr r0, r0, #3

00002ebc e58d000c str r0, [sp, #12]

00002ec0 e59d100c ldr r1, [sp, #12]

00002ec4 e58d1234 str r1, [sp, #564]

00002ec8 e58d0008 str r0, [sp, #8]

00002ecc e59d0234 ldr r0, [sp, #564]

00002ed0 e58d0238 str r0, [sp, #568]

00002ed4 e59d0238 ldr r0, [sp, #568]

00002ed8 e247d004 sub sp, r7, #4 @ 0x4

00002edc e8bd4090 pop {r4, r7, lr}

00002ee0 e12fff1e bx lr

00002ee4 00000000 andeq r0, r0, r0

00002ee8 00000001 andeq r0, r0, r1

00002eec 0000000e andeq r0, r0, lr

00002ef0 000001ec andeq r0, r0, ip, ror #3

00002ef4 e92d4080 push {r7, lr}

00002ef8 e1a0700d mov r7, sp

00002efc e24dd018 sub sp, sp, #24 @ 0x18

00002f00 e59f0070 ldr r0, [pc, #112] @ 0x2f78

00002f04 e5070008 str r0, [r7, #-8]

00002f08 e59f0068 ldr r0, [pc, #104] @ 0x2f78

00002f0c e58d0008 str r0, [sp, #8]

00002f10 ebffffb3 bl 0x2de4

00002f14 e59d1008 ldr r1, [sp, #8]

00002f18 e1500001 cmp r0, r1

00002f1c 1a000000 bne 0x2f24

00002f20 ea000004 b 0x2f38

00002f24 e59f0054 ldr r0, [pc, #84] @ 0x2f80

00002f28 e08f0000 add r0, pc, r0

00002f2c eb00001c bl 0x2fa4 @ symbol stub for: _puts

00002f30 e58d0004 str r0, [sp, #4]

00002f34 ea000003 b 0x2f48

00002f38 e59f003c ldr r0, [pc, #60] @ 0x2f7c

00002f3c e08f0000 add r0, pc, r0

00002f40 eb000017 bl 0x2fa4 @ symbol stub for: _puts

00002f44 e58d0000 str r0, [sp]

00002f48 e59f0034 ldr r0, [pc, #52] @ 0x2f84

00002f4c e59f1034 ldr r1, [pc, #52] @ 0x2f88

00002f50 e5172008 ldr r2, [r7, #-8]

00002f54 e0821001 add r1, r2, r1

00002f58 e5071008 str r1, [r7, #-8]

00002f5c e5171008 ldr r1, [r7, #-8]

00002f60 e1510000 cmp r1, r0

00002f64 daffffe7 ble 0x2f08

00002f68 e5170004 ldr r0, [r7, #-4]

00002f6c e1a0d007 mov sp, r7

00002f70 e8bd4080 pop {r7, lr}

00002f74 e12fff1e bx lr

00002f78 00000000 andeq r0, r0, r0

00002f7c 00000092 muleq r0, r2, r0

00002f80 0000008c andeq r0, r0, ip, lsl #1

00002f84 00000009 andeq r0, r0, r9

00002f88 00000001 andeq r0, r0, r1

跟踪这些代码是比较困难的。假如check_debugger函数是inline的,将更难弄清楚逻辑(原因见http://danqingdani.blog.163.com/blog/static/18609419520123111202352/ )。

来源:碳基体

碳基体

objective-c runtime安全措施之三:反汇编(-03 编译选项)

《O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑)

方法2:使用编译器的 -03 选项 

原理:-03编译选项可以将具体的计算逻辑隐藏起来,直接输出计算结果

int main(int argc, char **argv)

{

int i;

int a = 0;

for(i=0;i<10;++i) {

a += i;

}

printf("%d\n"...

《O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑)

方法2:使用编译器的 -03 选项 

原理:-03编译选项可以将具体的计算逻辑隐藏起来,直接输出计算结果

int main(int argc, char **argv)

{

int i;

int a = 0;

for(i=0;i<10;++i) {

a += i;

}

printf("%d\n", a);

return 0;

}

例子1:普通编译方式编译的汇编代码

_main:

0000000100000ec0 pushq %rbp

0000000100000ec1 movq %rsp,%rbp

0000000100000ec4 subq $0x20,%rsp

0000000100000ec8 movl %edi,0xfc(%rbp)

0000000100000ecb movq %rsi,0xf0(%rbp)

0000000100000ecf movl $0x00000000,0xe0(%rbp)

0000000100000ed6 movl $0x00000000,0xe4(%rbp)

0000000100000edd jmp 0x100000ef3

0000000100000edf movl 0xe0(%rbp),%eax

0000000100000ee2 movl 0xe4(%rbp),%ecx

0000000100000ee5 addl %ecx,%eax

0000000100000ee7 movl %eax,0xe0(%rbp)

0000000100000eea movl 0xe4(%rbp),%eax

0000000100000eed addl $0x01,%eax        ;加1

0000000100000ef0 movl %eax,0xe4(%rbp)

0000000100000ef3 movl 0xe4(%rbp),%eax

0000000100000ef6 cmpl $0x09,%eax         ;加9

0000000100000ef9 jle 0x100000edf

0000000100000efb movl 0xe0(%rbp),%eax

0000000100000efe xorb %cl,%cl

0000000100000f00 leaq 0x00000055(%rip),%rdx

0000000100000f07 movq %rdx,%rdi

0000000100000f0a movl %eax,%esi

0000000100000f0c movb %cl,%al

0000000100000f0e callq 0x100000f30 ; symbol stub for: _printf

0000000100000f13 movl $0x00000000,0xe8(%rbp)

0000000100000f1a movl 0xe8(%rbp),%eax

0000000100000f1d movl %eax,0xec(%rbp)

0000000100000f20 movl 0xec(%rbp),%eax

0000000100000f23 addq $0x20,%rsp

0000000100000f27 popq %rbp

0000000100000f28 ret

例子2:采用 -03 编译选项编译的汇编代码

  

_main:

0000000100000f10 pushq %rbp

0000000100000f11 movq %rsp,%rbp

0000000100000f14 movl $0x0000002d,%esi  ;最终结果45

0000000100000f19 xorb %al,%al

0000000100000f1b leaq 0x0000003a(%rip),%rdi

0000000100000f22 callq 0x100000f32 ; symbol stub for: _printf

0000000100000f27 xorl %eax,%eax

0000000100000f29 popq %rbp

0000000100000f2a ret

优化后的输出直接计算出结果0x2D(45,1+2+3+4+5+6+7+8+9的和),将其发送给printf函数,没有显示任何循环过程。这样依赖,攻击者也完全看不出计算逻辑了。下面的调试过程,显示了优化后,直接在寄存器中创建和存储了一个常量。

  

$ gdb -q ./testprog

Reading symbols for shared libraries .. done

(gdb) break printf

Function "printf" not defined.

Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (printf) pending.

(gdb) r

Starting program: /Users/jonz/Downloads/a

Reading symbols for shared libraries +........................ done

Breakpoint 1 at 0x7fff8b69922e

Pending breakpoint 1 - "printf" resolved

(gdb) info reg

rax 0x0 0

rbx 0x0 0

rcx 0x0 0

rdx 0x100000f5c 4294971228

rsi 0x2d 45

  

rdi 0x100000f5c 4294971228

...

来源:碳基体

碳基体

objective-c runtime安全措施之三:反汇编(inline编译方式)

《O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记

反汇编:通过优化编译器选项、去除符号表来复杂化编译后生成的汇编代码(使用反汇编工具结合动态调试工具弄清并篡改程序逻辑)


方法1:采用inline函数

原理:设置inline属性、static属性,可以使得编译后生成的目标代码在反汇编成汇编代码时,不容易阅读,弄清其中的逻辑,因为inline会导致汇编代码中是直接将inline函数的函数体拷贝到main中,而不是清晰的call调用;static属性,则会让编译生成的二进制代码中,没有清晰的符号表...

《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时比较适用。

来源:碳基体

碳基体

objective-c runtime安全措施之二:反注入

O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记

反注入:在类函数被调用前做完整性检测(预防应用自定义函数或apple标准库函数被修改或替换)


原理:调用dladdr()函数检查类方法的基本信息是否合法


例子1:检查Foundation框架类中NSMutableURLRequest基类(用于改变URL请求)的setHTTPBody方法的基本信息


#include <dlfcn.h>

#include <objc/objc...

O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记

反注入:在类函数被调用前做完整性检测(预防应用自定义函数或apple标准库函数被修改或替换)

  

原理:调用dladdr()函数检查类方法的基本信息是否合法

  

例子1:检查Foundation框架类中NSMutableURLRequest基类(用于改变URL请求)的setHTTPBody方法的基本信息

  

#include <dlfcn.h>

#include <objc/objc.h>

#include <objc/runtime.h>

#include <stdio.h>

#include <string.h>

int main() {

Dl_info info;

  

IMP imp = class_getMethodImplementation(

objc_getClass("NSMutableURLRequest"),

sel_registerName("setHTTPBody:"));

printf("pointer %p\n", imp);

if (dladdr(imp, &info)) {

printf("dli_fname: %s\n", info.dli_fname);

printf("dli_sname: %s\n", info.dli_sname);

printf("dli_fbase: %p\n", info.dli_fbase);

printf("dli_saddr: %p\n", info.dli_saddr);

} else {

printf("error: can't find that symbol.\n");

}

}

在Mac OS上使用gcc编译

$ gcc -o main main.m -lobjc -framework Foundation

然后运行该程序和观察输出,这些信息(地址空间、文件名、符号名)可以确认该函数来源、是否合法

$ ./main

  

pointer 0x7fff8e7aba62

  

dli_fname: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

  

dli_sname: -[NSMutableURLRequest(NSMutableHTTPURLRequest) setHTTPBody:]

  

dli_fbase: 0x7fff8e633000

  

dli_saddr: 0x7fff8e7aba62

  

例子2:使用dladdr函数检查类中的所有方法的通用代码

  

#include <dlfcn.h>

#include <stdio.h>

#include <objc/objc.h>

#include <objc/runtime.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

static inline BOOL validate_methods(const char *, const char *)

__attribute__((always_inline));

BOOL validate_methods(const char *cls, const char *fname) {

Class aClass = objc_getClass(cls);

Method *methods;

unsigned int nMethods;

Dl_info info;

IMP imp;

char buf[128];

Method m;

if (!aClass)

return NO;

methods = class_copyMethodList(aClass, &nMethods);

while(nMethods--) {

m = methods[nMethods];

printf("validating [ %s %s ]\n",

(const char *) class_getName(aClass),

(const char *) method_getName(m));

imp = method_getImplementation(m);

if (!imp) {

printf("error: method_getImplementation(%s) failed\n",

(const char *) method_getName(m));

free(methods);

return NO;

}

if (! dladdr(imp, &info)) {

printf("error: dladdr() failed for %s\n",

(const char *)method_getName(m));

free(methods);

return NO;

}

/* Validate image path */

  

if (strcmp(info.dli_fname, fname))

goto FAIL;

/* Validate class name in symbol */

  

snprintf(buf, sizeof(buf), "[%s ",

(const char *) class_getName(aClass));

if (strncmp(info.dli_sname+1, buf, strlen(buf)))

{

snprintf(buf, sizeof(buf), "[%s(",

(const char *) class_getName(aClass));

if (strncmp(info.dli_sname+1, buf, strlen(buf)))

goto FAIL;

}

/* Validate selector in symbol */

  

snprintf(buf, sizeof(buf), " %s]",

(const char *) method_getName(m));

if (strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)),

buf, strlen(buf)))

{

goto FAIL;

}

}

return YES;

FAIL:

printf("method %s failed integrity test:\n",

(const char *)method_getName(m));

printf(" dli_fname: %s\n", info.dli_fname);

printf(" dli_sname: %s\n", info.dli_sname);

printf(" dli_fbase: %p\n", info.dli_fbase);

printf(" dli_saddr: %p\n", info.dli_saddr);

free(methods);

return NO;

}

【注意】

  

  1. 重命名检查函数(例如不要用validate_methods这样具有明显意义的名字)

  

  1. 在应用的多处加入检查函数,任何与敏感数据交互的方法在执行前都需要检查

  

  1. 例子中的logging和printf语句只是用来调试,代码正式发布前要移除

  

  1. 验证完整性只是提高了攻击的门槛,不能完全防御黑客,需要综合应用多种技巧,例如思路一中的反调试技巧,及马上要介绍的反汇编技巧。


来源:碳基体

碳基体

objective-c runtime安全措施之一:反调试

<<O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记


反调试:检查是否有debugger在trace游戏进程 (预防动态调试进程)

方法1: 检查进程的状态是否为 P_TRACED 

原理:当进程被调试的时候,内核自动设置进程选项为P_TRACED。因此可以通过检查该标记来确认是否被调试

  

例子1:check_debugger()

  

#include <unistd.h>

#include <sys...

<<O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记


反调试:检查是否有debugger在trace游戏进程 (预防动态调试进程)

方法1: 检查进程的状态是否为 P_TRACED 

原理:当进程被调试的时候,内核自动设置进程选项为P_TRACED。因此可以通过检查该标记来确认是否被调试

  

例子1:check_debugger()

  

#include <unistd.h>

#include <sys/types.h>

#include <sys/sysctl.h>

#include <string.h>

static int check_debugger( ) __attribute__((always_inline));

  

int check_debugger( )

{

size_t size = sizeof(struct kinfo_proc);

struct kinfo_proc info;

int ret, name[4];

memset(&info, 0, sizeof(struct kinfo_proc));

name[0] = CTL_KERN;

name[1] = KERN_PROC;

name[2] = KERN_PROC_PID;

name[3] = getpid();

if (ret = (sysctl(name, 4, &info, &size, NULL, 0))) {

return ret; /* sysctl() failed for some reason */

}

return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0;

  

}

【注意】

1. 为了让程序以inline方式编译,需要设置编译器的优化选项,-0z(函数以inline方式编译),-fast(加快编译速度)

  

2. 为了避免sysctl被替换,需要在调用该函数前验证其完整性(在下面会介绍如何验证sysctl函数完整性)

  

--------------------------------------------------------------------------------------------------------------------------------------------------

方法2:调用ptrace请求来检查进程是否被调试

原理,调用ptrace请求的PT_DENY_ATTACH方法。使用该方法后,下面两种情况都会导致调试器崩溃,应用退出。

(1)如果应用使用调试器来运行,调试器会崩溃;(下面以调试iTunes为例)

$ gdb -q /Applications/iTunes.app/Contents/MacOS/iTunes

Reading symbols for shared libraries .................................... done

(gdb) r

Starting program: /Applications/iTunes.app/Contents/MacOS/iTunes

Reading symbols for shared libraries ++++++++++++++++++++++++++++++....++++

+....................................................................................

............................................................... done

Program exited with code 055.

(gdb)

(2)如果应用运行中,使用调试器,调试器也会崩溃。

$ gdb -q -p 3933

Attaching to process 3933.

Segmentation fault: 11

 

Mac OS X系统中

  

#include <sys/ptrace.h>

int main( ) {

ptrace(PT_DENY_ATTACH, 0, 0, 0);

  

...

}

iOS系统中

  

int main( ) {

ptrace(31, 0, 0, 0); //因为PT_DENY_ATTACH只存在于iOS模拟器头文件中,所以用31来代替

  

...

}

【注意】

1. ptrace方法可以通过打断点的方法绕过,如下所示:

  

# gdb ?1 ./main

  

Reading symbols for shared libraries . done

(gdb) b ptrace

  

Function "ptrace" not defined.

Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (ptrace) pending.

(gdb) commands

  

Type commands for when breakpoint 1 is hit, one per line.

End with a line saying just "end".

>return

  

>continue

  

>end

  

(gdb) run

  

Starting program: /private/var/root/main

Reading symbols for shared libraries .............................. done

Breakpoint 1 at 0x342afa98

Pending breakpoint 1 - "ptrace" resolved

Breakpoint 1, 0x342afa98 in ptrace ()

I'm doing something really secure here!!

2.由于可能被攻击者绕过该方法的调用,在应用的多处增加ptrace函数会提高应用的安全性


来源:碳基体

Squirrel

关于获取iOS设备唯一标识符笔记

在iOS的开发过程中,我们常常会遇到获取设备的UUID,IDFV,IDFA,MAC地址等一系列的设备标识符。

什么是设备的UUID呢,更详细的介绍可以参考www.cocoachina.com/industry/20130422/6040.html

话不多说,直接上代码:

1、获取广告标示符(IDFA-identifierForIdentifier)

导入    #import<AdSupport/ASIdentifierManager.h>

+(NSString *)dy_getDeviceIDFA{

ASIdentifierManager...

在iOS的开发过程中,我们常常会遇到获取设备的UUID,IDFV,IDFA,MAC地址等一系列的设备标识符。

什么是设备的UUID呢,更详细的介绍可以参考www.cocoachina.com/industry/20130422/6040.html

话不多说,直接上代码:

1、获取广告标示符(IDFA-identifierForIdentifier)

导入    #import<AdSupport/ASIdentifierManager.h>

+(NSString *)dy_getDeviceIDFA{

ASIdentifierManager *asIM = [[ASIdentifierManager alloc] init];

NSString *idfaStr = [asIM.advertisingIdentifier UUIDString];

return idfaStr;

}

2、获取Vindor标示符 (IDFV-identifierForVendor)

+(NSString *)dy_getDeviceIDFV{

NSString* idfvStr      = [[UIDevice currentDevice] identifierForVendor].UUIDString;

return idfvStr;

}

Git上的erica的UIDevice扩展文件,以前可用但由于IOKit framework没有公开,所以也无法使用。就算手动导入,依旧无法使用,看来获取IMEI要失败了,同时失败的还有IMSI。不过还存在另外一种可能,Stack Overflow上有人提供采用com.apple.coretelephony.Identity.get entitlement方法,but device must be jailbroken;在此附上链接,供大家参考:http://stackoverflow.com/questions/16667988/how-to-get-imei-on-iphone-5/16677043#16677043

3、获取MAC地址

#include <sys/sysctl.h>

#include <sys/socket.h>

#include <net/if.h>

#include <net/if_dl.h>

+(NSString*)dy_getDeviceMAC{

int mib[6];

size_t len;

char *buf;

unsigned char *ptr;

struct if_msghdr *ifm;

struct sockaddr_dl *sdl;

mib[0] = CTL_NET;

mib[1] = AF_ROUTE;

mib[2] = 0;

mib[3] = AF_LINK;

mib[4] = NET_RT_IFLIST;

if ((mib[5] = if_nametoindex("en0")) == 0) {

printf("Error: if_nametoindex error\n");

return NULL;

}

if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {

printf("Error: sysctl, take 1\n");

return NULL;

}

if ((buf = malloc(len)) == NULL) {

printf("Could not allocate memory. error!\n");

return NULL;

}

if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {

printf("Error: sysctl, take 2");

free(buf);

return NULL;

}

ifm = (struct if_msghdr *)buf;

sdl = (struct sockaddr_dl *)(ifm + 1);

ptr = (unsigned char *)LLADDR(sdl);

NSString *macStr = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",*ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];

free(buf);

return macStr;

}

4、获取UUID

+(NSString*)dy_getDeviceUUID{

CFUUIDRef uuid = CFUUIDCreate(NULL);

assert(uuid != NULL);

CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid);

return (__bridge NSString *)(uuidStr);

}

UDID是肯定要被苹果拒绝的,Stack Overflow提供了另外一种方法,越狱可尝试:http://stackoverflow.com/questions/27602368/how-to-get-serial-number-of-a-device-using-iokit-in-ios8-as-ioplatformserialnumb/27686125#27686125

 @ 

Squirrel

解析iOS崩溃日志(crash Log)

文档转自: http://blog.csdn.net/xyxjn/article/details/43310061

有关友盟的崩溃日志解析:

    首先看一些这些线上app crash 信息:  


SIGSEGV和SIGBUS一般是因为访问已经被释放的内存或者调用不存在的方法导致的,余下两个就是数组越界的问题了 这些你都知道的,然后来看看具体的log信息:  



看了这些Log,估计也看不出来什么,都是一些内存地址,帧调用栈等,那么久需要我们进一步解析:

看一下上边的cresh日志,找...

文档转自: http://blog.csdn.net/xyxjn/article/details/43310061

有关友盟的崩溃日志解析:

    首先看一些这些线上app crash 信息:  


SIGSEGV和SIGBUS一般是因为访问已经被释放的内存或者调用不存在的方法导致的,余下两个就是数组越界的问题了 这些你都知道的,然后来看看具体的log信息:  



看了这些Log,估计也看不出来什么,都是一些内存地址,帧调用栈等,那么久需要我们进一步解析:

看一下上边的cresh日志,找到一句:

    5   appname                            0x97525 appname + 615717

它指出了应用名称,崩溃时的调用方法的地址,文件的地址以及方法所在的行的位置(具体请看这篇文章),接下来就要符号化(Symbolication)这句,用dwarfdump来检测crash log中dSYM UUID和本地的dSYM文件是否匹配 

打开终端:

cd /Users/username/Library/Developer/Xcode/Archives/2013-08-30/app 8-30-13 6.19 PM.xcarchive/dSYMs  

其中dSYMs  是appname.app.dSYM所在的文件夹,找到/Users/username/Library/Developer/Xcode/Archives/2013-08-30/app 8-30-13 6.19 PM.xcarchive之后  右键显示包内容可见

回车之后再输入命令

dwarfdump --uuid appname.app.dSYM

好了 如果crash日志中的dYSM UUID和本地的dYSM文件是相配的,好接下来就查一下0x97525这个地址是什么

输入命令 

dwarfdump --arch=armv7 --lookup 0x97525  /Users/username/Library/Developer/Xcode/Archives/2013-08-30/appname\ 8-30-13\ 6.19\ PM.xcarchive/dSYMs/appname.app.dSYM/Contents/Resources/DWARF/appname  

得到结果


看一下结果:发现有AT_name、Line table dir :、Line table file:,aha!找到了出错的地方(出错的这个文件是网上别人写的,有bug,现已不再使用)。

注意:如果发现warning: unsupported file type:错误,这个问题是因为有文件或者目录的名称中包含空格,比如:2013-08-30/appname 8-30-13 6.19 ,所以,需要转义一下:2013-08-30/appname\ 8-30-13\ 6.19\ PM.xcarchive

Squirrel

全局断点由于音频导致的那些莫名崩溃原因

这几天经常因为打全局断点,导致app莫名的崩溃,怎么也找不到原因,总是断到音频的那句代码上,找了各方面资料,才知道原因,特别记录下。

如果你里面用了C++或者其他写的,可能会这样,all exceptions就表明,所有exceptions都会被catch,即使这个exception在代码里已经处理了的,也会被catch,高亮.如果是因为C++导致的,你可以右键点击all exceptions编辑将exception->all改为objective-c.如果不是这个原因,那你也不用管. 

所以,打全局断点的时候,需要把你的all  改成objective-c就好了...

这几天经常因为打全局断点,导致app莫名的崩溃,怎么也找不到原因,总是断到音频的那句代码上,找了各方面资料,才知道原因,特别记录下。

如果你里面用了C++或者其他写的,可能会这样,all exceptions就表明,所有exceptions都会被catch,即使这个exception在代码里已经处理了的,也会被catch,高亮.如果是因为C++导致的,你可以右键点击all exceptions编辑将exception->all改为objective-c.如果不是这个原因,那你也不用管. 

所以,打全局断点的时候,需要把你的all  改成objective-c就好了。如下图


Squirrel

关于pch路径不对的解决方案

现在大多数的iOS开发都已经不再使用pch了吧,苹果好像也是从xcode6就不提倡使用pch,但是还是有人会用pch,创建了工程和pch之后因为pch的路径要求是绝对路径,所以当同事上传svn之后,你update下来的时候有可能会出现路径不对的提示,怎么样才可以避免同类问题的出现呢?正好我今天遇到了这个情况,记录下来给大家分享下:

    因为pch要求是绝对路径,所以你可以在Biuld Settings里面找到pch,把路径替换成$(SRCROOT)/YourPchName.pch,

    这样当上传svn...

现在大多数的iOS开发都已经不再使用pch了吧,苹果好像也是从xcode6就不提倡使用pch,但是还是有人会用pch,创建了工程和pch之后因为pch的路径要求是绝对路径,所以当同事上传svn之后,你update下来的时候有可能会出现路径不对的提示,怎么样才可以避免同类问题的出现呢?正好我今天遇到了这个情况,记录下来给大家分享下:

    因为pch要求是绝对路径,所以你可以在Biuld Settings里面找到pch,把路径替换成$(SRCROOT)/YourPchName.pch,

    这样当上传svn之后无论谁再更新的时候,就不会出现路径不对的问题了。

Squirrel

Xcode的代码块提高编码效率

    相信很多做程序的都知道,相同的代码,会在多个地方出现,那么反复的编写这段代码也是很繁琐的,粘贴复制也需要花费太多的时间,那么Xcode也恰到好处的提供了这样一个功能,那就是在Xcode中定义一些常用的code snippets。

    通过code snippets,我们可以创建一些可重用的代码块,并且在任何需要的地方很容易的就可以使用这些代码块。这可以节省输入需要的操作和时间。并且,一旦你学会使用code snippets,会发现你可以创建并扩充自己的code snippet library。...

    相信很多做程序的都知道,相同的代码,会在多个地方出现,那么反复的编写这段代码也是很繁琐的,粘贴复制也需要花费太多的时间,那么Xcode也恰到好处的提供了这样一个功能,那就是在Xcode中定义一些常用的code snippets。

    通过code snippets,我们可以创建一些可重用的代码块,并且在任何需要的地方很容易的就可以使用这些代码块。这可以节省输入需要的操作和时间。并且,一旦你学会使用code snippets,会发现你可以创建并扩充自己的code snippet library。

  1. 如图,将需要添加到code snippets的代码拖拽到右边的属性栏。

  2. 拖拽之后,找到My Code Snippet,点击进入编辑状态,

  3. 第一个是Title,就是会显示在右侧的Code snippets面板中的标题(名字)了;第二个是Summary,这个你应该可以理解吧,写下你所需要表达的概述吧;第三个平台,根可以下拉选择(All/IOS/OS X);第四个是语言(Langauge);第五个就是设置快捷键了,比如:你输入@ps,保存后就可以通过直接输入该快捷键进行编码了,很方便的;第六个是该Snippets的生效区域,你也可以进行选择。 


    如此就可以在你需要的地方快速的编辑重复的代码。

  4. 补充一点,要是你想在你生命的对象那里弄一个可编辑的类似于xcode如下截图的那样的提示,只需输入<#(UITableView *)#>

xp

UITextField UI

我们有时需要自定义UITextField对象的风格,例如改变文本字段的显示行为、改变文本字段每个部件的边界范围,修改placeHolder颜色,字体等等,只要我们重写以下方法就能轻易实现,闲话不多说直接上代码:

– textRectForBounds:      //重写来重置文字区域

– drawTextInRect:         //改变绘文字属性.重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了.

– placeholderRectForBounds...

我们有时需要自定义UITextField对象的风格,例如改变文本字段的显示行为、改变文本字段每个部件的边界范围,修改placeHolder颜色,字体等等,只要我们重写以下方法就能轻易实现,闲话不多说直接上代码:

– textRectForBounds:      //重写来重置文字区域

– drawTextInRect:         //改变绘文字属性.重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了.

– placeholderRectForBounds:  //重写来重置占位符区域

– drawPlaceholderInRect:  //重写改变绘制占位符属性.重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了

– borderRectForBounds:  //重写来重置边缘区域

– editingRectForBounds:  //重写来重置编辑区域

– clearButtonRectForBounds:  //重写来重置clearButton位置,改变size可能导致button的图片失真

– leftViewRectForBounds:

– rightViewRectForBounds:


首先定义一个类CustomTextField让它继承UITextField实现以下方法即可:

//控制清除按钮的位置

-(CGRect)clearButtonRectForBounds:(CGRect)bounds

{

   return CGRectMake(bounds.origin.x + bounds.size.width - 50, bounds.origin.y + bounds.size.height -20, 16, 16);

}


//控制placeHolder的位置,左右缩20

-(CGRect)placeholderRectForBounds:(CGRect)bounds

{

    //return CGRectInset(bounds, 20, 0);

   CGRect inset = CGRectMake(bounds.origin.x+100, bounds.origin.y, bounds.size.width -10, bounds.size.height);//更好理解些

   return inset;

}


//控制显示文本的位置

-(CGRect)textRectForBounds:(CGRect)bounds

{

    //return CGRectInset(bounds, 50, 0);

    CGRect inset = CGRectMake(bounds.origin.x+190, bounds.origin.y, bounds.size.width -10, bounds.size.height);//更好理解些

    return inset;

}

//控制编辑文本的位置

-(CGRect)editingRectForBounds:(CGRect)bounds

{

    //return CGRectInset( bounds, 10 , 0 );

   CGRect inset = CGRectMake(bounds.origin.x +10, bounds.origin.y, bounds.size.width -10, bounds.size.height);

   return inset;

}

//控制左视图位置

- (CGRect)leftViewRectForBounds:(CGRect)bounds

{

   CGRect inset = CGRectMake(bounds.origin.x +10, bounds.origin.y, bounds.size.width-250, bounds.size.height);

   return inset;

    //return CGRectInset(bounds,50,0);

}


//控制placeHolder的颜色、字体

- (void)drawPlaceholderInRect:(CGRect)rect

{

    //CGContextRef context = UIGraphicsGetCurrentContext();

    //CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);

    [[UIColor orangeColor] setFill];

    [[self placeholder] drawInRect:rectwithFont:[UIFont systemFontOfSize:20]];

}


//下面是使用CustomTextField的代码,可放在viewDidLoad等方法中

    _textField = [[CustomTextField alloc]initWithFrame:CGRectMake(20,150,280, 30)];

    _textField.placeholder =@"请输入帐号信息";

    _textField.borderStyle =UITextBorderStyleRoundedRect;

    _textField.textAlignment =UITextAlignmentLeft;

    _textField.delegate =self;

    _textField.clearButtonMode =UITextFieldViewModeWhileEditing;

    _textField.text =@"aa";

    UIImageView *imgv = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon-iwant-2.png"]];

   _textField.leftView = imgv;

    _textField.leftViewMode =UITextFieldViewModeAlways;

    [self.view addSubview:_textField];


Kien Lhi

27个应用的全家福

从去年的8月份到现在,“每晚一小时行动”成果。

除了去年暑假做“位置记录专家”花了很多时间外,其他App都是利用晚上加班完成的。一开始没有想到会开发这么多,现在回头看看,做的事情真不少。

学习OC语言一年了,给这些小东西合个影!
学习Swift语言一个月了,用Swift写了两个应用,感觉还没有入门,慢慢来吧。

27个应用的全家福

从去年的8月份到现在,“每晚一小时行动”成果。

除了去年暑假做“位置记录专家”花了很多时间外,其他App都是利用晚上加班完成的。一开始没有想到会开发这么多,现在回头看看,做的事情真不少。

学习OC语言一年了,给这些小东西合个影!
学习Swift语言一个月了,用Swift写了两个应用,感觉还没有入门,慢慢来吧。

ryugaku

简单的关于NSRange

NSRange

是Cocoa中的一个结构体(struct),用来表示相关事物的范围。

NSRange.h中给出的结构如下

typedef struct _NSRange {

    NSUInteger location; //存放该范围的起始地址

    NSUInteger length; //该范围内所含元素的个数

} NSRange;

——————————————————————————————————

创建新的NSRange有三种方法:

三种方法都跟C语言里创建结构体有关

1.NSRange...

NSRange

是Cocoa中的一个结构体(struct),用来表示相关事物的范围。

NSRange.h中给出的结构如下

typedef struct _NSRange {

    NSUInteger location; //存放该范围的起始地址

    NSUInteger length; //该范围内所含元素的个数

} NSRange;

——————————————————————————————————

创建新的NSRange有三种方法:

三种方法都跟C语言里创建结构体有关

1.NSRange range;

  range.location = 数字1;

  range.length = 数字2;

2. NSRange range = {数字1, 数字2};

3.使用Cocoa中提供的函数NSMakeRange();

NSRange range = NSMakeRange(数字1, 数字2);

——————————————————————————————————

NSMakeRange函数在库文件中的实现为(本质上与1,2没区别):

NSRange NSMakeRange(NSUInteger loc, NSUInteger len) {

    NSRange r;

    r.location = loc;

    r.length = len;

    return r;

}

Squirrel

ios开发之静态库.a的制作教程


简介

什么是库?

    库是程序代码的集合,是共享程序代码的一种方式。

    根据原代码的公开情况,库可以分为两种类型。

开源库

    公开源代码,能够看到具体实现

    比如:SDWebImage、AFNetworking

闭源库

    不公开源代码,是经过编译之后的二进制文件,看不到具体实现。

    主要分为:静态库、动态库...


简介

什么是库?

    库是程序代码的集合,是共享程序代码的一种方式。

    根据原代码的公开情况,库可以分为两种类型。

开源库

    公开源代码,能够看到具体实现

    比如:SDWebImage、AFNetworking

闭源库

    不公开源代码,是经过编译之后的二进制文件,看不到具体实现。

    主要分为:静态库、动态库

静态库和动态库

    静态库和动态库的存在形式

    静态库:.a 和.framework

    动态库:.dylib 和.framework

    静态库和动态库使用上的区别:

    静态库:链接时,静态库会被完整的复制到可执行文件中,被多次使用就有多分冗余拷贝。(如上图)

     动态库:链接时不复制,程序运行时由系统加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。(如下图)


        需要注意的是:

        项目中要是使用了自制的动态库,不能被上传到Appstore

制作.a

        新建项目-》选择“Cocoa Touch Static Library”

    
        添加库需要包含的源代码


将李明杰老师的刷新框架MJRefresh拖入工程


选择需要暴露出来的.h文件,.m文件会自动编译到.a文件中


选择真机设备,然后command + B 编译,libMJRefresh.a文件从红色变为黑色


选择模拟器,依然command + B,模拟器和真机环境和下用的.a文件是分开的



右键“Show In Finder”,查看制作好的.a文件



  • Debug-iphoneos 文件夹里面的东西是用在真机上的

  • Debug-iphonesimulator 文件夹里面的东西是用在模拟器上的

  • 如果 Scheme 是 Release 模式,生成的文件夹就以 Release 开头

如果想让一个 .a 文件能同时用在真机和模拟器上,需要进行合并

在终端输入指令

lipo -create Debug-iphoneos/libMJRefresh.a Debug-iphonesimulator/libMJRefresh.a -output libMJRefresh.a

.a 文件的体积(一般情况下)

  • 真机用的 .a > 模拟器用的 .a

  • 所合成 .a == 真机用的 .a + 模拟器用的 .a

通过 lipo –info libMJRefresh.a 可以查看 .a 的类型(模拟器还是真机)

使用.a

    如何使用.a

    直接将.a .h资源文件拖拽到其他项目中即可


本文摘自http://www.cocoachina.com/ios/20150226/11182.html

Squirrel

NSLog占位符

oc里面要输出的格式化占位符都有:

%@         对象

%d,%i     整数

%u         无符整型

%f        浮点/双字

%x  %X         ...

oc里面要输出的格式化占位符都有:

%@         对象

%d,%i     整数

%u         无符整型

%f        浮点/双字

%x  %X         二进制整数

%o        八进制整数

%zu        size_t

%p        指针

%e        浮点/双字(科学计算)

%g        浮点/双字

%s C     字符串

%.*s        Pascal字符串

%c        字符

%C        unichar

%lld        64位长整数(long long)

%llu        无符64位长整数

%Lf        64位双字



NSLog定义在NSObjCRuntime.h中,如下所示:

void NSLog(NSString *format, …);


NSLog (@”this is a test”);

NSLog (@”string is :%@”, string);

NSLog (@”x=%d, y=%d”, 10, 20);


但是下面的写法是不行的:

int i = 12345;

NSLog( @”%@”, i );


原因是, %@需要显示对象,而int i明显不是一个对象,要想正确显示,要写成:

int i = 12345;

NSLog( @”%d”, i );



Andr0i0S
斯坦福大学的老教授讲得很不错,...

斯坦福大学的老教授讲得很不错,可以去看看................

斯坦福大学的老教授讲得很不错,可以去看看................

siwall

autoreleasepool自动释放池

@autoreleasepool会将接受该消息的对象放到一个预先建立的自动释放池 (auto release pool) 中,并在 自动释放池收到 drain 消息时将这些对象的引用计数减一,然后将它们从池子中移除 (这一过程形象地称为“抽干池子”)。

@autoreleasepool会将接受该消息的对象放到一个预先建立的自动释放池 (auto release pool) 中,并在 自动释放池收到 drain 消息时将这些对象的引用计数减一,然后将它们从池子中移除 (这一过程形象地称为“抽干池子”)。

LOFTER

让兴趣,更有趣

简单随性的记录
丰富多彩的内容
让生活更加充实

下载移动端
关注最新消息