两种传参方法
调用约定
决定了到底怎么传参,在C语言写函数定义时,写以下关键词来显示指定调用约定,
如void _fastcall cf330(unsigned m, char *buffer)
指定了约定方式为_fastcall
安排局部变量的方法
保护寄存器的约定
描述子程序的说明
//子程序名(入口标号):BTOHS
//功 能: 把32位二进制数转换为8位十六进制数的ASCII码串
//入口参数:(1)存放ASCII码串缓冲区的首地址(先压入堆栈)
// (2)二进制数据(后压入堆栈)
//出口参数: 无
//其他说明:(1)缓冲区应该足够大(至少9个字节)
// (2)ASCII串以字节0为结束标记
// (3)影响寄存器EAX、ECX、EDX的值
_asm
{
BTOHS: ;子程序入口标号
PUSH EBP
MOV EBP, ESP
PUSH EDI //保护EDI
MOV EDI, [EBP+12]
MOV EDX, [EBP+8]
MOV ECX, 8
NEXT:
ROL EDX, 4
MOV AL, DL
AND AL, 0FH
ADD AL, '0'
CMP AL, '9'
JBE LAB580
ADD AL, 7
LAB580:
MOV [EDI], AL
INC EDI
LOOP NEXT
MOV BYTE PTR [EDI], 0
POP EDI
POP EBP
RET
}
//子程序名(入口标号):ISDIGIT
//功 能:判断字符是否为十进制数字符
//入口参数:AL=字符
//出口参数:如果为非数字符,AL=0;否则AL保持不变
_asm
{
ISDIGIT:
CMP AL, '0' ;与字符'0'比较
JL ISDIG1 ;有效字符是'0'-'9'
CMP AL,'9'
JA ISDIG1
RET
ISDIG1: ;非数字符
XOR AL,AL ; AL= 0
RET
}
//演示调用上述子程序as334和子程序as335
#inclue
int main( )
{
char buff1[16] = "328";
char buff2[16] = "1234024";
unsigned x1, x2;
unsigned sum;
_asm
{
LEA ESI, buff1 ;转换一个字符串
CALL DSTOB
MOV x1, EAX
LEA ESI, buff2 ;转换另一个字符串
CALL DSTOB
MOV x2, EAX
;
MOV EDX, x1 ;求和
ADD EDX, x2
MOV sum, EDX
; ;如这些代码位于前面,
JMP OK ;需要通过该指令来跳过随后的子程序部分!
}
//
//在这里安排子程序DSTOB和ISDIGIT的代码
//
OK:
printf("%d\n", sum);
return 0;
}
名称 | call(段内直接调用指令) |
---|---|
格式 | CALL LABEL |
动作 | 把调用指令下一行指令地址压栈,然后转到LABEL处执行 |
注意 | 除了保存返回地址,其他同无条件转JMP |
名称 | call(段内间接调用指令) |
---|---|
格式 | CALL OPDR |
动作 | 把调用指令下一行指令地址压栈,然后OPDR内容送到EIP,转到OPDR给出偏移地址处执行 |
合法值 | OPDR:保护方式下,32位通用寄存器、双字存储单元 |
注意 | 除了保存返回地址,其他同无条件转JMP |
#include
int subr_addr; //存放子程序入口地址
int valu; //保存结果
int main( )
{
_asm
{
LEA EDX, SUBR2 //取得子程序二的入口地址
MOV subr_addr, EDX //保存到存储单元
LEA EDX, SUBR1 //取得子程序一的入口地址
XOR EAX, EAX //入口参数EAX=0
CALL EDX //调用子程序一(段内间接,32位Reg)
CALL subr_addr //调用子程序二(段内间接,双字存储单元)
MOV valu, EAX
}
printf("valu=%d\n",valu); //显示为valu=28
return 0;
}
//源C程序
#include
int max(int x, int y); //声明函数原型
int min(int x, int y); //
int main()
{
int (*pf)(int,int); //定义指向函数的指针变量
int val1, val2; //存放结果的变量
pf = max; //使得pf指向函数max
val1 = (*pf)(13,15); //调用由pf指向的函数
pf = min; //使得pf指向函数min
val2 = (*pf)(23,25); //调用由pf指向的函数
printf("%d,%d\n",val1,val2); //显示为15,23
return 0;
}
//反编译(不优化)
//标号max_YAHHH、min_YAHHH分别是两个函数入口地址
push ebp
mov ebp, esp ;建立堆栈框架
sub esp, 12 ;安排3个局部变量pf、val1和val2
; pf = max;
mov DWORD PTR [ebp-4], OFFSET max_YAHHH
; val1 = (*pf)(13,15);
push 15
push 13
call DWORD PTR [ebp-4] ;间接调用指针所指的函数max
add esp, 8 ;平衡堆栈
; val1= 返回结果
mov DWORD PTR [ebp-12], eax
; pf = min;
mov DWORD PTR [ebp-4], OFFSET min_YAHHH
; val2 = (*pf)(23,25);
push 25
push 23
call DWORD PTR [ebp-4] ;间接调用指针所指的函数min
add esp, 8
; val2= 返回结果
mov DWORD PTR [ebp-8], eax
mov eax, DWORD PTR [ebp-8] ; eax= val2
push eax
mov ecx, DWORD PTR [ebp-12] ; ecx= val1
push ecx
push OFFSET FORMTS ;格式字符串
call _printf ;段内直接调用
add esp, 12 ;平衡堆栈
;
xor eax, eax ;准备返回值
mov esp, ebp ;撤销局部变量
pop ebp ;撤销堆栈框架
ret
可以看到
按段内段间分
按返回时是否平衡堆栈
名称 | RET(段内返回不带立即数指令) |
---|---|
格式 | RET |
动作 | 指令从堆栈弹出地址偏移,送到指令指针寄存器EIP,返回到call时压栈的返回地址处执行 |
名称 | RET(段内返回带立即数指令) |
---|---|
格式 | RET count |
动作 | 指令从堆栈弹出地址偏移(当然这也会影响esp),送到指令指针寄存器EIP,还额外把count 加到ESP |
注意 | 用于平衡堆栈 |
.asm
文件.com
文件;说明:将十六进制数字符串转为数值(二进制),再转十进制输出查看
segment code ;不分段,所有段共用一片内存空间
org 100H ;从100H开始
MOV AX, CS ;使得数据段与代码段相同
MOV DS, AX ;DS = CS
MOV AX, string ;取到要转换的字符串首地址
PUSH AX
CALL Hex2Bin ;转换
CALL PutWordDec ;显示转换结果
MOV AH, 4CH
INT 21H
;子程序名:Hex2Bin
;功 能:把十六进制字符串转数值
;入口参数:堆栈存字符串起始
;出口参数:ax
Hex2Bin:
PUSH BP
MOV BP, SP ;建立堆栈框架
MOV SI, [BP+4] ;字长16位
MOV CX, -1 ;避免提前结束
XOR AX,AX ;存结果
DEC SI ;方便循环
TOBIN:
INC SI
MOV DL, '$' ;字符串结尾用$标记,DX在MUL的时候会被刷掉,这里要重新赋值
CMP [SI],DL
JE DONE
MOV BX,16 ;乘数16,BX在下面Hex2Bin_WORD的时候会被刷掉,要重新赋值
MUL BX ;AX是被乘数,积的低16位仍在AX
PUSH WORD [SI] ;取一个16进制字符,转值存到BX(这里入栈后面要手动平衡)
CALL Hex2Bin_WORD
ADD SP,2 ;平衡堆栈
ADD AX,BX
;CALL PutWordDec
;CALL PutSpace
LOOP TOBIN
DONE:
POP BP ;撤销堆栈框架
RET
;子程序名:Hex2Bin_WORD
;功 能:把一个十六进制字符转成二进制值
;入口参数:堆栈
;出口参数:BX
Hex2Bin_WORD:
PUSH BP
MOV BP, SP ;建立堆栈框架
MOV BX,[BP+4]
MOV BH,0
CMP BL,'A'
JB NUM
SUB BL,'A'-10
JMP OK
NUM:
SUB BL,'0'
OK:
POP BP ;撤销堆栈框架
RET
;子程序名:PutWordDec
;功 能:把一个字的值转十进制输出
;入口参数:AX
;出口参数:无
PutWordDec:
PUSH BP
MOV BP, SP ;建立堆栈框架
PUSHA ;保护所有reg(关键是AX/BX/CX/DX)
MOV CX, -1
MOV BX,10
LoopPWD1:
XOR DX, DX
DIV BX
PUSH DX
CMP AX, 0
LOOPNE LoopPWD1
NOT CX
LoopPWD2:
POP DX
ADD DL, '0'
CALL PutChar
LOOP LoopPWD2
POPA ;恢复所有reg
POP BP ;撤销堆栈框架
RET
;子程序名:PutChar
;功 能:显示输出一个字符
;入口参数:DL = 显示输出字符ASCII码
;出口参数:无
PutChar:
PUSH AX ;简单的函数,可以不建立堆栈框架
MOV AH,2
INT 21H ;调用2号系统功能显示输出
POP AX
RET
;子程序名:PutSpace
;功 能:显示输出一个空格
;入口参数:无
;出口参数:无
PutSpace:
PUSH AX ;简单的函数,可以不建立堆栈框架
PUSH DX
MOV DL,20H
MOV AH,2
INT 21H ;调用2号系统功能显示输出
POP DX
POP AX
RET
;---------------------------------------------
string db "1234", '$' ;在这里写要转换的十六进制数