- This is not an error from Ghidra. The values are just a naming convention, and the real instructions are correctly disassembled.
- Ghidra assigns variable names based on the function entry point, and displays offsets based on that.
- It seems Ghidra behaviour is like this to have a universal way to assign names, independently from the compiler.
As pointed by R4444, Ghidra shows variable offsets relative to the
entry stack-pointer and are not
Herein, Ghidra assigns variable names based on
ESP (or corresponding stack pointer) at the time the function is entered, without considering the coming
PUSH EBP, basically following this:
0019FF58 $-C 0019FF58 00000002 LOCAL 2
0019FF5C $-8 0019FF5C 00000001 LOCAL 1
0019FF60 $-4 0019FF60 0019FF80 will store the OLD EBP
0019FF64 $ ==> 0019FF64 | 00401025 return to layout.00401025 from layout.sub_40102C
0019FF68 $+4 0019FF68 | 00000041 PARAM 1
0019FF6C $+8 0019FF6C | 0000BABE PARAM 2
0019FF70 $+C 0019FF70 | 0000CAFE PARAM 3
This is how Ghidra obtained the values:
Stack[0x4] -> param_1
Stack[0x8] -> param_2
Stack[0xc] -> param_3
Stack[-0x8] -> local_8
Stack[-0xc] -> local_c
It must be considered that this is just a variable naming, the actual instruction is addressing the data with the correct offset. If we navigate to one of the offensive instructions, we can see that Ghidra provides the correct instruction at the bottom right corner, in this case,
EBP-4 for the named variable
This default Ghidra behavior can be modified permanently via:
Edit > Tool Options > Listing Fields > Operands Field > Markup Stack Variable References, and then Ghidra will display:
undefined4 Stack[0x4]:4 param_1 XREF: 00401040 (R)
undefined4 Stack[0x8]:4 param_2 XREF: 00401043 (R)
undefined4 Stack[0xc]:4 param_3 XREF: 00401046 (R)
undefined4 Stack[-0x8]:4 local_8 XREF: 00401032 (W)
undefined4 Stack[-0xc]:4 local_c XREF: 00401039 (W)
00401032 C745FC01000000 MOV dword ptr [EBP + -0x4 ]=> local_8 ,0x1
00401039 C745F802000000 MOV dword ptr [EBP + -0x8 ]=> local_c ,0x2
00401040 8B5D08 MOV EBX ,dword ptr [EBP + 0x8 ]=> param_1
00401043 8B4D0C MOV ECX ,dword ptr [EBP + 0xc ]=> param_2
00401046 FF7510 PUSH dword ptr [EBP + 0x10 ]=> param_3
This is the REASON for the values mismatch and the HOW the values are obtained, but WHY is Ghidra naming variables based on the function entry? @emteere explains that:
The choice of stack variable offsets based on the frame variable versus stack variables based on the stack pointer can cause some confusion. What it allows is ignoring the stack frame variable and just creating references to the stack wherever they occur however they occur. There are many examples of the stack pointer loaded into alternate registers without a frame, so a universal base of the stack at entry seemed like a good choice and less confusing when there is and isn't a frame pointer in two different functions. When debug information is loaded the conversion to SP at entry needs to be done. In addition, many compilers have gotten rid of the use of a stack frame register all together.
So, I imagine that the explanation is that one normally would wish to have frame-based variable naming, like seen in IDA, at least for some of the most extended architectures/compilers. However, Ghidra names variables with a general policy, and they decided to harmonise behaviour of different architectures/compilers by offseting variables based on the stack-pointer at the time the function is entered.