簡體   English   中英

如何將參數傳遞給匯編中的過程?

[英]How to pass parameters to a procedure in assembly?

我有一個C程序從我的匯編文件中調用兩個過程,這些過程的定義如下: extern int myfunc(int a,int b)myfunc2(int c,int d) ,現在在C中執行myfunc調用之后,我可以如下訪問裝配中的參數: b[BP+6]a[BP+4] ,在SMALL MODEL中。 現在我想在我的myfunc從我的匯編文件中調用myfunc2(int c,int d) 如何為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

我的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);
}

您可以通過將值壓入堆棧來設置堆棧。 堆棧機制的優點在於,只要您沒有做任何非常錯誤的事情,將參數傳遞給另一個函數就不會弄亂當前函數的堆棧。

您的問題沒有簡單的答案,因為很多情況取決於所使用的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時,我認為這是合理的。

因為確實沒有一個簡單的答案,所以解決此問題的最佳方法是使用gnu調試器或docs,但是無論如何您最終還是會進入gdb。 一種方法是用C編寫程序,將其反匯編,然后親自查看調用約定是什么。 您可以使用堆棧,也可以像使用64位通常和32位syscall一樣輕松地使用寄存器來傳遞這些簡單值。

//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
unroot@flerb:~/stacko$ gcc -m32 -g cfile.o testc.o -o a.out
unroot@flerb:~/stacko$ ./a.out
unroot@flerb:~/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;

在main斷開時查看堆棧中的內容

(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

只是為了在輸入參數之前快速查看內容

(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,這很明顯,因為堆棧向下增長到較小的地址。 當被調用函數訪問這些參數時,有時會先使用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.

介入功能

(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的偏移量將a加上2(第二個函數壓入堆棧,因此更接近堆棧上的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來調用另一個程序集的匯編程序,則可以根據需要顯式控制這些程序集之間的基本指針。 您可以選擇手動調整它們,然后將其跳轉到第二個函數調用,這將繞過自動調用過程(即推回地址和設置堆棧),這並不完全有用; 否則匯編調用指令將使用相同的過程來初始化被調用函數,並且可以期望偏移量與C函數中的偏移量相同。

x86調用約定上的良好鏈接,包括cdecl和stdcall以及方便的syscall參考,顯示了將寄存器用於函數調用

這是使用寄存器傳遞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
unroot@flerb:~/LearningLinuxBinaryAnalysis_Code$ ld -m elf_i386 -o testasm32 testasm32.o
unroot@flerb:~/LearningLinuxBinaryAnalysis_Code$ ./testasm32
Hello1Hello2

$ echo $?
1

有趣的是,ebx保留了退出syscall的錯誤代碼,因此它返回1,因為ebx在print_string命令中設置為1且未清除。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM