2017-06-24 34 views
0

我有一个调用从我的汇编文件一两个程序一个C程序,该程序是这样定义的extern int myfunc(int a,int b)myfunc2(int c,int d),现在经过用C myfunc电话,我可以访问该参数在装配这样的:b[BP+6]a[BP+4]这是在小型模型。 现在我想拨打myfunc2(int c,int d),但是从我的汇编文件中,而我在myfunc。 如何设置myfunc2的堆栈并传递参数? 并且它会搞砸myfunc的当前堆栈,如果是的话我该如何处理? MY大会文件:如何将参数传递给程序集?

.MODEL SMALL 
.STACK 100h 
.DATA 
.CODE 
PUBLIC _myfunc 
PUBLIC _myfunc2 
_myfunc PROC NEAR 
.386 
PUSH BP 
MOV BP,SP 
;here i need to do myfun2(1,2) 
POP BP 
RET 
_myfunc ENDP 

_myfunc2 PROC NEAR 
.386 
PUSH BP 
MOV BP,SP 
MOV DX,[BP+6];get d 
MOV AX,[BP+4];get c 
ADD AX,DX;add them up 
;the return value will be in AX 
POP BP 
RET 
_myfunc2 ENDP 

END 

我的C文件:

#include <stdio.h> 
#include <stdlib.h> 

extern int myfunc(int a,int b); 
extern int myfunc2(int c,int d); 
int main() 
{ 
    int res; 
    res=myfunc(int a,int b); 
} 
+1

不要描述你的代码....显示它。 –

+0

请显示您的尝试。 – TomServo

+0

通过调用'myfunc2'在C中编写一个虚拟'myfunc'函数,并查看由C编译器生成的汇编代码。 –

回答

2

你推值设置到它设置堆栈。堆栈机制的美妙之处在于,将参数传递给另一个函数将而不是搞乱了当前函数的堆栈,前提是您不会做任何严重错误。

有没有简单的回答你的问题,因为很多依赖于ABI(应用程序二进制接口)使用,在调用你的函数的约定,(是cdecl?)等

最安全的方法是让C编译器生成C代码的汇编输出,然后按照原样进行。但在一般情况下,它看起来就像这样:以上

push ax   ; pass int c parameter (assuming int is 16-bit) 
push dx   ; pass int d parameter (assuming int is 16-bit) 
call _myfunc2 ; invoke the function 
add sp, 4  ; clean up stack (assuming cdecl calling convention) 

假设一个int是16位的,这是我觉得当我听到你说的MODEL SMALL合理。

+0

你能向我解释为什么你添加4到sp? – user3133165

+0

不要紧,我现在明白它的作用,就像打电话之后为ax和dx做POP一样,谢谢。 – user3133165

+0

是的,确切地说。 –

0

因为确实没有一个简单的答案,所以最好的方法是使用gnu调试器或者文档,但是最终你会在gdb中结束。一种方法是用C编写程序,反汇编它们,然后亲自查看调用约定。您可以使用堆栈,您可以轻松使用寄存器来传递这些简单的值,如64位典型值和32位系统调用一样。

//testc.c 
int func2(int c, int d) 
{ 
    return c-d; 
} 


int func(int a, int b) 
{ 
    a+=2; 
    b++; 
    func2(a,b); 

} 


//cfile.c 

#include <stdio.h> 
#include <stdlib.h> 

extern int func(int a,int b); 
extern int func2(int c,int d); 
int main() 
{ 
    int res; 
    int b = 4; 
    int c = 3; 
    res=func(b, c); 
} 

编译

$ gcc -m32 -g -c testc.c 
[email protected]:~/stacko$ gcc -m32 -g cfile.o testc.o -o a.out 
[email protected]:~/stacko$ ./a.out 
[email protected]:~/stacko$ echo $? 
0 

在gdb中

$ gdb -q a.out 
Reading symbols from a.out...done. 
(gdb) set listsize 50 
(gdb) list 0 
1 #include <stdio.h> 
2 #include <stdlib.h> 
3 
4 extern int func(int a,int b); 
5 extern int func2(int c,int d); 
6 int main() 
7 { 
8  int res; 
9  int b = 4; 
10  int c = 3; 
11  res=func(b, c); 
12 } 


(gdb) break main 
Breakpoint 1 at 0x57c: file cfile.c, line 9. 
(gdb) run 
Starting program: /home/unroot/stacko/a.out 

Breakpoint 1, main() at cfile.c:9 
9  int b = 4; 

纵观栈的内容,当我们在主

(gdb) x/20x $esp 
0xffffd290: 0x00000001 0xffffd354 0xffffd35c 0x56555611 
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276 
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276 
0xffffd2c0: 0x00000001 0xffffd354 0xffffd35c 0x00000000 
0xffffd2d0: 0x00000000 0x00000000 0xf7fad000 0xf7ffdc04 

正在breaked只是有一个快速在参数前查看内容ERS在堆栈的内容上

(gdb) x/20x $esp-0x10 
0xffffd280: 0x00000003 0x56557000 0x00000001 0x56555577 
0xffffd290: 0x00000001 0xffffd354 0xffffd35c 0x56555611 
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276 
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276 
0xffffd2c0: 0x00000001 0xffffd354 0xffffd35c 0x00000000 
(gdb) disas 
Dump of assembler code for function main: 
    0x56555560 <+0>: lea ecx,[esp+0x4] 
    0x56555564 <+4>: and esp,0xfffffff0 
    0x56555567 <+7>: push DWORD PTR [ecx-0x4] 
    0x5655556a <+10>: push ebp 
    0x5655556b <+11>: mov ebp,esp 
    0x5655556d <+13>: push ebx 
    0x5655556e <+14>: push ecx 
    0x5655556f <+15>: sub esp,0x10 
    0x56555572 <+18>: call 0x565555af <__x86.get_pc_thunk.ax> 
    0x56555577 <+23>: add eax,0x1a89 
=> 0x5655557c <+28>: mov DWORD PTR [ebp-0xc],0x4 
    0x56555583 <+35>: mov DWORD PTR [ebp-0x10],0x3 
    0x5655558a <+42>: sub esp,0x8 
    0x5655558d <+45>: push DWORD PTR [ebp-0x10] 
    0x56555590 <+48>: push DWORD PTR [ebp-0xc] 
    0x56555593 <+51>: mov ebx,eax 
    0x56555595 <+53>: call 0x565555c8 <func> 
    0x5655559a <+58>: add esp,0x10 
    0x5655559d <+61>: mov DWORD PTR [ebp-0x14],eax 
    0x565555a0 <+64>: mov eax,0x0 
    0x565555a5 <+69>: lea esp,[ebp-0x8] 
    0x565555a8 <+72>: pop ecx 
    0x565555a9 <+73>: pop ebx 
    0x565555aa <+74>: pop ebp 
    0x565555ab <+75>: lea esp,[ecx-0x4] 
    0x565555ae <+78>: ret  
End of assembler dump. 
(gdb) break *0x56555595 
Breakpoint 2 at 0x56555595: file cfile.c, line 11. 
(gdb) cont 
Continuing. 

查找推现在,C = 3被推上,然后B = 4(注意我只这样为了便于撰写本,值被推上是纯粹的值并具有与代表他们,一旦它们在栈上的变量)

Breakpoint 2, 0x56555595 in main() at cfile.c:11 
11  res=func(b, c); 
(gdb) x/20x $esp 
0xffffd280: 0x00000004 0x00000003 0x00000001 0x56555577 
0xffffd290: 0x00000001 0xffffd354 0x00000003 0x00000004 
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276 
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276 
0xffffd2c0: 0x00000001 0xffffd354 0xffffd35c 0x00000000 
(gdb) 

所以参数至FUNC呼叫以相反的顺序被推向没有连接,推3然后推4,显然,因为堆栈向下生长到更小的地址。当被调用的函数有时访问这些参数时,它会首先弹出4个,然后使用esp弹出4个单独的寄存器,或者,如下面的反汇编所示,被调用的函数可以通过来自ebp的指针访问它们。

(gdb) disas 
Dump of assembler code for function main: 
    0x56555560 <+0>: lea ecx,[esp+0x4] 
    0x56555564 <+4>: and esp,0xfffffff0 
    0x56555567 <+7>: push DWORD PTR [ecx-0x4] 
    0x5655556a <+10>: push ebp 
    0x5655556b <+11>: mov ebp,esp 
    0x5655556d <+13>: push ebx 
    0x5655556e <+14>: push ecx 
    0x5655556f <+15>: sub esp,0x10 
    0x56555572 <+18>: call 0x565555af <__x86.get_pc_thunk.ax> 
    0x56555577 <+23>: add eax,0x1a89 
    0x5655557c <+28>: mov DWORD PTR [ebp-0xc],0x4 
    0x56555583 <+35>: mov DWORD PTR [ebp-0x10],0x3 
    0x5655558a <+42>: sub esp,0x8 
    0x5655558d <+45>: push DWORD PTR [ebp-0x10] 
    0x56555590 <+48>: push DWORD PTR [ebp-0xc] 
    0x56555593 <+51>: mov ebx,eax 
=> 0x56555595 <+53>: call 0x565555c8 <func> 
    0x5655559a <+58>: add esp,0x10 
    0x5655559d <+61>: mov DWORD PTR [ebp-0x14],eax 
    0x565555a0 <+64>: mov eax,0x0 
    0x565555a5 <+69>: lea esp,[ebp-0x8] 
    0x565555a8 <+72>: pop ecx 
    0x565555a9 <+73>: pop ebx 
    0x565555aa <+74>: pop ebp 
    0x565555ab <+75>: lea esp,[ecx-0x4] 
    0x565555ae <+78>: ret  
End of assembler dump. 

步进到FUNC

(gdb) stepi 
func (a=4, b=3) at testc.c:9 
9 { 
(gdb) disas 
Dump of assembler code for function func: 
=> 0x565555c8 <+0>: push ebp 
    0x565555c9 <+1>: mov ebp,esp 
    0x565555cb <+3>: call 0x565555af <__x86.get_pc_thunk.ax> 
    0x565555d0 <+8>: add eax,0x1a30 
    0x565555d5 <+13>: add DWORD PTR [ebp+0x8],0x2 
    0x565555d9 <+17>: add DWORD PTR [ebp+0xc],0x1 
    0x565555dd <+21>: push DWORD PTR [ebp+0xc] 
    0x565555e0 <+24>: push DWORD PTR [ebp+0x8] 
    0x565555e3 <+27>: call 0x565555b3 <func2> 
    0x565555e8 <+32>: add esp,0x8 
    0x565555eb <+35>: nop 
    0x565555ec <+36>: leave 
    0x565555ed <+37>: ret  
End of assembler dump. 

的C调用函数推的返回值压入堆栈没有任何明显的暗示,这是要做到这一点,但它的存在占用空间

(gdb) x/20x $esp 
0xffffd27c: 0x5655559a 0x00000004 0x00000003 0x00000001 
0xffffd28c: 0x56555577 0x00000001 0xffffd354 0x00000003 
0xffffd29c: 0x00000004 0xffffd2c0 0x00000000 0x00000000 
0xffffd2ac: 0xf7e12276 0x00000001 0xf7fad000 0x00000000 
0xffffd2bc: 0xf7e12276 0x00000001 0xffffd354 0xffffd35c 

然后我们进一步介绍func的实质,其中使用ebp的偏移量将2加到a(第二个函数被推入堆栈,因此更接近堆栈上的ebp)并增加1到B这只是它

(gdb) step 
10  a+=2; 

(gdb) print/x $ebp 
$1 = 0xffffd278 

(gdb) x/20x $ebp 
0xffffd278: 0xffffd2a8 0x5655559a 0x00000004 0x00000003 
0xffffd288: 0x00000001 0x56555577 0x00000001 0xffffd354 
0xffffd298: 0x00000003 0x00000004 0xffffd2c0 0x00000000 
0xffffd2a8: 0x00000000 0xf7e12276 0x00000001 0xf7fad000 
0xffffd2b8: 0x00000000 0xf7e12276 0x00000001 0xffffd354 


(gdb) disas 
Dump of assembler code for function func: 
    0x565555c8 <+0>: push ebp 
    0x565555c9 <+1>: mov ebp,esp 
    0x565555cb <+3>: call 0x565555af <__x86.get_pc_thunk.ax> 
    0x565555d0 <+8>: add eax,0x1a30 
=> 0x565555d5 <+13>: add DWORD PTR [ebp+0x8],0x2  //a+=2 
    0x565555d9 <+17>: add DWORD PTR [ebp+0xc],0x1  //b++ 
    0x565555dd <+21>: push DWORD PTR [ebp+0xc]  //push b 
    0x565555e0 <+24>: push DWORD PTR [ebp+0x8]  //push a 
    0x565555e3 <+27>: call 0x565555b3 <func2> 
    0x565555e8 <+32>: add esp,0x8 
    0x565555eb <+35>: nop 
    0x565555ec <+36>: leave 
    0x565555ed <+37>: ret  
End of assembler dump. 

打破在通话,并再次

(gdb) break *0x565555e3 
Breakpoint 3 at 0x565555e3: file testc.c, line 12. 
(gdb) cont 
Continuing. 

Breakpoint 3, 0x565555e3 in func (a=6, b=4) at testc.c:12 
12  func2(a,b); 
(gdb) disas 
Dump of assembler code for function func: 
    0x565555c8 <+0>: push ebp 
    0x565555c9 <+1>: mov ebp,esp 
    0x565555cb <+3>: call 0x565555af <__x86.get_pc_thunk.ax> 
    0x565555d0 <+8>: add eax,0x1a30 
    0x565555d5 <+13>: add DWORD PTR [ebp+0x8],0x2 
    0x565555d9 <+17>: add DWORD PTR [ebp+0xc],0x1 
    0x565555dd <+21>: push DWORD PTR [ebp+0xc] 
    0x565555e0 <+24>: push DWORD PTR [ebp+0x8] 
=> 0x565555e3 <+27>: call 0x565555b3 <func2> 
    0x565555e8 <+32>: add esp,0x8 
    0x565555eb <+35>: nop 
    0x565555ec <+36>: leave 
    0x565555ed <+37>: ret 

(gdb) x/20x $esp 
0xffffd270: 0x00000006 0x00000004 0xffffd2a8 0x5655559a 
0xffffd280: 0x00000006 0x00000004 0x00000001 0x56555577 
0xffffd290: 0x00000001 0xffffd354 0x00000003 0x00000004 
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276 
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276 

Again our variables have been pushed on the stack, again in reverse order, as always, but you could control this too in assembly if you feel like being difficult. 

(gdb) stepi 
func2 (c=6, d=4) at testc.c:3 
3 { 
(gdb) disas 
Dump of assembler code for function func2: 
=> 0x565555b3 <+0>: push ebp 
    0x565555b4 <+1>: mov ebp,esp 
    0x565555b6 <+3>: call 0x565555af <__x86.get_pc_thunk.ax> 
    0x565555bb <+8>: add eax,0x1a45 
    0x565555c0 <+13>: mov eax,DWORD PTR [ebp+0x8] 
    0x565555c3 <+16>: sub eax,DWORD PTR [ebp+0xc] 
    0x565555c6 <+19>: pop ebp 
    0x565555c7 <+20>: ret  
End of assembler dump. 

Again the call pushes the return address onto the stack before transfering control to the called function. 
(gdb) x/20x $esp 
0xffffd26c: 0x565555e8 0x00000006 0x00000004 0xffffd2a8 
0xffffd27c: 0x5655559a 0x00000006 0x00000004 0x00000001 
0xffffd28c: 0x56555577 0x00000001 0xffffd354 0x00000003 
0xffffd29c: 0x00000004 0xffffd2c0 0x00000000 0x00000000 
0xffffd2ac: 0xf7e12276 0x00000001 0xf7fad000 0x00000000 

检查组继续在被调用函数的实质突破前推。

(gdb) break *0x565555c0 
Breakpoint 4 at 0x565555c0: file testc.c, line 4. 
(gdb) cont 
Continuing. 


Breakpoint 4, func2 (c=6, d=4) at testc.c:4 
4  return c-d; 


(gdb) disas 
Dump of assembler code for function func2: 
    0x565555b3 <+0>: push ebp 
    0x565555b4 <+1>: mov ebp,esp 
    0x565555b6 <+3>: call 0x565555af <__x86.get_pc_thunk.ax> 
    0x565555bb <+8>: add eax,0x1a45 
=> 0x565555c0 <+13>: mov eax,DWORD PTR [ebp+0x8] 
    0x565555c3 <+16>: sub eax,DWORD PTR [ebp+0xc] 
    0x565555c6 <+19>: pop ebp 
    0x565555c7 <+20>: ret  
End of assembler dump. 

检查在FUNC2的物质,其中该变量被访问堆栈和减法发生的情况:

(gdb) x/20x $esp 
0xffffd268: 0xffffd278 0x565555e8 0x00000006 0x00000004 
0xffffd278: 0xffffd2a8 0x5655559a 0x00000006 0x00000004 
0xffffd288: 0x00000001 0x56555577 0x00000001 0xffffd354 
0xffffd298: 0x00000003 0x00000004 0xffffd2c0 0x00000000 
0xffffd2a8: 0x00000000 0xf7e12276 0x00000001 0xf7fad000 
(gdb) x/20x $ebp 
0xffffd268: 0xffffd278 0x565555e8 0x00000006 0x00000004 
0xffffd278: 0xffffd2a8 0x5655559a 0x00000006 0x00000004 
0xffffd288: 0x00000001 0x56555577 0x00000001 0xffffd354 
0xffffd298: 0x00000003 0x00000004 0xffffd2c0 0x00000000 
0xffffd2a8: 0x00000000 0xf7e12276 0x00000001 0xf7fad000 

因此,这里[EBP + 0x8中] = 6和[EBP +位于0xC] = 4并用eax寄存器中的减法指令修改这些值,并将结果返回到eax寄存器。

C的默认约定是让调用者在将控制权传递给被调用者之前先推送返回地址,并让被调用者调整堆栈和基本指针,但是当您调用自己的指针时可以做任何事情功能并返回到您自己的功能。在这里我用C程序来说明C的功能,但是如果你使用C来调用一个调用另一个程序集的程序集,那么你可以根据需要显式控制这些程序集之间的基指针。您可以选择手动调整它们,并将jmp调整到第二个函数调用,这将绕过推送返回地址和设置堆栈的自动调用过程,但并不完全有用;或者程序集调用指令将使用相同的过程来初始化调用的函数,并且您可以预期偏移量与C函数中的偏移量相同。

Good link on x86 Call conventions including cdecl and stdcallA handy syscall reference showing use of registers for function calls

这是使用寄存器传递变量在32位,没有必要堆的一个简单的例子。 //testasm32.asm .text段 全球_start

_start: 
    mov ecx, hello1 
    call print_string 
    mov ecx, hello2 
    call print_string 

    mov eax, 1 
    int 0x80 

print_string: 
    mov eax, 4 
    mov ebx, 1 
    mov edx, 6 
    int 0x80 
    ret 

section .data 
    hello1 db "Hello1" 
    hello2 db "Hello2" 

$ nasm -f elf32 testasm32.asm 
[email protected]:~/LearningLinuxBinaryAnalysis_Code$ ld -m elf_i386 -o testasm32 testasm32.o 
[email protected]:~/LearningLinuxBinaryAnalysis_Code$ ./testasm32 
Hello1Hello2 

$ echo $? 
1 

有趣的是EBX适用于出口系统调用的错误代码,是因为EBX是在print_string命令设置为1,并没有被清除,它返回1。

相关问题