From: http://blog.163.com/liuguang_123/blog/static/816701920105262543890/
------------------------------------------------------------------------------------------------
The most important two points to understand the call stack are: The structure of the stack, the role of the EBP register.
The first thing to realize is that there are two of facts:
1, a function call action can be decomposed into: 0 to multiple push instructions (for parameter into the stack), a calling command. The inside of the call command also implies an action to stack the return address (that is, the address of the next instruction in the call instruction).
2. Almost all local compilers will insert a directive similar to the following before each function body: PUSH EBP; MOV EBP ESP;
That is, when the program executes to the real function body of a function, there is already the following data order into the stack: parameter, return address, EBP.
This gives a stack structure similar to the following (the parameter in the stack order is related to the invocation method, here the C language default cdecl for example):
+| (bottom direction, high address) |
| .................... |
| .................... |
| Parameter 3 |
| Parameter 2 |
| Parameter 1 |
| Return Address |
-| Previous level [EBP] | <--------[EBP]
The two instructions for "PUSH ebp" "MOV ebp ESP" are profound: first, the EBP is put into the stack, and then the stack-top pointer ESP is assigned to EBP. The "MOV EBP ESP" directive is ostensibly to cover the original value of EBP with ESP, which is not--because the original EBP value has been stacked (at the top of the stack) before the value is assigned to EBP, and the new EBP points to the top of the stack.
At this point, the EBP register is already in a very important position, the register is stored in the stack of an address (the original EBP into the stack after the top of the stack), from the address as a benchmark, up (the bottom of the stack) can get the return address, parameter values, down (the top of the stack) can get the value of function local The address also stores the EBP value when the previous function call is in place!
In general, Ss:[ebp+4] is the return address, Ss:[ebp+8] is the first parameter value (the last parameter value of the stack, which is assumed to occupy 4 bytes of memory), Ss:[ebp-4] is the first local variable, SS:[EBP] is the previous level of the EBP value.
Since the address in EBP is always "the EBP value at the previous function call", in each layer of the function call, the value of the function local variable can be obtained by "up (the bottom direction) of the return address, the value of the parameter, and down (the top of the stack)."
This forms recursion until it reaches the bottom of the stack. This is the function call stack.
The compiler's use of EBP was too subtle.
From the current EBP, it is very easy to find all EBP layers upward:
unsigned int _ebp;
__asm _EBP, EBP;
while (not stack bottom)
{
//...
_EBP = * (unsigned int*) _ebp;
}
Based on the above principles, we can implement: Copy code
function Getinthex (var A; len:integer): string;//integer turns into hex string
Var
D:pchar;
I:integer;
Begin
Getmem (d, Len * 2);
Bintohex (@a, D, Len);
Result: = ';
For I: = len-1 downto 0 Do
Result: = result + D[i * 2] + d[i * 2 + 1];
Freemem (d);
End
function Printcallstack (): string;
Var
CUREBP, NEXTEBP, Val1, val3:cardinal;
P: ^cardinal;
Begin
Asm
MOV curebp,ebp;//Get current EBP
mov eax,dword ptr ss:[ebp];
MOV nextebp,eax;//on a layer of EBP
End
Val3: = 0;
Result: = ';
Repeat
P: = Pointer (CUREBP + 4);
Val1: = p^; The breakpoint of the previous layer's calling function (Next statement address)
Val3: = Val3 + 1;
Result: = result + ' ================= No. ' + inttostr (val3) + ' ============= ' #13 # #;
Result: = result + ' current EBP: ' + getinthex (CUREBP, SizeOf (CUREBP)) + #13 # #;
Result: = result + ' previous EBP: ' + getinthex (NEXTEBP, SizeOf (NEXTEBP)) + #13 # #;
Result: = result + ' previous breakpoint: ' + Getinthex (val1, SizeOf (val1)) + #13 # #;
P: = Pointer (CUREBP);
CUREBP: = p^;
P: = Pointer (CUREBP);
NEXTEBP: = p^;
Until (NEXTEBP = 0) or (DWORD (p) >= $0012FFFC);//To the top of the stack?
End
In this way, finding the base address of the game's call is no longer a very difficult thing to do.
Analysis of the principle of function call stack and Delphi implementation