C 如何将参数传递给程序集中的过程?
我有一个C程序,它从我的汇编文件中调用两个过程,这些过程的定义如下:C 如何将参数传递给程序集中的过程?,c,assembly,x86-16,C,Assembly,X86 16,我有一个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堆栈,如果是,我该如何处理? 我
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
堆栈,如果是,我该如何处理?
我的程序集文件:
.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
#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);
}
我的C文件:
.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
#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);
}
#包括
#包括
外部int myfunc(int a,int b);
外部int myfunc2(int c,int d);
int main()
{
国际关系;
res=myfunc(int a,int b);
}
通过将值推送到堆栈上来设置堆栈。堆栈机制的美妙之处在于,将参数传递给另一个函数不会弄乱当前函数的堆栈,前提是您没有做任何严重错误的事情
您的问题没有简单的答案,因为很大程度上取决于正在使用的ABI(应用程序二进制接口),取决于您函数的调用约定(是cdecl
?),等等
最安全的方法是让您的C编译器生成C代码的汇编输出,然后按照它所做的去做。但总的来说,它看起来是这样的:
push-ax;传递int c参数(假设int为16位)
推送dx;传递int d参数(假设int为16位)
调用_myfunc2;调用函数
添加sp,4;清理堆栈(假设cdecl调用约定)
上面假设一个
int
是16位的,当我听到你提到modelsmal
时,我认为这是合理的,你可以通过将值推到堆栈上来设置堆栈。堆栈机制的美妙之处在于,将参数传递给另一个函数不会弄乱当前函数的堆栈,前提是您没有做任何严重错误的事情
您的问题没有简单的答案,因为很大程度上取决于正在使用的ABI(应用程序二进制接口),取决于您函数的调用约定(是cdecl
?),等等
最安全的方法是让您的C编译器生成C代码的汇编输出,然后按照它所做的去做。但总的来说,它看起来是这样的:
push-ax;传递int c参数(假设int为16位)
推送dx;传递int d参数(假设int为16位)
调用_myfunc2;调用函数
添加sp,4;清理堆栈(假设cdecl调用约定)
上面假设一个
int
是16位的,当我听到你提到modelsmal
时,我认为这是合理的,因为确实没有一个简单的答案,最好的方法是使用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);
}
在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-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.
因此func调用的参数按相反的顺序推送,推送3然后推送4,这很明显,因为堆栈向下扩展到更小的地址。当被调用函数访问这些参数时,有时会使用esp先弹出4个关闭,然后弹出3个到单独的寄存器中,或者,如下面的反汇编所示,被调用函数可以通过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的实质部分,其中使用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.
因此这里的[ebp+0x8]=6和[ebp+0xc]=4,使用eax寄存器中的减法指令修改值,并将结果返回到eax寄存器
C的默认约定是,在将控制权转移给被调用方之前,让调用方推送返回地址,并让被调用方调整堆栈和基指针,但在调用自己的函数并返回到自己的函数时,可以执行任何操作。这里我用C程序来说明C的功能,但是如果你用C来调用一个调用另一个汇编程序的汇编程序,那么如果你需要的话,你可以显式地控制这些汇编程序之间的基指针。您可以选择手动调整它们,并将jmp调整到第二个函数调用,这将绕过推送返回地址和设置堆栈的自动调用过程,这并不完全有用;或者汇编调用指令将使用相同的过程初始化被调用的函数,并且可以预期偏移量与C函数中的偏移量相同
和
这是一个使用寄存器以32位形式传递变量的简单示例,不需要堆栈。
//testasm32.asm
第节.案文
全球启动
_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
unroot@flerb:~/LearningLinuxBinaryAnalysis_Code$ ld -m elf_i386 -o testasm32 testasm32.o
unroot@flerb:~/LearningLinuxBinaryAnalysis_Code$ ./testasm32
Hello1Hello2
$ echo $?
1
有趣的是,ebx保存了exit系统调用的错误代码,因此它返回1,因为ebx在print_string命令中被设置为1,并且没有被清除。因为确实没有简单的答案,所以最好的方法是使用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);
}
在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;
只是要快速查看一下PAR前的内容。
(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.
(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
_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
unroot@flerb:~/LearningLinuxBinaryAnalysis_Code$ ld -m elf_i386 -o testasm32 testasm32.o
unroot@flerb:~/LearningLinuxBinaryAnalysis_Code$ ./testasm32
Hello1Hello2
$ echo $?
1