1.问题描述
Native Crash 如下:
Build fingerprint: 'XXX/ddddn:7.0/dddd/7.3.27:user/release-keys' Revision: '0' ABI: 'arm' pid: 23898, tid: 23898, name: dex2oat >>> /system/bin/dex2oat <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'FORTIFY: read: prevented write past end of buffer' backtrace: #00 pc 00049b38 /system/lib/libc.so (tgkill+12) #01 pc 000472b3 /system/lib/libc.so (pthread_kill+34) #02 pc 0001d555 /system/lib/libc.so (raise+10) #03 pc 000190a1 /system/lib/libc.so (__libc_android_abort+34) #04 pc 00017104 /system/lib/libc.so (abort+4) #05 pc 0001b54f /system/lib/libc.so (__libc_fatal+22) #06 pc 0001b52f /system/lib/libc.so (__fortify_chk_fail+26) #07 pc 0004f69d /system/lib/libc.so (__read_chk+36) #08 pc 00035987 <anonymous:eba9b000>
map如下:
可以看到 eba9b000 - ebaf8fff 段是 r-x 属性,即可执行代码段;eba96000-eba96fff — 0 1000 [anon:thread signal stack guard page] eba97000-eba98fff rw- 0 2000 [anon:thread signal stack] eba99000-eba99fff rw- 0 1000 [anon:linker_alloc_small_objects] eba9a000-eba9afff r-- 0 1000 [anon:atexit handlers] eba9b000-ebaf8fff r-x 0 5e000 ebaf9000-ebafdfff rw- 0 5000 ebafe000-ebb1dfff r-- 0 20000 /dev/_properties_/properties_serial ebb1e000-ebb1efff rw- 0 1000 [anon:linker_alloc_vector]
在 main log中有如下log:
系统调用的 dex2oat是不会有这种参数的,看起来是第三方 APP 自行调用的 dex2oat,看下当时的进程信息:03-28 05:10:51.629 23898 23898 I dex2oat : /system/bin/dex2oat --dex-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes.dex --dex-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes2.dex --dex-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes3.dex --oat-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes.oat
可以看出来,dex2oat(23898)进程确实是由 23710 进程 fork出来的,那么 dex2oat的参数也应该是其组织的;u0_a297 23710 584 1969400 186196 SyS_epoll_ 00ea9041f8 S com.happyelements.AndroidAnimal.qq u0_a297 23898 23710 23324 4688 do_signal_ 00eb3ddb38 T /system/bin/dex2oat
抓到 core file后,进行分析:
直观的看 backtrace, frame8中的可执行代码,调用了 __read_chk函数,出发了abort,导致进程终止。
据此,我们分析参数 count 和 buf_size 在调用过程的传递。extern "C" ssize_t __read_chk(int fd, void* buf, size_t count, size_t buf_size) { if (__predict_false(count > buf_size)) { //此处 count > buf_size 会出发下一行调用 __fortify_chk_fail("read: prevented write past end of buffer", 0); } if (__predict_false(count > SSIZE_MAX)) { __fortify_chk_fail("read: count > SSIZE_MAX", 0); } return read(fd, buf, count); }
2.问题分析
(gdb) bt #0 tgkill () at bionic/libc/arch-arm/syscalls/tgkill.S:10 #1 0xeb3db2b6 in pthread_kill (t=<optimized out>, sig=6) at bionic/libc/bionic/pthread_kill.cpp:45 #2 0xeb3b1558 in raise (sig=23898) at bionic/libc/bionic/raise.cpp:34 #3 0xeb3ad0a4 in __libc_android_abort () at bionic/libc/bionic/abort.cpp:47 #4 0xeb3ab108 in abort () at bionic/libc/arch-arm/bionic/abort_arm.S:43 #5 0xeb3af552 in __libc_fatal (format=0x0) at bionic/libc/bionic/libc_logging.cpp:678 #6 0xeb3af532 in __fortify_chk_fail (msg=0xeb40b9ec "read: prevented write past end of buffer", tag=0) at bionic/libc/bionic/libc_logging.cpp:645 #7 0xeb3e36a0 in _read_chk (fd=<optimized out>, buf=<optimized out>, count=6, buf_size=114) at bionic/libc/bionic/_read_chk.cpp:35 #8 0xebad0988 in ?? ()
map对应的段:
eba9b000-ebaf8fff r-x 0 5e000 ebaf9000-ebafdfff rw- 0 5000
2.1 调用流程推倒
从backtrace看到 frame8是个匿名可执行段,可能是某个第三方设计出来的一些技术;
但总归要有函数要跳转到这个可执行段,才能执行到这段代码,我们从 coredump中分析,进程中是怎么跳转到 frame8的;
这个推到的关键点就在于 LR寄存器和SP寄存器;
LR保存着上一层跳转到当前函数的指令的下一条指令;SP保存着当前堆栈的栈顶位置,而当前堆栈的栈底就是上一层调用的堆栈的栈顶;
#00
#00 tgkill()
(gdb) disassemble
Dump of assembler code for function tgkill:
0xeb3ddb2c <+0>: mov r12, r7
0xeb3ddb30 <+4>: ldr r7, [pc, #20] ; 0xeb3ddb4c
0xeb3ddb34 <+8>: svc 0x00000000
=> 0xeb3ddb38 <+12>: mov r7, r12
0xeb3ddb3c <+16>: cmn r0, #4096 ; 0x1000
0xeb3ddb40 <+20>: bxls lr
0xeb3ddb44 <+24>: rsb r0, r0, #0
0xeb3ddb48 <+28>: b 0xeb404174
(gdb) info reg
r0 0x0 0
r1 0x5d5a 23898
r2 0x6 6
r3 0x8 8
r4 0xebb9658c 3954795916
r5 0x6 6
r6 0xebb96534 3954795828
r7 0x10c 268
r8 0x1 1
r9 0x61b738 6403896
r10 0xeb3e3679 3946722937
r11 0xffffffff 4294967295
r12 0x0 0
sp 0xffbe71e8 0xffbe71e8
lr 0xeb3db2b7 -348278089
pc 0xeb3ddb38 0xeb3ddb38 <tgkill+12>
cpsr 0x200e0010 537788432
(gdb) info frame
Stack level 0, frame at 0xffbe71e8:
pc = 0xeb3ddb38 in tgkill (bionic/libc/arch-arm/syscalls/tgkill.S:10); saved pc = 0xeb3db2b6
called by frame at 0xffbe7200
source language asm.
Arglist at 0xffbe71e8, args:
Locals at 0xffbe71e8, Previous frame's sp is 0xffbe71e8
(gdb) p $sp
$11 = (void *) 0xffbe71e8
所以:
#00 tgkill()
pc:0xeb3ddb38
sp:0xffbe71e8
lr:0xeb3db2b7
stack: none (frame0 中没有push操作和增长堆栈的操作)
#01 pthread_kill()
pc:0xeb3db2b7 //是#00对应的 lr
sp:0xffbe71e8 //是#00的栈底,由于#00没有栈,所以还是 0xffbe71e8
(gdb) disassemble
Dump of assembler code for function pthread_kill(pthread_t, int):
0xeb3db290 <+0>: push {r4, r5, r6, r7, lr}
0xeb3db292 <+2>: sub sp, #4
0xeb3db294 <+4>: mov r5, r1
0xeb3db296 <+6>: mov r6, r0
0xeb3db298 <+8>: blx 0xeb3a85c8 <__errno@plt>
0xeb3db29c <+12>: mov r4, r0
0xeb3db29e <+14>: mov r0, r6
0xeb3db2a0 <+16>: ldr r7, [r4, #0]
0xeb3db2a2 <+18>: bl 0xeb3dafdc <__pthread_internal_find(long)>
0xeb3db2a6 <+22>: mov r6, r0
0xeb3db2a8 <+24>: cbz r6, 0xeb3db2c2 <pthread_kill(pthread_t, int)+50>
0xeb3db2aa <+26>: blx 0xeb3a8b74 <getpid@plt>
0xeb3db2ae <+30>: ldr r1, [r6, #8]
0xeb3db2b0 <+32>: mov r2, r5
0xeb3db2b2 <+34>: blx 0xeb3a9fc0 <tgkill@plt> // 这里跳转的位置不是 #00 的代码开始位置,而是 plt
=> 0xeb3db2b6 <+38>: cmp.w r0, #4294967295 ; 0xffffffff
0xeb3db2ba <+42>: ite eq
0xeb3db2bc <+44>: ldreq r0, [r4, #0]
0xeb3db2be <+46>: movne r0, #0
0xeb3db2c0 <+48>: b.n 0xeb3db2c4 <pthread_kill(pthread_t, int)+52>
0xeb3db2c2 <+50>: movs r0, #3
0xeb3db2c4 <+52>: str r7, [r4, #0]
0xeb3db2c6 <+54>: add sp, #4
0xeb3db2c8 <+56>: pop {r4, r5, r6, r7, pc}
End of assembler dump.
stack存放 r4-r7,lr,以及另外4字节
stack:
addr value reg
0xffbe71e8 0x00001204
0xffbe71ec 0x00000006 r4
0xffbe71f0 0x00000000 r5
0xffbe71f4 0x00000008 r6
0xffbe71f8 0xeb3e3679 r7
0xffbe71fc 0xeb3b1559 lr
#02:
#02 raise()
pc:0xeb3b1559 //同样的,是 #01的lr
sp:0xffbe71fc + 4 = 0xffbe7200 // 是#01的栈底,这里+4是因为,栈是从高地址向低地址扩展的
(gdb) disassemble 0xeb3b1559
Dump of assembler code for function raise(int):
0xeb3b154a <+0>: push {r4, lr}
0xeb3b154c <+2>: mov r4, r0
0xeb3b154e <+4>: blx 0xeb3a92d0 <pthread_self@plt>
0xeb3b1552 <+8>: mov r1, r4
0xeb3b1554 <+10>: blx 0xeb3a9204 <pthread_kill@plt>
=> 0xeb3b1558 <+14>: mov r4, r0
0xeb3b155a <+16>: cbz r4, 0xeb3b1568 <raise(int)+30>
0xeb3b155c <+18>: blx 0xeb3a85c8 <__errno@plt>
0xeb3b1560 <+22>: str r4, [r0, #0]
0xeb3b1562 <+24>: mov.w r0, #4294967295 ; 0xffffffff
0xeb3b1566 <+28>: pop {r4, pc}
0xeb3b1568 <+30>: movs r0, #0
0xeb3b156a <+32>: pop {r4, pc}
End of assembler dump.
stack:
addr value reg
0xffbe7200 0xffbe721c r4
0xffbe7204 0xeb3ad0a5 lr
#03:
#03 __libc_android_abort()
pc:0xeb3ad0a5
sp:0xffbe7204 + 4 = 0xffbe7208
(gdb) disassemble 0xeb3ad0a5
Dump of assembler code for function __libc_android_abort():
0xeb3ad07e <+0>: push {r4, r5, r7, lr}
0xeb3ad080 <+2>: sub sp, #24
0xeb3ad082 <+4>: add r4, sp, #20
0xeb3ad084 <+6>: mov r0, r4
0xeb3ad086 <+8>: blx 0xeb3a8ac0 <sigfillset@plt>
0xeb3ad08a <+12>: mov r0, r4
0xeb3ad08c <+14>: movs r1, #6
0xeb3ad08e <+16>: blx 0xeb3a8acc <sigdelset@plt>
0xeb3ad092 <+20>: movs r0, #2
0xeb3ad094 <+22>: mov r1, r4
0xeb3ad096 <+24>: movs r2, #0
0xeb3ad098 <+26>: movs r5, #0
0xeb3ad09a <+28>: blx 0xeb3a8658 <sigprocmask@plt>
0xeb3ad09e <+32>: movs r0, #6
0xeb3ad0a0 <+34>: blx 0xeb3a8ad8 <raise@plt>
=> 0xeb3ad0a4 <+38>: str r5, [sp, #4]
0xeb3ad0a6 <+40>: mov.w r0, #268435456 ; 0x10000000
0xeb3ad0aa <+44>: add r5, sp, #4
0xeb3ad0ac <+46>: str r0, [sp, #12]
0xeb3ad0ae <+48>: adds r0, r5, #4
0xeb3ad0b0 <+50>: blx 0xeb3a8ae4 <sigemptyset@plt>
0xeb3ad0b4 <+54>: movs r0, #6
0xeb3ad0b6 <+56>: mov r1, r5
0xeb3ad0b8 <+58>: mov r2, r5
0xeb3ad0ba <+60>: blx 0xeb3a8af0 <sigaction@plt>
0xeb3ad0be <+64>: movs r0, #2
0xeb3ad0c0 <+66>: mov r1, r4
0xeb3ad0c2 <+68>: movs r2, #0
0xeb3ad0c4 <+70>: blx 0xeb3a8658 <sigprocmask@plt>
0xeb3ad0c8 <+74>: movs r0, #6
0xeb3ad0ca <+76>: blx 0xeb3a8ad8 <raise@plt>
0xeb3ad0ce <+80>: movs r0, #1
0xeb3ad0d0 <+82>: blx 0xeb3a8afc <_exit@plt>
End of assembler dump.
stack:
addr value reg
0xffbe7208 0x0000004a
0xffbe720c 0x0061b737
0xffbe7210 0xeae0b000
0xffbe7214 0xeae0b008
0xffbe7218 0x00000001
0xffbe721c 0xffffffdf
0xffbe7220 0xeb40b9ec r4
0xffbe7224 0x00000000 r5
0xffbe7228 0xeb3e3679 r7
0xffbe722c 0xeb3ab108 lr
#04:
#04 abort()
pc:0xeb3ab108
sp:0xffbe722c + 4 = 0xffbe7230
(gdb) disassemble 0xeb3ab108-0x10,+0x20
Dump of assembler code from 0xeb3ab0f8 to 0xeb3ab118:
0xeb3ab0f8 <memcmp+640>: pop {r5, r6, r7}
0xeb3ab0fc <memcmp+644>: b 0xeb3aaf94 <memcmp+284>
0xeb3ab100 <abort+0>: push {r3, lr}
0xeb3ab104 <abort+4>: blx 0xeb3ad07e <__libc_android_abort()>
=> 0xeb3ab108 <__bionic_clone+0>: mov r12, sp
0xeb3ab10c <__bionic_clone+4>: push {r4, r5, r6, r7}
0xeb3ab110 <__bionic_clone+8>: ldm r12, {r4, r5, r6}
0xeb3ab114 <__bionic_clone+12>: stmdb r1!, {r5, r6}
stack:
addr value reg
0xffbe7230 0x00000072 r3
0xffbe7234 0xeb3af553 lr
#05 __libc_fatal()
pc:0xeb3af553
sp:0xffbe7234 + 4 = 0xffbe7238
(gdb) disassemble 0xeb3af553-0x20,+0x21
Dump of assembler code from 0xeb3af533 to 0xeb3af554:
0xeb3af533 <__fortify_chk_fail(char const*, uint32_t)+30>: nop
0xeb3af535 <__fortify_chk_fail(char const*, uint32_t)+32>: strh r4, [r4, #60] ; 0x3c
0xeb3af537 <__fortify_chk_fail(char const*, uint32_t)+34>: movs r5, r0
0xeb3af539 <__libc_fatal(char const*, ...)+0>: sub sp, #12
0xeb3af53b <__libc_fatal(char const*, ...)+2>: push {r7, lr}
0xeb3af53d <__libc_fatal(char const*, ...)+4>: sub sp, #4
0xeb3af53f <__libc_fatal(char const*, ...)+6>: add.w r12, sp, #12
0xeb3af543 <__libc_fatal(char const*, ...)+10>: stmia.w r12, {r1, r2, r3}
0xeb3af547 <__libc_fatal(char const*, ...)+14>: add r1, sp, #12
0xeb3af549 <__libc_fatal(char const*, ...)+16>: str r1, [sp, #0]
0xeb3af54b <__libc_fatal(char const*, ...)+18>: bl 0xeb3af554 <__libc_fatal(char const*, std::__va_list)>
0xeb3af54f <__libc_fatal(char const*, ...)+22>: blx 0xeb3a8f64 <abort@plt>
=> 0xeb3af553: movs r0, r0
End of assembler dump.
其中 sp 先减去 12,再push lr,r7,再减去 4
stack:
addr value reg
0xffbe7238 0xffbe7244
0xffbe723c 0xeb3e3679 r7
0xffbe7240 0xeb3af533 lr
0xffbe7244 0xeb40b9ec
0xffbe7248 0x0061b738
0xffbe724c 0x00000000
#06 __fortify_chk_fail()
pc:0xeb3af533
sp:0xffbe724c + 4 = 0xffbe7250
(gdb) disassemble 0xeb3af533
Dump of assembler code for function __fortify_chk_fail(char const*, uint32_t):
0xeb3af514 <+0>: push {r4, r5, r7, lr}
0xeb3af516 <+2>: mov r5, r1
0xeb3af518 <+4>: mov r4, r0
0xeb3af51a <+6>: cbz r5, 0xeb3af528 <__fortify_chk_fail(char const*, uint32_t)+20>
0xeb3af51c <+8>: blx 0xeb3a8f58 <getuid@plt>
0xeb3af520 <+12>: mov r1, r0
0xeb3af522 <+14>: mov r0, r5
0xeb3af524 <+16>: bl 0xeb3af44c <__libc_android_log_event_int(int32_t, int)>
0xeb3af528 <+20>: ldr r0, [pc, #8] ; (0xeb3af534 <__fortify_chk_fail(char const*, uint32_t)+32>)
0xeb3af52a <+22>: mov r1, r4
0xeb3af52c <+24>: add r0, pc
0xeb3af52e <+26>: bl 0xeb3af538 <__libc_fatal(char const*, ...)>
=> 0xeb3af532 <+30>: nop
0xeb3af534 <+32>: andeq r8, r5, r4, lsr #15
End of assembler dump.
stack:
addr value reg
0xffbe7250 0xeb41d008 r4
0xffbe7254 0xea480000 r5
0xffbe7258 0xeb3e3679 r7
0xffbe725c 0xeb3e36a1 lr
#07:
#07 __read_chk()
pc:0xeb3e36a1
sp:0xffbe725c + 4 = 0xffbe7260
(gdb) disassemble 0xeb3e36a1
Dump of assembler code for function __read_chk(int, void*, size_t, size_t):
0xeb3e3678 <+0>: push {r7, lr}
0xeb3e367a <+2>: cmp r2, r3
0xeb3e367c <+4>: bhi.n 0xeb3e3696 <__read_chk(int, void*, size_t, size_t)+30>
0xeb3e367e <+6>: cmp.w r2, #4294967295 ; 0xffffffff
0xeb3e3682 <+10>: itt gt
0xeb3e3684 <+12>: ldmiagt.w sp!, {r7, lr}
0xeb3e3688 <+16>: bgt.w 0xeb404b84
0xeb3e368c <+20>: ldr r0, [pc, #16] ; (0xeb3e36a0 <__read_chk(int, void*, size_t, size_t)+40>)
0xeb3e368e <+22>: movs r1, #0
0xeb3e3690 <+24>: add r0, pc
0xeb3e3692 <+26>: bl 0xeb3af514 <__fortify_chk_fail(char const*, uint32_t)>
0xeb3e3696 <+30>: ldr r0, [pc, #12] ; (0xeb3e36a4 <__read_chk(int, void*, size_t, size_t)+44>)
0xeb3e3698 <+32>: movs r1, #0
0xeb3e369a <+34>: add r0, pc
0xeb3e369c <+36>: bl 0xeb3af514 <__fortify_chk_fail(char const*, uint32_t)>
=> 0xeb3e36a0 <+40>: andeq r8, r2, r1, lsl #7
0xeb3e36a4 <+44>: andeq r8, r2, lr, asr #6
End of assembler dump.
stack:
addr value reg
0xffbe7260 0xeb3e3679 r7
0xffbe7264 0xebad0989 lr
实际上,从#00 - #07 GDB都能给解析出来了,我们这么做,可以加深这个印象,对接下来的分析指向更明确#08:
#08 // 由于在匿名可执行段中,所以不知道这个函数的名字
pc:0xebad0989
sp:0xffbe7264 + 4 = 0xffbe7268
0xebad0894: push {r4, r5, r6, r7, lr}
0xebad0896: mov r7, r11
0xebad0898: mov r6, r10
0xebad089a: mov r5, r9
0xebad089c: mov r4, r8
-> 0xebad089e: push {r4, r5, r6, r7}
0xebad08a0: ldr r4, [pc, #528] ; (0xebad0ab4) // r4=0xffffef6c
0xebad08a2: ldr r2, [pc, #532] ; (0xebad0ab8) // r2=0x1084
0xebad08a4: adds r6, r0, #0 //
-> 0xebad08a6: add sp, r4 //
0xebad08a8: add r1, sp, #8
0xebad08aa: mov r12, r1
0xebad08ac: ldr r4, [pc, #524] ; (0xebad0abc) // r4=0x0002a46a
0xebad08ae: add r2, r12
0xebad08b0: add r5, sp, #140 ; 0x8c
0xebad08b2: add r4, pc // r4=0xebad08b6
0xebad08b4: ldr r4, [r4, #0] // r4=0x68232100
0xebad08b6: movs r1, #0 //
0xebad08b8: ldr r3, [r4, #0]
0xebad08ba: adds r0, r5, #0
0xebad08bc: str r3, [r2, #0]
0xebad08be: movs r2, #128 ; 0x80
0xebad08c0: ldr r3, [pc, #508] ; (0xebad0ac0)
0xebad08c2: lsls r2, r2, #5
0xebad08c4: add r3, pc
0xebad08c6: ldr r7, [r3, #0]
0xebad08c8: bl 0xebaea964
0xebad08cc: ldr r1, [pc, #500] ; (0xebad0ac4)
0xebad08ce: adds r0, r6, #0
0xebad08d0: add r1, pc
0xebad08d2: bl 0xebaeac04
0xebad08d6: cmp r6, r0
0xebad08d8: bls.n 0xebad08e2
0xebad08da: b.n 0xebad08ee
0xebad08dc: subs r0, #1
0xebad08de: cmp r6, r0
0xebad08e0: bhi.n 0xebad08ee
0xebad08e2: ldrb r3, [r0, #0]
0xebad08e4: subs r3, #48 ; 0x30
0xebad08e6: lsls r3, r3, #24
0xebad08e8: lsrs r3, r3, #24
0xebad08ea: cmp r3, #9
0xebad08ec: bls.n 0xebad08dc
0xebad08ee: subs r3, r0, r6
0xebad08f0: mov r8, r3
0xebad08f2: adds r2, r3, #0
0xebad08f4: adds r1, r6, #0
0xebad08f6: adds r0, r5, #0
0xebad08f8: bl 0xebaeb064
0xebad08fc: mov r3, r8
0xebad08fe: adds r0, r5, r3
0xebad0900: ldr r3, [pc, #452] ; (0xebad0ac8)
0xebad0902: movs r2, #4
0xebad0904: add r3, pc
0xebad0906: mov r8, r3
0xebad0908: adds r1, r3, #0
0xebad090a: bl 0xebaea944
0xebad090e: adds r0, r6, #0
0xebad0910: mov r1, r8
0xebad0912: bl 0xebaeac04
0xebad0916: cmp r0, #0
0xebad0918: beq.n 0xebad0942
0xebad091a: subs r0, #1
0xebad091c: cmp r6, r0
0xebad091e: bhi.n 0xebad0942
0xebad0920: ldrb r3, [r0, #0]
0xebad0922: subs r3, #48 ; 0x30
0xebad0924: lsls r3, r3, #24
0xebad0926: lsrs r3, r3, #24
0xebad0928: cmp r3, #9
0xebad092a: bhi.n 0xebad09b6
0xebad092c: subs r6, #1
0xebad092e: b.n 0xebad093c
0xebad0930: ldrb r3, [r0, #0]
0xebad0932: subs r3, #48 ; 0x30
0xebad0934: lsls r3, r3, #24
0xebad0936: lsrs r3, r3, #24
0xebad0938: cmp r3, #9
0xebad093a: bhi.n 0xebad09b6
0xebad093c: subs r0, #1
0xebad093e: cmp r0, r6
0xebad0940: bne.n 0xebad0930
0xebad0942: movs r3, #1
0xebad0944: mov r8, r3
0xebad0946: adds r0, r5, #0
0xebad0948: movs r1, #0
0xebad094a: bl 0xebaea9e4
0xebad094e: subs r6, r0, #0
0xebad0950: ble.n 0xebad09d2
0xebad0952: add r5, sp, #32
0xebad0954: movs r1, #0
0xebad0956: adds r0, r5, #0
0xebad0958: movs r2, #104 ; 0x68
0xebad095a: bl 0xebaea964
0xebad095e: adds r0, r6, #0
0xebad0960: adds r1, r5, #0
0xebad0962: bl 0xebaeadb4
0xebad0966: adds r3, r0, #1
0xebad0968: beq.n 0xebad098c
0xebad096a: ldr r3, [r5, #48] ; 0x30
0xebad096c: movs r0, #1
0xebad096e: adds r1, r3, #0
0xebad0970: mov r9, r3
0xebad0972: bl 0xebaea9b4
0xebad0976: subs r5, r0, #0
0xebad0978: beq.n 0xebad098c
0xebad097a: adds r0, r6, #0
0xebad097c: adds r1, r5, #0
0xebad097e: mov r2, r9
0xebad0980: cmp r7, #0
0xebad0982: bne.n 0xebad0986
0xebad0984: b.n 0xebad0aa4
0xebad0986: blx r7
=> 0xebad0988: cmp r0, r9
在这个 frame中,对sp做了以下操作,稍微复杂一点:
0xebad0894: push {r4, r5, r6, r7, lr} //先push r4-r7,lr
0xebad0896: mov r7, r11
0xebad0898: mov r6, r10
0xebad089a: mov r5, r9
0xebad089c: mov r4, r8
-> 0xebad089e: push {r4, r5, r6, r7} //,因为把r8-r11放到了r4-r7,相当于保存r8-r11
0xebad08a0: ldr r4, [pc, #528] ; (0xebad0ab4) // r4=0xffffef6c
0xebad08a2: ldr r2, [pc, #532] ; (0xebad0ab8)
0xebad08a4: adds r6, r0, #0 //
-> 0xebad08a6: add sp, r4 //sp=sp+r4
我们已经知道了当前frame的sp栈顶是在 sp:0xffbe7264 + r4 = 0xffbe7268,
所以这里 sp = sp+r4 = 0xffbe7268,也即在 0xebad08a6 之前的sp我们记为 sp2 = 0xffbe7268 - r4
其中 r4 在0xebad08a0处赋值, r4 = [0xebad0ab4] = 0xffffef6c
所以 sp2 = 0xffbe7268 - 0xffffef6c = 0xffbe82fc
(gdb) p /x 0xffbe7268-0xffffef6c
$12 = 0xffbe82fc
所以 sp2至当前栈顶的距离等于 0xffbe82fc - 0xffbe7268 = 4244;
所以 0xebad08a6: add sp, r4 这条指令相当于 sp = sp - 4244,堆栈扩展了 4k+的一个空间;是的sp指向到 0xffbe7268;
这个栈的栈底我们标记为 sp_start,
0xebad0894: push {r4, r5, r6, r7, lr}
0xebad089e: push {r4, r5, r6, r7}
由于这两条push指令,
所以:
sp_start - 5*4 - 4*4 = sp2 = 0xffbe82fc
sp_start = 0xffbe8320
所以这个frame中,栈底位置是 0xffbe8320
#8 stack 如下:
stack:
addr value reg
0xffbe7268 0x00000000
...
...(4244 byte)
...
0xffbe82fc 0xffbe83f0 r8
0xffbe8300 0x00000001 r9
0xffbe8304 0xeb3e3679 r10
0xffbe8308 0xffffffff r11
0xffbe830c 0xeb41d008 r4
0xffbe8310 0xffbe8320 r5
0xffbe8314 0x00000007 r6
0xffbe8318 0x00000004 r7
0xffbe831c 0xebad1113 lr
#09:
#09 // 同样在匿名可执行段中,不知道函数的名字
pc:0xebad1113
sp:0xffbe831c + 4 = 0xffbe8320
(gdb) disassemble 0xebad1069,0xebad1115
Dump of assembler code from 0xebad1069 to 0xebad1115:
0xebad1069: push {r4, r5, r6, r7, lr}
0xebad106b: mov r7, r11
0xebad106d: mov r6, r10
0xebad106f: mov r4, r8
0xebad1071: mov r5, r9
0xebad1073: push {r4, r5, r6, r7}
0xebad1075: adds r6, r0, #0
0xebad1077: sub sp, #20
0xebad1079: mov r5, sp
0xebad107b: ldr r4, [pc, #380] ; (0xebad11f8)
0xebad107d: mov r11, r3
0xebad107f: add r4, pc
0xebad1081: ldr r4, [r4, #0]
0xebad1083: mov r8, r1
0xebad1085: ldr r3, [r4, #0]
0xebad1087: mov r0, sp
0xebad1089: str r3, [sp, #12]
0xebad108b: ldr r3, [pc, #368] ; (0xebad11fc)
0xebad108d: adds r1, r6, #0
0xebad108f: add r3, pc
0xebad1091: ldr r3, [r3, #0]
0xebad1093: adds r7, r2, #0
0xebad1095: mov r10, r3
0xebad1097: bl 0xebacfd8c
0xebad109b: ldrb r3, [r5, #0]
0xebad109d: lsls r3, r3, #31
0xebad109f: bpl.n 0xebad111a
0xebad10a1: ldr r3, [sp, #8]
0xebad10a3: ldr r1, [pc, #348] ; (0xebad1200)
0xebad10a5: adds r0, r3, #0
0xebad10a7: add r1, pc
0xebad10a9: mov r9, r3
0xebad10ab: bl 0xebaeac04
0xebad10af: cmp r0, #0
0xebad10b1: beq.n 0xebad113c
0xebad10b3: ldr r1, [pc, #336] ; (0xebad1204)
0xebad10b5: mov r0, r9
0xebad10b7: add r1, pc
0xebad10b9: bl 0xebaeac04
0xebad10bd: cmp r0, #0
0xebad10bf: beq.n 0xebad113c
0xebad10c1: subs r2, r0, #1
0xebad10c3: cmp r9, r2
0xebad10c5: bhi.n 0xebad10ee
0xebad10c7: ldrb r3, [r2, #0]
0xebad10c9: subs r3, #48 ; 0x30
0xebad10cb: lsls r3, r3, #24
0xebad10cd: lsrs r3, r3, #24
0xebad10cf: cmp r3, #9
0xebad10d1: bls.n 0xebad10d4
0xebad10d3: b.n 0xebad11c2
0xebad10d5: mov r0, r9
0xebad10d7: subs r0, #1
0xebad10d9: b.n 0xebad10e8
0xebad10db: ldrb r3, [r2, #0]
0xebad10dd: subs r3, #48 ; 0x30
0xebad10df: lsls r3, r3, #24
0xebad10e1: lsrs r3, r3, #24
0xebad10e3: cmp r3, #9
0xebad10e5: bls.n 0xebad10e8
0xebad10e7: b.n 0xebad11c2
0xebad10e9: subs r2, #1
0xebad10eb: cmp r2, r0
0xebad10ed: bne.n 0xebad10da
0xebad10ef: movs r3, #1
0xebad10f1: movs r0, #1
0xebad10f3: mov r9, r3
0xebad10f5: ldr r3, [pc, #272] ; (0xebad1208)
0xebad10f7: add r3, pc
0xebad10f9: ldr r2, [r3, #0]
0xebad10fb: ldr r3, [r3, #4]
0xebad10fd: subs r3, r3, r2
0xebad10ff: asrs r3, r3, #2
0xebad1101: cmp r3, r0
0xebad1103: bcs.n 0xebad1178
0xebad1105: mov r2, sp
0xebad1107: ldrb r3, [r5, #0]
0xebad1109: adds r0, r2, #1
0xebad110b: lsls r3, r3, #31
0xebad110d: bmi.n 0xebad11d6
0xebad110f: bl 0xebad0894 // 可以看到这里是 frame8函数的起始地址,说明没有推倒错
=> 0xebad1113: cmp r0, #0
End of assembler dump.
操作sp的指令:
0xebad1069: push {r4, r5, r6, r7, lr}
0xebad106b: mov r7, r11
0xebad106d: mov r6, r10
0xebad106f: mov r4, r8
0xebad1071: mov r5, r9
0xebad1073: push {r4, r5, r6, r7}
0xebad1075: adds r6, r0, #0
0xebad1077: sub sp, #20
stack:
addr value reg
0xffbe8320 0x00000051
0xffbe8324 0x00000040
0xffbe8328 0xeae35140
0xffbe832c 0xb53930d7
0xffbe8330 0xeae00000
0xffbe8334 0xffbe83dc r8
0xffbe8338 0xeae2d000 r9
0xffbe833c 0x00000006 r10
0xffbe8340 0xab08e004 r11
0xffbe8344 0xffbe83f0 r4
0xffbe8348 0xffbe83e0 r5
0xffbe834c 0xffbe8e4e r6
0xffbe8350 0x00000007 r7
0xffbe8354 0xeb4d0735 lr
#10:
#10 art::OpenAndReadMagic() //这里可以看到函数名了
pc:0xeb4d0735
sp:0xffbe8354 + 4 = 0xffbe8358
当前pc在 libart.so的可执行段中:
eb428000-eb87afff r-x 0 453000 /system/lib/libart.so (BuildId: 05da207454e69ef76261022c1a78d6fe) (load base 0xb000)
可以看到 frame10 的 pc 已经在 libart.so中了,对应的函数是 art::OpenAndReadMagic。
(gdb) disassemble 0xeb4d0735
Dump of assembler code for function art::OpenAndReadMagic(char const*, unsigned int*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*):
0xeb4d06f8 <+0>: stmdb sp!, {r4, r5, r6, r7, r8, r9, r10, lr}
0xeb4d06fc <+4>: sub sp, #64 ; 0x40
0xeb4d06fe <+6>: mov r8, r0
0xeb4d0700 <+8>: ldr r0, [pc, #448] ; (0xeb4d08c4 <art::OpenAndReadMagic(char const*, unsigned int*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)+460>)
0xeb4d0702 <+10>: mov r4, r2
0xeb4d0704 <+12>: mov r5, r3
0xeb4d0706 <+14>: add r0, pc
0xeb4d0708 <+16>: mov r6, r1
0xeb4d070a <+18>: cmp r4, #0
0xeb4d070c <+20>: ldr r0, [r0, #0]
0xeb4d070e <+22>: ldr r0, [r0, #0]
0xeb4d0710 <+24>: str r0, [sp, #60] ; 0x3c
0xeb4d0712 <+26>: beq.n 0xeb4d0812 <art::OpenAndReadMagic(char const*, unsigned int*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)+282>
0xeb4d0714 <+28>: mov r0, r6
0xeb4d0716 <+30>: movs r1, #0
0xeb4d0718 <+32>: movs r2, #0
0xeb4d071a <+34>: blx 0xeb4b2ea4 <open@plt> //
0xeb4d071e <+38>: mov r7, r0 // open的返回值是 fd,放到 r7
0xeb4d0720 <+40>: cmp.w r7, #4294967295 ; 0xffffffff
0xeb4d0724 <+44>: beq.n 0xeb4d0746 <art::OpenAndReadMagic(char const*, unsigned int*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)+78>
0xeb4d0726 <+46>: mov r0, r7 // fd
0xeb4d0728 <+48>: mov r1, r4 // buf首地址
0xeb4d072a <+50>: movs r2, #4 // count 是4
0xeb4d072c <+52>: mov.w r3, #4294967295 ; 0xffffffff // buf_size
0xeb4d0730 <+56>: blx 0xeb4b2eb0 <__read_chk@plt>
=> 0xeb4d0734 <+60>: cmp.w r0, #4294967295 ; 0xffffffff
0xeb4d0738 <+64>: bne.n 0xeb4d076c <art::OpenAndReadMagic(char const*, unsigned int*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)+116>
0xeb4d073a <+66>: blx 0xeb4b2ebc <__errno@plt>
可以看到 该方法中调用 __read_chk:0xeb4d0730 <+56>: blx 0xeb4b2eb0 <__read_chk@plt>
即跳转到 libart.so的 plt 表中 __read_chk对应的函数, 我们从 0xeb4b2eb0往下推推看:
(gdb) disassemble 0xeb4b2eb0-0x10,+0x20
Dump of assembler code from 0xeb4b2ea0 to 0xeb4b2ec0:
0xeb4b2ea0 <__aeabi_memmove@plt+8>: ldr pc, [r12, #3356]! ; 0xd1c
0xeb4b2ea4 <open@plt+0>: add r12, pc, #3145728 ; 0x300000
0xeb4b2ea8 <open@plt+4>: add r12, r12, #847872 ; 0xcf000
0xeb4b2eac <open@plt+8>: ldr pc, [r12, #3348]! ; 0xd14
-> 0xeb4b2eb0 <__read_chk@plt+0>: add r12, pc, #3145728 ; 0x300000
0xeb4b2eb4 <__read_chk@plt+4>: add r12, r12, #847872 ; 0xcf000
=>0xeb4b2eb8 <__read_chk@plt+8>: ldr pc, [r12, #3340]! ; 0xd0c
0xeb4b2ebc <__errno@plt+0>: add r12, pc, #3145728 ; 0x300000
End of assembler dump.
在 __read_chk的 plt 项中会通过 ldr pc, [r12, #3340]! 进行跳转,这里是没有保存LR的,所以LR仍然是 OpenAndReadMagic 函数中, 0xeb4d0730 <+56>: blx 0xeb4b2eb0 的下一条指令地址:0xeb4d0735
看下此时跳转到哪:
pc= [0xeb4b2eb0+8+0x300000+0xcf000+0xd0c] = 0xebad1069
(gdb) x /x 0xeb4b2eb0+8+0x300000+0xcf000+0xd0c
0xeb882bc4: 0xebad1069
这里就 0xebad1069 就是frame9对应的函数地址了。
所以到这里,调用关系梳理完了,如下:
#0 tgkill () at bionic/libc/arch-arm/syscalls/tgkill.S:10
#1 0xeb3db2b6 in pthread_kill (t=<optimized out>, sig=6) at bionic/libc/bionic/pthread_kill.cpp:45
#2 0xeb3b1558 in raise (sig=23898) at bionic/libc/bionic/raise.cpp:34
#3 0xeb3ad0a4 in __libc_android_abort () at bionic/libc/bionic/abort.cpp:47
#4 0xeb3ab108 in abort () at bionic/libc/arch-arm/bionic/abort_arm.S:43
#5 0xeb3af552 in __libc_fatal (format=0x0) at bionic/libc/bionic/libc_logging.cpp:678
#6 0xeb3af532 in __fortify_chk_fail (msg=0xeb40b9ec "read: prevented write past end of buffer", tag=0) at bionic/libc/bionic/libc_logging.cpp:645
#7 0xeb3e36a0 in _read_chk (fd=<optimized out>, buf=<optimized out>, count=6, buf_size=114) at bionic/libc/bionic/_read_chk.cpp:35
#8 <anonymous:eba9b000>:blx r7 (0xeb3e3679 r7)
#9 <anonymous:eba9b000>:bl 0xebad0894
<anonymous:eba9b000>:ldr pc, [r12, #3340]! ; 0xd0c// 相当于 b 0xebad1069 ,由于没有堆栈且是直接跳转,归为 frame10
liabrt.so: blx 0xeb4b2eb0 <__read_chk@plt>// 由于没有堆栈且是直接跳转,归为 frame10
#10 libart.so:: OpenAndReadMagic
2.2 业务逻辑推倒
查看 Art中OpenAndReadMagic函数:
1.ScopedFd OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
CHECK(magic != nullptr);
ScopedFd fd(open(filename, O_RDONLY, 0));
if (fd.get() == -1) {
*error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno));
return ScopedFd();
}
int n = TEMP_FAILURE_RETRY(read(fd.get(), magic, sizeof(*magic)));
if (n != sizeof(*magic)) {
*error_msg = StringPrintf("Failed to find magic in '%s'", filename);
return ScopedFd();
}
if (lseek(fd.get(), 0, SEEK_SET) != 0) {
*error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename,
strerror(errno));
return ScopedFd();
}
return fd;
}
其中是调用 read函数,读取dex文件的 magic数据;
read是个inline函数:
__BIONIC_FORTIFY_INLINE
ssize_t read(int fd, void* buf, size_t count) {
size_t bos = __bos0(buf);
#if !defined(__clang__)
if (__builtin_constant_p(count) && (count > SSIZE_MAX)) {
__read_count_toobig_error();
}
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
return __read_real(fd, buf, count);
}
if (__builtin_constant_p(count) && (count > bos)) {
__read_dest_size_error();
}
if (__builtin_constant_p(count) && (count <= bos)) {
return __read_real(fd, buf, count);
}
#endif
return __read_chk(fd, buf, count, bos);
}
所以,正常流程下,ART的OpenAndReadMagic编译后会直接调用系统的 read_chk;
而我们这里看到,调用read_chk时,却跳转到了<anonymous:eba9b000>中的代码中运行,这应该是当前进程中__read_chk函数被 hook了;
由于libjiagu是加固框架,其对dex文件加密,所以如果是正常流程读取dex文件,是无法识别其加密过的dex文件的,这就需要其自己对加密的dex文件做解密处理;
这里的逻辑应该就是:
libjiagu.so hook 当前进程的__read_chk函数,当执行到 jiagu.so中的 read_chk时,先进行解密等相关操作后,在调用系统的read_check把正确的数据交给ART去处理。
2.3 参数推倒
从目前分析来看是从 frame8中调用到 frame7的系统调用 __read_chk时,检查参数错误导致的abort,所以从frame8的参数传递开始分析;
系统调用:
__read_chk(int fd, void* buf, size_t count, size_t buf_size)// __read_chk 共需要4个参数,ARM中就是 r0,r1,r2,r3
查看frame8中的参数准备:
(gdb) f 8
#8 0xecab842c in ?? ()
(gdb) disassemble 0xecab842c-0x50,+0x51
Dump of assembler code from 0xecab83dc to 0xecab842d:
0xecab83dc: cmp r3, #9
0xecab83de: bhi.n 0xecab845a
0xecab83e0: subs r0, #1
0xecab83e2: cmp r0, r6
0xecab83e4: bne.n 0xecab83d4
0xecab83e6: movs r3, #1
0xecab83e8: mov r8, r3
0xecab83ea: adds r0, r5, #0
0xecab83ec: movs r1, #0
0xecab83ee: bl 0xecad2e64 //调用函数open
0xecab83f2: subs r6, r0, #0 //将open函数的返回值r0(fd)放到 r6
0xecab83f4: ble.n 0xecab8476
0xecab83f6: add r5, sp, #32
0xecab83f8: movs r1, #0 // 准备第二个参数,即 memset 的内容 0
0xecab83fa: adds r0, r5, #0 //准备第一个参数,即memset的首地址
0xecab83fc: movs r2, #104 ; 0x68 // 准备地三个参数,即memset的大小 104
0xecab83fe: bl 0xecad2da4 //跳转到函数 memset, buf对应的前 104字节设置为 0
0xecab8402: adds r0, r6, #0 //准备 fstat函数第一个参数fd
0xecab8404: adds r1, r5, #0 //准备 fstat函数第二个参数 buf
0xecab8406: bl 0xecad3304 //跳转到函数 fstat64
0xecab840a: adds r3, r0, #1
0xecab840c: beq.n 0xecab8430
0xecab840e: ldr r3, [r5, #48] ; 0x30 //从buf中取出要malloc的的内存大小 count
0xecab8410: movs r0, #1 //分配一组数据
0xecab8412: adds r1, r3, #0 //每组数据大小为count
0xecab8414: mov r9, r3 //count保存到 r9 中
0xecab8416: bl 0xecad2df4 //跳转到 calloc函数
0xecab841a: subs r5, r0, #0 // calloc函数返回值,即calloc的地址放到 r5
0xecab841c: beq.n 0xecab8430
0xecab841e: adds r0, r6, #0 // 准备第一个参数 r0(fd)
0xecab8420: adds r1, r5, #0 // 准备第二个参数 r1(buf),从上面 calloc的r5过来的,作为 buffer起始地址
0xecab8422: mov r2, r9 // 准备第三个参数 r2(count),到这里,可以看到没有第四个参数 r3 的构造
0xecab8424: cmp r7, #0 // 判断__read_chk函数(r7) 指针是否为空
0xecab8426: bne.n 0xecab842a // __read_chk不为空,跳转到 0xecab842a
0xecab8428: b.n 0xecab8548
0xecab842a: blx r7 //跳转到 libc 的 __read_chk函数
在这里,我们看到,blx r7之前,也即调用libc __read_ chk之前只准备了 r0,r1,r2三个参数,没有准备 r3,
看起来像是在函数调用时只传递了3个参数编译出来的指令。
那么 r2作为 count, r3 作为 buf_size,在 frame7中要做比较:
#07 __read_chk()
pc:0xeb3e36a1
sp:0xffbe725c + 4 = 0xffbe7260(gdb) disassemble 0xeb3e36a1
Dump of assembler code for function __read_chk(int, void*, size_t, size_t):
0xeb3e3678 <+0>: push {r7, lr}
0xeb3e367a <+2>: cmp r2, r3 // 比较 r2,r3大小
0xeb3e367c <+4>: bhi.n 0xeb3e3696 <__read_chk(int, void*, size_t, size_t)+30> // r2 比 r3大,跳转到 0xeb3e3696
0xeb3e367e <+6>: cmp.w r2, #4294967295 ; 0xffffffff
0xeb3e3682 <+10>: itt gt
0xeb3e3684 <+12>: ldmiagt.w sp!, {r7, lr}
0xeb3e3688 <+16>: bgt.w 0xeb404b84
0xeb3e368c <+20>: ldr r0, [pc, #16] ; (0xeb3e36a0 <__read_chk(int, void*, size_t, size_t)+40>)
0xeb3e368e <+22>: movs r1, #0
0xeb3e3690 <+24>: add r0, pc
0xeb3e3692 <+26>: bl 0xeb3af514 <__fortify_chk_fail(char const*, uint32_t)>
0xeb3e3696 <+30>: ldr r0, [pc, #12] ; (0xeb3e36a4 <__read_chk(int, void*, size_t, size_t)+44>)
0xeb3e3698 <+32>: movs r1, #0
0xeb3e369a <+34>: add r0, pc
0xeb3e369c <+36>: bl 0xeb3af514 <__fortify_chk_fail(char const*, uint32_t)> // 跳转到 __fortify_chk_fail函数进行Abort
=> 0xeb3e36a0 <+40>: andeq r8, r2, r1, lsl #7
0xeb3e36a4 <+44>: andeq r8, r2, lr, asr #6
End of assembler dump.
由于在frame8中,调用 __read_chk时没有准备r3,那么此时使用的 r3的值肯定不是正确的,它是不确定的,可能在之前的某一次函数调用修改过;
经过查看在 0xecab8416: bl 0xecad2df4 //跳转到 calloc函数, calloc函数中就修改了r3;
从而就导致,概率性的 r2 的值大于r3的值,进程abort退出。
2.4 结论
所以,问题原因是:
应用启动的dex2oat进程中 libjiagu hook了 libc的 __read_chk函数做解密操作,在解密完成后,重新调用libc的 __read_chk函数时,
少传了第四个参数 buf_size.
3.扩展
其实到上面的分析,我们已经能够得出问题的结论,但继续推到调用栈应该能够推到dex2oat的main函数,继续推倒:
接着上面 frame10:
#10 pc:0xeb4d0735 sp:0xffbe8354 + 4 = 0xffbe8358
#11:stack: addr value reg 0xffbe8358 0xeba00500 0xffbe835c 0x00000000 0xffbe8360 0xeae0b008 0xffbe8364 0xeb3e8d5f 0xffbe8368 0xeb421c88 0xffbe836c 0xeb3e7b19 0xffbe8370 0x00000000 0xffbe8374 0xeae0f6a0 0xffbe8378 0xeae0b040 0xffbe837c 0xeb3f8a29 0xffbe8380 0x0000000f 0xffbe8384 0xeae0f6a0 0xffbe8388 0xeae0b008 0xffbe838c 0xeae0b040 0xffbe8390 0xeae0b070 0xffbe8394 0xb53930d7 0xffbe8398 0xeae4d000 r4 0xffbe839c 0xffbe8e4e r5 0xffbe83a0 0xffbe8e4e r6 0xffbe83a4 0x00000000 r7 0xffbe83a8 0xffbe868c r8 0xffbe83ac 0xeae2d000 r9 0xffbe83b0 0x00000006 r10 0xffbe83b4 0xeb12523b lr
#11 art::OatWriter::AddDexFileSource() pc:0xeb12523b sp:0xffbe83b4 + 4 = 0xffbe83b8
(gdb) disassemble 0xeb12523b Dump of assembler code for function art::OatWriter::AddDexFileSource(char const*, char const*, art::OatWriter::CreateTypeLookupTable): 0xeb12520c <+0>: stmdb sp!, {r4, r5, r6, r7, r8, r9, r10, lr} 0xeb125210 <+4>: sub sp, #72 ; 0x48 0xeb125212 <+6>: mov r4, r0 0xeb125214 <+8>: ldr r0, [pc, #588] ; (0xeb125464 <art::OatWriter::AddDexFileSource(char const*, char const*, art::OatWriter::CreateTypeLookupTable)+600>) 0xeb125216 <+10>: mov r7, r3 0xeb125218 <+12>: mov r6, r2 0xeb12521a <+14>: add r0, pc 0xeb12521c <+16>: add r2, sp, #56 ; 0x38 0xeb12521e <+18>: add r3, sp, #40 ; 0x28 0xeb125220 <+20>: mov r5, r1 0xeb125222 <+22>: ldr r0, [r0, #0] 0xeb125224 <+24>: ldr r0, [r0, #0] 0xeb125226 <+26>: str r0, [sp, #68] ; 0x44 0xeb125228 <+28>: movs r0, #0 0xeb12522a <+30>: strd r7, r6, [sp, #60] ; 0x3c 0xeb12522e <+34>: strd r0, r0, [sp, #40] ; 0x28 0xeb125232 <+38>: str r0, [sp, #48] ; 0x30 0xeb125234 <+40>: add r0, sp, #36 ; 0x24 0xeb125236 <+42>: blx 0xeb06ef5c <_ZN3art16OpenAndReadMagicEPKcPjPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE@plt> => 0xeb12523a <+46>: ldr r0, [sp, #36] ; 0x24
#12:stack: addr value reg 0xffbe83b8 0xffbe868c ... 0xffbe8400 0xeae0e000 r4 0xffbe8404 0xeae2d000 r5 0xffbe8408 0x00000000 r6 0xffbe840c 0x00000001 r7 0xffbe8410 0xffbe868c r8 0xffbe8414 0xeae2d000 r9 0xffbe8418 0x00000006 r10 0xffbe841c 0xab08005f lr
#12 art::Dex2Oat::AddDexFileSources() pc:0xab08005f sp:0xffbe841c + 4 = 0xffbe8420
(gdb) disassemble 0xab08005f Dump of assembler code for function art::Dex2Oat::AddDexFileSources(): 0xab07ff88 <+0>: stmdb sp!, {r4, r5, r6, r7, r8, lr} 0xab07ff8c <+4>: sub sp, #8 0xab07ff8e <+6>: mov r5, r0 0xab07ff90 <+8>: ldr r0, [pc, #288] ; (0xab0800b4 <art::Dex2Oat::AddDexFileSources()+300>) 0xab07ff92 <+10>: ldr r1, [pc, #292] ; (0xab0800b8 <art::Dex2Oat::AddDexFileSources()+304>) 0xab07ff94 <+12>: add r0, pc 0xab07ff96 <+14>: add r1, pc 0xab07ff98 <+16>: ldr r0, [r0, #0] ... 0xab08004c <+196>: ldr.w r2, [r5, #196] ; 0xc4 0xab080050 <+200>: movs r3, #0 0xab080052 <+202>: ldr r1, [r1, #0] 0xab080054 <+204>: movs r6, #0 0xab080056 <+206>: ldr r0, [r0, #0] 0xab080058 <+208>: ldr r2, [r2, #0] 0xab08005a <+210>: blx 0xab075158 <_ZN3art9OatWriter16AddDexFileSourceEPKcS2_NS0_21CreateTypeLookupTableE@plt> => 0xab08005e <+214>: cbz r0, 0xab080092 <art::Dex2Oat::AddDexFileSources()+266>
#13:stack: addr value reg 0xffbe8420 0xeae0cd50 0xffbe8424 0xb53930d7 0xffbe8428 0xeae0e000 r4 0xffbe842c 0xeae0cd50 r5 0xffbe8430 0xeadfd060 r6 0xffbe8434 0x00000000 r7 0xffbe8438 0x00000004 r8 0xffbe843c 0xab078103 lr
#13 art::Dex2Oat::Setup() //到这里已经到了 dex2oat的Setup函数,这个函数是 dex2oat中的 pc:0xab078103 sp:0xffbe843c + 4 = 0xffbe8440
Dump of assembler code for function art::Dex2Oat::Setup(): 0xab078040 <+0>: stmdb sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr} 0xab078044 <+4>: sub sp, #284 ; 0x11c 0xab078046 <+6>: mov r9, r0 0xab078048 <+8>: ldr.w r0, [pc, #3728] ; 0xab078edc <art::Dex2Oat::Setup()+3740> 0xab07804c <+12>: ldr.w r1, [pc, #3728] ; 0xab078ee0 <art::Dex2Oat::Setup()+3744> 0xab078050 <+16>: add r0, pc 0xab078052 <+18>: add r1, pc 0xab078054 <+20>: ldr r0, [r0, #0] 0xab078056 <+22>: ldr r0, [r0, #0] 0xab078058 <+24>: str r0, [sp, #280] ; 0x118 ... 0xab0780d4 <+148>: ittt ne 0xab0780d6 <+150>: ldrne r1, [r0, #0] 0xab0780d8 <+152>: ldrne r1, [r1, #4] 0xab0780da <+154>: blxne r1 0xab0780dc <+156>: add r1, sp, #208 ; 0xd0 0xab0780de <+158>: movs r7, #0 0xab0780e0 <+160>: adds r0, r1, #4 0xab0780e2 <+162>: strd r7, r7, [sp, #212] ; 0xd4 0xab0780e6 <+166>: str r0, [sp, #56] ; 0x38 0xab0780e8 <+168>: str r0, [sp, #208] ; 0xd0 0xab0780ea <+170>: mov r0, r9 0xab0780ec <+172>: bl 0xab07f37c <art::Dex2Oat::PrepareRuntimeOptions(art::RuntimeArgumentMap*)> 0xab0780f0 <+176>: cmp r0, #1 0xab0780f2 <+178>: bne.w 0xab0790c6 <art::Dex2Oat::Setup()+4230> 0xab0780f6 <+182>: mov r0, r9 0xab0780f8 <+184>: bl 0xab07fe08 <art::Dex2Oat::CreateOatWriters()> 0xab0780fc <+188>: mov r0, r9 0xab0780fe <+190>: bl 0xab07ff88 <art::Dex2Oat::AddDexFileSources()> => 0xab078102 <+194>: cmp r0, #1
stack: addr value reg 0xffbe8440 0xb53930d7 ... 0xffbe855c 0xeae2d000 r4 0xffbe8560 0xeadfa510 r5 0xffbe8564 0xeadfd060 r6 0xffbe8568 0x00000001 r7 0xffbe856c 0x00000004 r8 0xffbe8570 0x00000000 r9 0xffbe8574 0x00000006 r10 0xffbe8578 0xab08e004 r11 0xffbe857c 0xab075869 lr
#14:#14 main(int, char**) pc:0xab075869 sp:0xffbe857c + 4 = 0xffbe8580 (gdb) disassemble 0xab075869 Dump of assembler code for function main(int, char**): 0xab075650 <+0>: stmdb sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr} 0xab075654 <+4>: sub sp, #4 0xab075656 <+6>: vpush {d8-d9} 0xab07565a <+10>: sub.w sp, sp, #664 ; 0x298 0xab07565e <+14>: mov r11, r0 0xab075660 <+16>: ldr.w r0, [pc, #3128] ; 0xab07629c <main(int, char**)+3148> 0xab075664 <+20>: add r7, sp, #268 ; 0x10c 0xab075666 <+22>: mov r10, r1 0xab075668 <+24>: add r0, pc 0xab07566a <+26>: ldr r0, [r0, #0] 0xab07566c <+28>: ldr r0, [r0, #0] 0xab07566e <+30>: str r0, [sp, #660] ; 0x294 0xab075670 <+32>: mov r0, r7 0xab075672 <+34>: blx 0xab074b94 <uname@plt> ... 0xab07584c <+508>: ldrb.w r0, [sp, #76] ; 0x4c 0xab075850 <+512>: tst.w r0, #1 0xab075854 <+516>: itt ne 0xab075856 <+518>: ldrne r0, [sp, #84] ; 0x54 0xab075858 <+520>: blxne 0xab074c3c <_ZdlPv@plt> 0xab07585c <+524>: add r0, sp, #88 ; 0x58 0xab07585e <+526>: blx 0xab074bdc <_ZN3art10LogMessageD1Ev@plt> 0xab075862 <+530>: mov r0, r4 0xab075864 <+532>: bl 0xab078040 <art::Dex2Oat::Setup()> 0xab075868 <+536>: cmp r0, #0
到了 main函数:int main(int argc, char** argv) { int result = art::dex2oat(argc, argv); ... }
(gdb) p 166+12 $13 = 178 (gdb) x /178wx 0xffbe8580 stack: addr value reg 0xffbe8580 0xffffffff ... 0xffbe8840 0x00000000 0xffbe8840 0x00000000 lr
main函数的 lr 是0x00000000,没有上一层调用了。
此时sp=0xffbe8840+4 = 0xffbe8844
main函数是由exec执行 dex2oat可执行程序调用的,
exec时会传递参数,从此处的sp开始向前推,可以找到exec的参数如下:
0xffbe8e12: "" 0xffbe8e13: "" 0xffbe8e14: "" 0xffbe8e15: "" 0xffbe8e16: "" 0xffbe8e17: "" ---Type <return> to continue, or q <return> to quit--- 0xffbe8e18: "" 0xffbe8e19: "/system/bin/dex2oat" 0xffbe8e2d: "--instruction-set=arm" 0xffbe8e43: "--dex-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes.dex" 0xffbe8e8f: "--dex-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes2.dex" 0xffbe8edc: "--dex-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes3.dex" 0xffbe8f29: "--oat-file=/data/data/com.happyelements.AndroidAnimal.qq/.jiagu/classes.oat" 0xffbe8f75: "LD_PRELOAD=/data/data/com.happyelements.AndroidAnimal.qq/files/libjiagu.so" 0xffbe8fc0: "ANDROID_ROOT=/system" 0xffbe8fd5: "OAT_VERSION=79" 0xffbe8fe4: "/system/bin/dex2oat" 0xffbe8ff8: "" 0xffbe8ff9: "" 0xffbe8ffa: "" 0xffbe8ffb: "" 0xffbe8ffc: "" 0xffbe8ffd: "" 0xffbe8ffe: "" 0xffbe8fff: "" 0xffbe9000: <error: Cannot access memory at address 0xffbe9000>
从参数可以看到,应用调用dex2oat时传递的参数,3个dex文件,以及设置了
LD_PRELOAD=libjiagu.so
使用LD_PRELOAD环境变量是一种hook的方法,libjiagu看来是使用这个方法做的hook;
更多推荐
hook read_chk 导致dex2oat进程 abort
发布评论