汇编语言的主体是汇编指令。汇编指令和机器指令的差别在于指令的表示方法上。汇编指令是机器指令便于记忆的书写格式。
#操作:寄存器BX的内容送到AX中
1000100111011000 #机器指令
mov ax,bx #汇编指令
汇编语言语句的语法 汇编语言语句输入每行一个语句。每个语句如下的格式如下:
[label] mnemonic [operands] [;comment]
方括号中的字段是可选的。基本指令有两部分组成,第一个是要执行的指令(助记符)的名称和所述第二命令的操作数或参数的。
以下是一些典型的汇编语言语句的例子:
INC COUNT ; Increment the memory variable COUNT
MOV TOTAL, 48 ; Transfer the value 48 in the
; memory variable TOTAL
ADD AH, BH ; Add the content of the
; BH register into the AH register
AND MASK1, 128 ; Perform AND operation on the
; variable MASK1 and 128
ADD MARKS, 10 ; Add 10 to the variable MARKS
MOV AL, 10 ; Transfer the value 10 to the AL register
IDAPro is best disassembler tool for many processors and file types. HexRays ARM - plugin for IDAPro (doesn’t work separately), which trying to decompile assembler to C-like source code
both not free
https://www.hex-rays.com/index.shtml
首先查看如下关键信息进行方向确定
“signal”: “11 (SIGSEGV)”,
“code”: “2 (SEGV_ACCERR)”,
“fault addr”: “0x15049024”,
“backtrace”: “#00 pc 0017c126 /system/lib/libart.so (_ZN3art2gc9allocator8RosAlloc8BulkFreeEPNS_6ThreadEPPvj+149)\n”,
“java stacktrace”: [ “at dalvik.system.VMRuntime.runHeapTasks(Native method)”,
rax 0000000000000000 rbx 0000743b3f05f200 rcx 0b8b8de9d74c4547 rdx 0000743b50a00000
r8 ffffffffffffffe0 r9 0000000000000000 r10 0000000000000022 r11 0000000000000206
r12 0000743b3ff4c410 r13 0000743b5c2451d0 r14 0000743b3ff4c630 r15 0000743b3ff4c630
rdi 0000000000000003 rsi 0000743b50b6f940
rbp 0000743b3ff4c110 rsp 0000743b3ff4c100 rip 0000743b569942df
backtrace:
#00 pc 000000000000c2df /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_4+31)
#01 pc 000000000000c315 /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_3+21)
#02 pc 000000000000c345 /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_2+21)
#03 pc 000000000000c371 /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_1+17)
#04 pc 000000000000c412 /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_crash+130)
#05 pc 0000000000267f23 /data/app/xcrash.sample-1/oat/x86_64/base.odex
int xc_test_call_4(int v)
{
int *a = NULL;
xc_test_set_abort_msg();
*a = v; // crash!
(*a)++;
v = *a;
return v;
}
000000000000c2c0 <xc_test_call_4>:
c2c0: 55 push %rbp
c2c1: 48 89 e5 mov %rsp,%rbp
c2c4: 48 83 ec 10 sub $0x10,%rsp
c2c8: 89 7d fc mov %edi,-0x4(%rbp)
c2cb: 48 c7 45 f0 00 00 00 movq $0x0,-0x10(%rbp)
c2d2: 00
c2d3: e8 38 02 00 00 callq c510 <xc_test_set_abort_msg>
c2d8: 8b 7d fc mov -0x4(%rbp),%edi
c2db: 48 8b 45 f0 mov -0x10(%rbp),%rax
c2df: 89 38 mov %edi,(%rax) //尝试从rax中提取数据时,由于地址为0报错
c2e1: 48 8b 45 f0 mov -0x10(%rbp),%rax
c2e5: 8b 38 mov (%rax),%edi
c2e7: 83 c7 01 add $0x1,%edi
tombstone->
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4798
r0 00004780 r1 00000780 r2 00000060 r3 e0cbc018
r4 e71f01d0 r5 db53b4b0 r6 db53b4a8 r7 00000000
r8 e71f01d4 r9 e71efa10 r10 e7cd94ec r11 e16ff848
ip b9839850 sp e16ff830 lr b951a62c pc b9519f10
backtrace:
#00 pc 000a3f10 /data/data/com.test/files/testapp
stack:
e16ff7f0 00000000
e16ff7f4 00000000
e16ff7f8 00000002
e16ff7fc ffffffff
e16ff800 e0c8f1c0 [anon:libc_malloc]
e16ff804 e16ff8a8 <anonymous:e1603000>
e16ff808 e0c83640 [anon:libc_malloc]
e16ff80c 0000c50b
e16ff810 e71e6900 [anon:libc_malloc]
e16ff814 00000000
e16ff818 e71efa17 [anon:libc_malloc]
e16ff81c e71efa18 [anon:libc_malloc]
e16ff820 dfea3014 [anon:libc_malloc]
e16ff824 00000000
e16ff828 acb0ac7c
e16ff82c 00000000
#00 e16ff830 00000000
e16ff834 b98c1694 <anonymous:b983a000>
e16ff838 00400014
e16ff83c 00004000
e16ff840 00004780
e16ff844 80140000 //fp-4,所以fp-2是0x8014(注意这里是小端字节序)
e16ff848 e16ff8b8 <anonymous:e1603000> //fp地址
e16ff84c b951a62c /data/data/com.test/files/testapp
#代码示例,做了简化,方便理解
struct entry *testGetEntryById(unsigned short Id)
{
...
blockId = Id >> 9;
blockOffset = Id & (1 << 9 - 1);
block = g_Table.EntryBlock[blockId];//问题出在这一行
if (block == NULL)
{
return NULL;
}
entry = &block->Entry[blockOffset];
if (!entry->Valid)
{
return NULL;
}
...
}
objdump-->
000a3e54 <testGetEntryById>:
a3e54: e92d4800 push {fp, lr}
a3e58: e1a0b00d mov fp, sp
a3e5c: e24dd018 sub sp, sp, #24
a3e60: e59f10d0 ldr r1, [pc, #208] ; a3f38 <testGetEntryById+0xe4>
a3e64: e08f1001 add r1, pc, r1
a3e68: e2811004 add r1, r1, #4
a3e6c: e14b00b2 strh r0, [fp, #-2] //fp-2位置存放了入参Id
...//省略
a3f08: e50b0008 str r0, [fp, #-8]
a3f0c: e51b0008 ldr r0, [fp, #-8]
a3f10: e5900018 ldr r0, [r0, #24] //问题发生位置
#第一列对应tombstone文件中的pc值,也就是so文件的相对地址
#在tombstone中可以找到fp(即r11)为e16ff848
#在stack中可以找到fp-2的值为0x8014。
#Id = 32788(对应16进制数0x8014), 右移9bit后是64,即blockId是64。但是g_Table.EntryBlock[]数组大小为64, index范围 0~63。64超出数组范围,造成访问溢出,取到了错误的block地址。从而导致的最终的问题发生。
https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/siginfo.h#L223
/*
* SIGSEGV si_codes
*/
#define SEGV_MAPERR 1 /* address not mapped to object */
#define SEGV_ACCERR 2 /* invalid permissions for mapped object */
#define SEGV_BNDERR 3 /* failed address bound checks */
#ifdef __ia64__
# define __SEGV_PSTKOVF 4 /* paragraph stack overflow */
#else
# define SEGV_PKUERR 4 /* failed protection key checks */
#endif
#define SEGV_ACCADI 5 /* ADI not enabled for mapped object */
#define SEGV_ADIDERR 6 /* Disrupting MCD error */
#define SEGV_ADIPERR 7 /* Precise MCD exception */
#define SEGV_MTEAERR 8 /* Asynchronous ARM MTE error */
#define SEGV_MTESERR 9 /* Synchronous ARM MTE exception */
#define NSIGSEGV 9
###