Ghidra函数中的局部变量,不是根据RBP的偏移,而是根据函数开始处RSP的偏移进行标识。
使用csaw18 pwn中的boi程序作为例子,来分析看ghidra对局部变量是如何标识。
Ghidra分析结果
*******************************************************
* FUNCTION *
*******************************************************
undefined main()
undefined AL:1 <RETURN>
undefined8 Stack[-0x10 local_10 XREF[2]: 00400659(W),
004006ca(R)
undefined4 Stack[-0x20 local_20 XREF[1]: 00400677(W)
undefined8 Stack[-0x28 local_28 XREF[1,2]: 0040066f(W),
0040067e(W),
004006a5(R)
undefined8 Stack[-0x30 local_30 XREF[1]: 00400667(W)
undefined8 Stack[-0x38 local_38 XREF[2]: 0040065f(W),
0040068f(*)
undefined4 Stack[-0x3c local_3c XREF[1]: 00400649(W)
undefined8 Stack[-0x48 local_48 XREF[1]: 0040064c(W)
main XREF[5]: Entry Point(*),
_start:0040054d(*),
_start:0040054d(*), 004007b4,
00400868(*)
00400641 PUSH RBP # 入栈8字节
00400642 MOV RBP,RSP
00400645 SUB RSP,0x40
00400649 MOV dword ptr [RBP + local_3c],EDI
0040064c MOV qword ptr [RBP + local_48],RSI
00400650 MOV RAX,qword ptr FS:[0x28]
00400659 MOV qword ptr [RBP + local_10],RAX
0040065d XOR EAX,EAX
0040065f MOV qword ptr [RBP + local_38],0x0
00400667 MOV qword ptr [RBP + local_30],0x0
0040066f MOV qword ptr [RBP + local_28],0x0
00400677 MOV dword ptr [RBP + local_20],0x0
0040067e MOV dword ptr [RBP + local_28+0x4],0xdeadbeef
00400685 MOV EDI=>s_Are_you_a_big_boiiiii??_00400764,s = "Are you a big boiiiii??"
0040068a CALL puts int puts(char * __s)
0040068f LEA RAX=>local_38,[RBP + -0x30] # 此处 RAX=>local_38 是什么意思?
00400693 MOV EDX,0x18
00400698 MOV RSI,RAX
0040069b MOV EDI,0x0
004006a0 CALL read ssize_t read(int __fd, void
004006a5 MOV EAX,dword ptr [RBP + local_28+0x4]
004006a8 CMP EAX,0xcaf3baee
004006ad JNZ LAB_004006bb
004006af MOV EDI=>s_/bin/bash_0040077c,s_/bin/bash_004 = "/bin/bash"
004006b4 CALL run_cmd undefined run_cmd()
004006b9 JMP LAB_004006c5
LAB_004006bb XREF[1]: 004006ad(j)
004006bb MOV EDI=>s_/bin/date_00400786,s_/bin/date_004 = "/bin/date"
004006c0 CALL run_cmd undefined run_cmd()
LAB_004006c5 XREF[1]: 004006b9(j)
004006c5 MOV EAX,0x0
004006ca MOV RCX,qword ptr [RBP + local_10]
004006ce XOR RCX,qword ptr FS:[0x28]
004006d7 JZ LAB_004006de
004006d9 CALL __stack_chk_fail undefined __stack_chk_fail()
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
LAB_004006de XREF[1]: 004006d7(j)
004006de LEAVE
004006df RET
从最上面可以看到,ghidra函数中的局部变量,直接是根据STACK进行标识,是根据函数开始处RSP的偏移。
当看到这行LEA RAX=>local_38,[RBP + -0x30]时,是获取RBP-0x30的地址,在想和local_38有什么关系?
从最上面Stack[-0x38 local_38看到,local_38 = 起始RSP-0x38
由PUSH RBP MOV RBP,RSP可知,RBP=起始RSP - 0x8,那RBP-0x30 = 起始RSP-0x8-0x30=起始RSP-0x38=local_38
所以,RAX获取到的是local_38的地址。
IDA的分析结果
IDA的结果:
text:0000000000400641 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400641 public main
.text:0000000000400641 main proc near ; DATA XREF: _start+1D↑o
.text:0000000000400641
.text:0000000000400641 var_40 = qword ptr -40h
.text:0000000000400641 var_34 = dword ptr -34h
.text:0000000000400641 buf = qword ptr -30h
.text:0000000000400641 var_28 = qword ptr -28h
.text:0000000000400641 var_20 = qword ptr -20h
.text:0000000000400641 var_18 = dword ptr -18h
.text:0000000000400641 var_8 = qword ptr -8
.text:0000000000400641
.text:0000000000400641 ; __unwind {
.text:0000000000400641 push rbp
.text:0000000000400642 mov rbp, rsp
.text:0000000000400645 sub rsp, 40h
.text:0000000000400649 mov [rbp+var_34], edi
.text:000000000040064C mov [rbp+var_40], rsi
.text:0000000000400650 mov rax, fs:28h
.text:0000000000400659 mov [rbp+var_8], rax
.text:000000000040065D xor eax, eax
.text:000000000040065F mov [rbp+buf], 0
.text:0000000000400667 mov [rbp+var_28], 0
.text:000000000040066F mov [rbp+var_20], 0
.text:0000000000400677 mov [rbp+var_18], 0
.text:000000000040067E mov dword ptr [rbp+var_20+4], 0DEADBEEFh
.text:0000000000400685 mov edi, offset s ; "Are you a big boiiiii??"
.text:000000000040068A call _puts
.text:000000000040068F lea rax, [rbp+buf]
.text:0000000000400693 mov edx, 18h ; nbytes
.text:0000000000400698 mov rsi, rax ; buf
.text:000000000040069B mov edi, 0 ; fd
.text:00000000004006A0 call _read
.text:00000000004006A5 mov eax, dword ptr [rbp+var_20+4]
.text:00000000004006A8 cmp eax, 0CAF3BAEEh
.text:00000000004006AD jnz short loc_4006BB
.text:00000000004006AF mov edi, offset aBinBash ; "/bin/bash"
.text:00000000004006B4 call run_cmd
.text:00000000004006B9 jmp short loc_4006C5
.text:00000000004006BB ; ---------------------------------------------------------------------------
.text:00000000004006BB
.text:00000000004006BB loc_4006BB: ; CODE XREF: main+6C↑j
.text:00000000004006BB mov edi, offset aBinDate ; "/bin/date"
.text:00000000004006C0 call run_cmd
.text:00000000004006C5
.text:00000000004006C5 loc_4006C5: ; CODE XREF: main+78↑j
.text:00000000004006C5 mov eax, 0
.text:00000000004006CA mov rcx, [rbp+var_8]
.text:00000000004006CE xor rcx, fs:28h
.text:00000000004006D7 jz short locret_4006DE
.text:00000000004006D9 call ___stack_chk_fail
对于IDA来说,局部变量是针对RBP进行偏移的,RBP是函数调用中的栈帧的基址,并且在函数返回前不会改动,所以比较好理解。
Ghidra的问题
IDA上0x00400649地址处汇编为mov [rbp+var_34], edi,根据之前的RBP=起始RSP-0x8,转化成起始RSP-0x8-0x34即起始RSP-0x3C
而该地址对应的是local_3c,但是ghidra上却是RBP + local_3c,因为local_3c的地址想对于RBP是0x34,所以ghidra使用MOV dword ptr [local_3c],EDI会比较合适。
在github上,看到了为什么ghidra会选择以stack作为偏移。链接
关闭ghidra局部变量引用
这种可以关闭,通过开始菜单上的Edit –> Tool Options –> Listing Fields –> Operands Field,将Markup Stack Variable References勾去掉就行。
效果如下:
关闭后效果会更好,可以直接知道每行汇编代码是针对那个局部变量进行处理,并且针对RBP的偏移也有展示。