Consider the code:
#include <stdio.h>
#include <stdlib.h>
int update (int *arr, int size);
#define SIZE 10
int main() { // <---------------------- Breakpoint 1
int x[SIZE];
// Initialize array
for (int c = 0 ; c < SIZE ; c++) {
x[c] = c * 2;
}
// Do some random updates to an array
update((int*) &x, SIZE);
// Print the elements
for (int c = 0 ; c < SIZE ; c++) {
printf("%d\n", x[c]);
}
return EXIT_SUCCESS;
} // <----------------------Breakpoint 2
int update (int *arr, int size) {
for (int i = 0 ; i < size ; i++) {
arr[i] += i;
update(arr+i, size-1);
}
return 1;
}
Result of running info frame at breakpoint 1:
Stack level 0, frame at 0x7ffc176b2610:
rip = 0x56434b0d76b8 in main (array.c:12); saved rip = 0x7f8190fb92b1
source language c.
Arglist at 0x7ffc176b2600, args:
Locals at 0x7ffc176b2600, Previous frame's sp is 0x7ffc176b2610
Saved registers:
rbp at 0x7ffc176b2600, rip at 0x7ffc176b2608
Result of running info frame at breakpoint 2:
Stack level 0, frame at 0x7ffc176b2610:
rip = 0x56434b0d771a in main (array.c:24); saved rip = 0x2d28490fd6501
source language c.
Arglist at 0x7ffc176b2600, args:
Locals at 0x7ffc176b2600, Previous frame's sp is 0x7ffc176b2610
Saved registers:
rbp at 0x7ffc176b2600, rip at 0x7ffc176b2608
We see that main()'s saved return address rip at 0x7ffc176b2608 is mutated from 0x7f8190fb92b1 to 0x2d28490fd6501 between the two breakpoints.
However, setting a watchpoint on the address of rip with watch * 0x7ffc176b2608 and running the executable anew does not pause execution between the breakpoints as expected.
How can this be?
-----------EDIT-----------
Output of disassemble /s main:
Dump of assembler code for function main:
array.c:
8 int main() {
0x000056434b0d76b0 <+0>: push rbp
0x000056434b0d76b1 <+1>: mov rbp,rsp
0x000056434b0d76b4 <+4>: sub rsp,0x30
9 int x[SIZE];
10
11 // Initialize array
12 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d76b8 <+8>: mov DWORD PTR [rbp-0x4],0x0
0x000056434b0d76bf <+15>: jmp 0x56434b0d76d4 <main+36>
13 x[c] = c * 2;
0x000056434b0d76c1 <+17>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d76c4 <+20>: lea edx,[rax+rax*1]
0x000056434b0d76c7 <+23>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d76ca <+26>: cdqe
0x000056434b0d76cc <+28>: mov DWORD PTR [rbp+rax*4-0x30],edx
12 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d76d0 <+32>: add DWORD PTR [rbp-0x4],0x1
0x000056434b0d76d4 <+36>: cmp DWORD PTR [rbp-0x4],0x9
0x000056434b0d76d8 <+40>: jle 0x56434b0d76c1 <main+17>
14 }
15
16 // Do some random updates to an array
17 update((int*) &x, SIZE);
0x000056434b0d76da <+42>: lea rax,[rbp-0x30]
0x000056434b0d76de <+46>: mov esi,0xa
0x000056434b0d76e3 <+51>: mov rdi,rax
0x000056434b0d76e6 <+54>: call 0x56434b0d7721 <update>
18
19 // Print the elements
20 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d76eb <+59>: mov DWORD PTR [rbp-0x8],0x0
0x000056434b0d76f2 <+66>: jmp 0x56434b0d7714 <main+100>
21 printf("%d\n", x[c]);
0x000056434b0d76f4 <+68>: mov eax,DWORD PTR [rbp-0x8]
0x000056434b0d76f7 <+71>: cdqe
0x000056434b0d76f9 <+73>: mov eax,DWORD PTR [rbp+rax*4-0x30]
0x000056434b0d76fd <+77>: mov esi,eax
0x000056434b0d76ff <+79>: lea rdi,[rip+0x12e] # 0x56434b0d7834
0x000056434b0d7706 <+86>: mov eax,0x0
0x000056434b0d770b <+91>: call 0x56434b0d7560 <printf#plt>
20 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d7710 <+96>: add DWORD PTR [rbp-0x8],0x1
0x000056434b0d7714 <+100>: cmp DWORD PTR [rbp-0x8],0x9
0x000056434b0d7718 <+104>: jle 0x56434b0d76f4 <main+68>
22 }
23
24 return EXIT_SUCCESS;
=> 0x000056434b0d771a <+106>: mov eax,0x0
25 }
0x000056434b0d771f <+111>: leave
0x000056434b0d7720 <+112>: ret
End of assembler dump.
Output of disassemble /s update:
Dump of assembler code for function update:
array.c:
27 int update (int *arr, int size) {
0x000056434b0d7721 <+0>: push rbp
0x000056434b0d7722 <+1>: mov rbp,rsp
0x000056434b0d7725 <+4>: sub rsp,0x20
0x000056434b0d7729 <+8>: mov QWORD PTR [rbp-0x18],rdi
0x000056434b0d772d <+12>: mov DWORD PTR [rbp-0x1c],esi
28 for (int i = 0 ; i < size ; i++) {
0x000056434b0d7730 <+15>: mov DWORD PTR [rbp-0x4],0x0
0x000056434b0d7737 <+22>: jmp 0x56434b0d7793 <update+114>
29 arr[i] += i;
0x000056434b0d7739 <+24>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d773c <+27>: cdqe
0x000056434b0d773e <+29>: lea rdx,[rax*4+0x0]
0x000056434b0d7746 <+37>: mov rax,QWORD PTR [rbp-0x18]
0x000056434b0d774a <+41>: add rax,rdx
0x000056434b0d774d <+44>: mov edx,DWORD PTR [rbp-0x4]
0x000056434b0d7750 <+47>: movsxd rdx,edx
0x000056434b0d7753 <+50>: lea rcx,[rdx*4+0x0]
0x000056434b0d775b <+58>: mov rdx,QWORD PTR [rbp-0x18]
0x000056434b0d775f <+62>: add rdx,rcx
0x000056434b0d7762 <+65>: mov ecx,DWORD PTR [rdx]
0x000056434b0d7764 <+67>: mov edx,DWORD PTR [rbp-0x4]
0x000056434b0d7767 <+70>: add edx,ecx
0x000056434b0d7769 <+72>: mov DWORD PTR [rax],edx
30 update(arr+i, size-1);
0x000056434b0d776b <+74>: mov eax,DWORD PTR [rbp-0x1c]
0x000056434b0d776e <+77>: lea edx,[rax-0x1]
0x000056434b0d7771 <+80>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d7774 <+83>: cdqe
0x000056434b0d7776 <+85>: lea rcx,[rax*4+0x0]
0x000056434b0d777e <+93>: mov rax,QWORD PTR [rbp-0x18]
0x000056434b0d7782 <+97>: add rax,rcx
0x000056434b0d7785 <+100>: mov esi,edx
0x000056434b0d7787 <+102>: mov rdi,rax
0x000056434b0d778a <+105>: call 0x56434b0d7721 <update>
28 for (int i = 0 ; i < size ; i++) {
0x000056434b0d778f <+110>: add DWORD PTR [rbp-0x4],0x1
0x000056434b0d7793 <+114>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d7796 <+117>: cmp eax,DWORD PTR [rbp-0x1c]
0x000056434b0d7799 <+120>: jl 0x56434b0d7739 <update+24>
31 }
32 return 1;
0x000056434b0d779b <+122>: mov eax,0x1
33 }
0x000056434b0d77a0 <+127>: leave
0x000056434b0d77a1 <+128>: ret
End of assembler dump.
Contents of ~/.gdbinit
# Security
set auto-load safe-path /
# Misc
set disassembly-flavor intel
set disable-randomization off
set pagination off
set follow-fork-mode child
# History
set history filename ~/.gdbhistory
set history save
set history expansion
disp/10i $pc
handle SIGXCPU SIG33 SIG35 SIGPWR nostop noprint
set tui enable
It is likely that this line in your .gdbinit is the source of your troubles:
set disable-randomization off
By default, GDB disables address space layout randomization (ASLR). That means that the binary under GDB starts at exactly the same address, with exactly the same stack pointer every time it runs. This is on by default precisely so you can set a watchpoints and breakpoints on a given address, and have it fire on each run.
By setting disable-randomization off, you are asking GDB to run your binary the same way it would run outside of GDB, i.e. with ASLR enabled. Now the location of stack variables (and globals for a PIE binary that you have) will change from run to run, and setting a watchpoint on a given stack address will only work randomly and rarely.
You can confirm that that is the cause by issuing info frame and run several times. You'll observe that the location where registers are saved changes between runs.
TL;DR: Don't put settings you don't completely understand into your .gdbinit.
the problem with the code is the clobbering of the stack.
The comment by #Mark Plotnick clarifies the problem and includes a suggestion on how to fix it.
Related
Consider this code:
int a = 2;
int b = 5;
int c = 7;
When compiling it stores them in the order I assign them:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 2 ; int a = 2
mov DWORD PTR [rbp-8], 5 ; int b = 5
mov DWORD PTR [rbp-12], 7 ; int c = 7
...
But if I decide to print the address of b, now b switched with c their addresses:
int a = 2;
int b = 5;
int c = 7;
printf("%d\n", &b);
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 2 ; int a = 2
mov DWORD PTR [rbp-12], 5 ; int b = 5
mov DWORD PTR [rbp-8], 7 ; int c = 7
...
So why do they switch addresses? By the way, I'm using gcc 12.2.
The Compiler Explorer uses gcc 12.2 and does not show this behaviour.
I tried compiling this code myself and gdb shows the "correct" order.
#include "stdio.h"
int main()
{
int a=2;
int b=5;
int c=7;
printf("%d", b);
return 0;
}
gdb output of "disassemble main":
Dump of assembler code for function main:
0x0000000000401126 <+0>: push rbp
0x0000000000401127 <+1>: mov rbp,rsp
0x000000000040112a <+4>: sub rsp,0x10
0x000000000040112e <+8>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000401135 <+15>: mov DWORD PTR [rbp-0x8],0x5
0x000000000040113c <+22>: mov DWORD PTR [rbp-0xc],0x7
0x0000000000401143 <+29>: mov eax,DWORD PTR [rbp-0x8]
0x0000000000401146 <+32>: mov esi,eax
0x0000000000401148 <+34>: mov edi,0x402010
0x000000000040114d <+39>: mov eax,0x0
0x0000000000401152 <+44>: call 0x401030 <printf#plt>
0x0000000000401157 <+49>: mov eax,0x0
0x000000000040115c <+54>: leave
0x000000000040115d <+55>: ret
End of assembler dump.
pleasse i need someone to assit me with my assighnment.
q1. Examine the code below and Draw the stack frame after analysing the assembly code when function1, function2 and function3 are called by the main program for a 32-bit system. Figure 2 shows a sample stack frame of a function.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int function1(int x, int y, int z)
{
int result_func1;
result_func1 = x + y + z;
return result_func1;
}
int function2(int x, int y, char* input_string)
{
int result_func2;
char buffer[20];
strcpy(buffer, input_string);
printf("your input string %s is copied in the buffer \n", input_string);
result_func2= x - y;
return result_func2;
}
void function3(int result1, int result2)
{
printf("The result of function 1 is %d\n", result1);
printf("The result of function 1 is %d\n", result1);
}
void function4(void)
{
printf("The function never gets called is \n");
exit(-1);
}
int main(int argc, char* argv[])
{
int result1;
int result2;
result1 = function1(5, 10, 15);
result2 = function2(20, 8, argv[1]);
function3(result1, result1);
}
assembly code of the after the analysis:
0x00005555555552c0 <+0>: endbr64
0x00005555555552c4 <+4>: push %rbp
0x00005555555552c5 <+5>: mov %rsp,%rbp
0x00005555555552c8 <+8>: sub $0x20,%rsp
0x00005555555552cc <+12>: mov %edi,-0x14(%rbp)
0x00005555555552cf <+15>: mov %rsi,-0x20(%rbp)
0x00005555555552d3 <+19>: mov $0xf,%edx
0x00005555555552d8 <+24>: mov $0xa,%esi
0x00005555555552dd <+29>: mov $0x5,%edi
0x00005555555552e2 <+34>: callq 0x5555555551c9 <function1>
0x00005555555552e7 <+39>: mov %eax,-0x8(%rbp)
0x00005555555552ea <+42>: mov -0x20(%rbp),%rax
0x00005555555552ee <+46>: add $0x8,%rax
0x00005555555552f2 <+50>: mov (%rax),%rax
0x00005555555552f5 <+53>: mov %rax,%rdx
0x00005555555552f8 <+56>: mov $0x8,%esi
0x00005555555552fd <+61>: mov $0x14,%edi
0x0000555555555302 <+66>: callq 0x5555555551ef <function2>
0x0000555555555307 <+71>: mov %eax,-0x4(%rbp)
0x000055555555530a <+74>: mov -0x8(%rbp),%edx
0x000055555555530d <+77>: mov -0x8(%rbp),%eax
0x0000555555555310 <+80>: mov %edx,%esi
0x0000555555555312 <+82>: mov %eax,%edi
0x0000555555555314 <+84>: callq 0x555555555261 <function3>
0x0000555555555319 <+89>: mov $0x0,%eax
0x000055555555531e <+94>: leaveq
0x000055555555531f <+95>: retq
modidied(actual 32bit assembly code)
function1:
push ebp
mov ebp, esp
mov eax, DWORD PTR [ebp+12]
add eax, DWORD PTR [ebp+8]
add eax, DWORD PTR [ebp+16]
pop ebp
ret
.LC0:
.string "your input string %s is copied in the buffer \n"
function2:
push ebp
mov ebp, esp
push ebx
lea eax, [ebp-28]
sub esp, 44
mov ebx, DWORD PTR [ebp+16]
push ebx
push eax
call strcpy
pop eax
pop edx
push ebx
push OFFSET FLAT:.LC0
call printf
mov eax, DWORD PTR [ebp+8]
mov ebx, DWORD PTR [ebp-4]
sub eax, DWORD PTR [ebp+12]
leave
ret
.LC1:
.string "The result of function 1 is %d\n"
function3:
push ebp
mov ebp, esp
push ebx
sub esp, 12
mov ebx, DWORD PTR [ebp+8]
push ebx
push OFFSET FLAT:.LC1
call printf
mov DWORD PTR [ebp+12], ebx
add esp, 16
mov ebx, DWORD PTR [ebp-4]
mov DWORD PTR [ebp+8], OFFSET FLAT:.LC1
leave
jmp printf
.LC2:
.string "The function never gets called is "
function4:
push ebp
mov ebp, esp
sub esp, 20
push OFFSET FLAT:.LC2
call puts
mov DWORD PTR [esp], -1
call exit
main:
lea ecx, [esp+4]
and esp, -16
push DWORD PTR [ecx-4]
push ebp
mov ebp, esp
push ecx
sub esp, 8
mov eax, DWORD PTR [ecx+4]
push DWORD PTR [eax+4]
push 8
push 20
call function2
pop edx
pop ecx
push 30
push 30
call function3
mov ecx, DWORD PTR [ebp-4]
xor eax, eax
leave
lea esp, [ecx-4]
ret
please i need someone who can assist me with who to start
I have this C code that is disassembled (AT&T) and I have some confusion with two things. The first, my understanding is that EBP-4 should be the first local variable (here, int i) on the stack. I is clearly in EBP-8 here. Why is this?
Second, is it necessary to move values into registers before performing arithmetic operations on them? (this is an x86 32 bit machine) example:
0x08048402 <+21>: mov 0x8(%ebp),%eax //move parameter a into eax
0x08048405 <+24>: add %eax,-0x4(%ebp) //r += a
Why cant this be :
0x08048405 <+24>: add 0x8(%ebp),-0x4(%ebp) //r += a
C code:
int loop_w (int a, int b){
int i = 0;
int r = a;
while ( i < 256){
r += a;
a -= b;
i += b;
}
return r;
Disassembly:
Dump of assembler code for function loop_w:
0x080483ed <+0>: push %ebp
0x080483ee <+1>: mov %esp,%ebp
0x080483f0 <+3>: sub $0x10,%esp
---------------Above is for stack setup-----------------
0x080483f3 <+6>: movl $0x0,-0x8(%ebp) //I=0
0x080483fa <+13>: mov 0x8(%ebp),%eax //move parameter a into eax
0x080483fd <+16>: mov %eax,-0x4(%ebp) //move a into local var (r=a)
0x08048400 <+19>: jmp 0x8048414 <loop_w+39> //start while loop
0x08048402 <+21>: mov 0x8(%ebp),%eax //move parameter a into eax
0x08048405 <+24>: add %eax,-0x4(%ebp) //r += a
0x08048408 <+27>: mov 0xc(%ebp),%eax //move parameter b into eax
0x0804840b <+30>: sub %eax,0x8(%ebp) //a += parameter b
0x0804840e <+33>: mov 0xc(%ebp),%eax //move parameter b into eax
0x08048411 <+36>: add %eax,-0x8(%ebp) //i+=b
0x08048414 <+39>: cmpl $0xff,-0x8(%ebp) //compare i to 256
0x0804841b <+46>: jle 0x8048402 <loop_w+21> //continue loop if failed condition
=> 0x0804841d <+48>: mov -0x4(%ebp),%eax //move r into eax
0x08048420 <+51>: leave
0x08048421 <+52>: ret //return eax
End of assembler dump.
The compiler can decide wherever it wants to place the local variable. It might have placed it at %ebp - 8 for alignment reasons.
For your second question - Do variables have to be loaded into registers before operation, depends on the operation and the instruction set provided by the architecture.
You mentioned x86. So particular to this architecture, X86 doesn't allow instructions with two memory operands (yes there are a few exceptions).
You can search per instruction basis to know what kind of operands they allow.
I'm trying to rewrite ret adress from function foo() to skip first instruction after leaving function foo(). I'm following Smash stack for fun or profit and Hacking by Jon Erickson. Here is simple code where I want to skip "b = 2;" instruction, so in b should be stored value 1.
Compiled in gcc, settings: -fno-stack-protector -z execstack -g
Also I have turned off ASLR.
#include <stdio.h>
int foo() {
int c = 0;
int *ptr;
ptr = (int*) 0x00007fffffffdf38;
// Second instruction after leaving from foo(). I also tried to skip all instructions before call printf function.
*ptr = 0x0000000000400577;
return 0;
}
int main()
{
int b = 1;
foo();
b = 2;
printf("b = %d\n", b);
return 0;
}
Output of this program is: "b = 2".
How I assumed where is stored RET instruction:
(gdb) disass main
...
0x000000000040056b <+20>: call 0x40052d <foo>
0x0000000000400570 <+25>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>: mov esi,eax
...
(gdb) break 4 // set breakpoint 1 in function foo before int *ptr;
(gdb) break 7 // set breakpoint 2 in function foo after rewriting RET for checking
(gdb) run
(gdb) x/8x &c // "x/8x $esp" doesn't work, it says: "Cannot access memory at address 0xffffffffffffdf30", so I'm looking into memory from &c
0x7fffffffdf2c: 0x00000000 0xffffdf50 0x00007fff 0x00400570
0x7fffffffdf3c: 0x00000000 0xffffe030 0x00007fff 0x00000000
// simple math tell me, it should be:
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x70 0x05 0x40 0x00 0x00 0x00 0x00 0x00
This is how I have found the RET address, it is at 0x7fffffffdf38. To this place I'm putting address of next instruction - 0x0000000000400577.
However, computer still didn't skip instruction b = 2, even when RET is successfully rewrited.
I checked it for confirmation if it really replaced the RET address:
(gdb) c
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x77 0x05 0x40 0x00 0x00 0x00 0x00 0x00
So RET address is really rewrited but when program leaves from function foo() it jumps to original address 0x0000000000400570 wich I want to skip...
It should be simple, find where is stored ret adress and then put to this place other adress. What am I doing wrong?
Thanks for every answer.
For specifying the questing I'm adding dissasembled functions:
Dump of assembler code for function foo:
0x000000000040052d <+0>: push rbp
0x000000000040052e <+1>: mov rbp,rsp
0x0000000000400531 <+4>: mov DWORD PTR [rbp-0x4],0x0
0x0000000000400538 <+11>: movabs rax,0x7fffffffdf38
0x0000000000400542 <+21>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400546 <+25>: mov rax,QWORD PTR [rbp-0x10]
0x000000000040054a <+29>: mov DWORD PTR [rax],0x400577
0x0000000000400550 <+35>: mov eax,0x0
0x0000000000400555 <+40>: pop rbp
0x0000000000400556 <+41>: ret
End of assembler dump.
(gdb) disass main
Dump of assembler code for function main:
0x0000000000400557 <+0>: push rbp
0x0000000000400558 <+1>: mov rbp,rsp
0x000000000040055b <+4>: sub rsp,0x10
0x000000000040055f <+8>: mov DWORD PTR [rbp-0x4],0x1
0x0000000000400566 <+15>: mov eax,0x0
0x000000000040056b <+20>: call 0x40052d <foo>
0x0000000000400570 <+25>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>: mov esi,eax
0x000000000040057c <+37>: mov edi,0x400624
0x0000000000400581 <+42>: mov eax,0x0
0x0000000000400586 <+47>: call 0x400410 <printf#plt>
0x000000000040058b <+52>: mov eax,0x0
0x0000000000400590 <+57>: leave
0x0000000000400591 <+58>: ret
When I run this in gdb debugger it works. So the code is written good, question solved. The problem is somewhere else...
Function in c:
PHPAPI char *php_pcre_replace(char *regex, int regex_len,
char *subject, int subject_len,
zval *replace_val, int is_callable_replace,
int *result_len, int limit, int *replace_count TSRMLS_DC)
{
pcre_cache_entry *pce; /* Compiled regular expression */
/* Compile regex or get it from cache. */
if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) {
return NULL;
}
return php_pcre_replace_impl(pce, subject, subject_len, replace_val,
is_callable_replace, result_len, limit, replace_count TSRMLS_CC);
}
Its assembly:
php5ts!php_pcre_replace:
1015db70 8b442408 mov eax,dword ptr [esp+8]
1015db74 8b4c2404 mov ecx,dword ptr [esp+4]
1015db78 56 push esi
1015db79 8b74242c mov esi,dword ptr [esp+2Ch]
1015db7d 56 push esi
1015db7e 50 push eax
1015db7f 51 push ecx
1015db80 e8cbeaffff call php5ts!pcre_get_compiled_regex_cache (1015c650)
1015db85 83c40c add esp,0Ch
1015db88 85c0 test eax,eax
1015db8a 7502 jne php5ts!php_pcre_replace+0x1e (1015db8e)
php5ts!php_pcre_replace+0x1c:
1015db8c 5e pop esi
1015db8d c3 ret
php5ts!php_pcre_replace+0x1e:
1015db8e 8b542428 mov edx,dword ptr [esp+28h]
1015db92 8b4c2424 mov ecx,dword ptr [esp+24h]
1015db96 56 push esi
1015db97 52 push edx
1015db98 8b542428 mov edx,dword ptr [esp+28h]
1015db9c 51 push ecx
1015db9d 8b4c2428 mov ecx,dword ptr [esp+28h]
1015dba1 52 push edx
1015dba2 8b542428 mov edx,dword ptr [esp+28h]
1015dba6 51 push ecx
1015dba7 8b4c2428 mov ecx,dword ptr [esp+28h]
1015dbab 52 push edx
1015dbac 8b542428 mov edx,dword ptr [esp+28h]
1015dbb0 51 push ecx
1015dbb1 52 push edx
1015dbb2 50 push eax
1015dbb3 e808000000 call php5ts!php_pcre_replace_impl (1015dbc0)
1015dbb8 83c424 add esp,24h
1015dbbb 5e pop esi
1015dbbc c3 ret
As we can see that pcre_get_compiled_regex_cache takes 2 parameters,but why 3 parameters are pushed into the stack?
1015db7d 56 push esi
1015db7e 50 push eax
1015db7f 51 push ecx
1015db80 e8cbeaffff call php5ts!pcre_get_compiled_regex_cache (1015c650)
I guess the TSRMLS_DC and TSRMLS_CC macros contain some hidden extra parameters. A quick google showed up these macros in PHP programming as global state data. It makes sense, the macro in the function declaration must have a parameter which is at [esp+02ch] on the stack - the tenth parameter - you have nine already, and is passed as the first value on the stack (values are pushed right to left), followed by regex_len and then regex.