ASM16
花指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| MyStack segment stack db 256 dup(?) MyStack ends
MyData segment MY_MSG db "Hello World!",0dh,0ah,'$' MyData ends
MyCode segment MAIN: mov ax,MyData mov ds,ax mov es,ax jmp LABEL1 db 0b8h ;花指令
LABEL1: mov dx,offset MY_MSG mov ah,09h int 21h
mov ax,4c00h int 21h MyCode ends
end MAIN
|
从B8开始下面全部的代码都会出错 但是运行的结果是不变的 只不过反汇编代码分析时会被干扰(实现代码加密)
找到花指令 写入一条机器码 从正确的地方开始分析 即可获得正确的代码
子程序
重复利用代码段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| MyStack segment stack db 256 dup(?) MyStack ends
MyData segment MY_MSG db "Hello World!",0dh,0ah,'$' MyData ends
MyCode segment MAIN: mov ax,MyData mov ds,ax mov es,ax mov bx,offset RETURN1 jmp SHOW_HELLO RETURN1: mov bx,offset RETURN2 jmp SHOW_HELLO RETURN2:
;LABEL1: mov ax,4c00h int 21h ;退出程序 SHOW_HELLO: mov dx,offset MY_MSG mov ah,09h int 21h jmp ax
MyCode ends
end MAIN
|
但是按照这种做法会有局限性jmp ax
跳转回去时若ax的值发生改变则不能使用
修改后(效率太低):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| MyStack segment stack db 256 dup(?) MyStack ends
MyData segment MY_MSG db "Hello World!",0dh,0ah,'$' MyData ends
MyCode segment MAIN: mov ax,MyData mov ds,ax mov es,ax
mov ax,offset RETURN1 push ax jmp SHOW_HELLO RETURN1: ;子程序 mov bx,offset RETURN2 push ax jmp SHOW_HELLO RETURN2:
;LABEL1: mov ax,4c00h int 21h ;退出程序 SHOW_HELLO: mov dx,offset MY_MSG mov ah,09h int 21h pop ax jmp ax
MyCode ends
end MAIN
|
用子程序改一下:
1 2 3 4 5
| mov ax,offset RETURN1 push ax jmp SHOW_HELLO RETURN1: ;子程序 等同于 call SHOW_HELLO
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| MyStack segment stack db 256 dup(?) MyStack ends
MyData segment MY_MSG db "Hello World!",0dh,0ah,'$' MyData ends
MyCode segment MAIN: mov ax,MyData mov ds,ax mov es,ax
;mov ax,offset RETURN1 ;push ax ;jmp SHOW_HELLO ;子程序 call ret ;RETURN1: call SHOW_HELLO
mov ax,4c00h int 21h ;退出程序 SHOW_HELLO: push ax ;保存环境 mov dx,offset MY_MSG mov ah,09h int 21h
pop ax;恢复环境 ;pop ax ;jmp ax 等价于 ret ret
MyCode ends
end MAIN
|
恢复环境:
1 2 3 4
| pop dx: 从堆栈中恢复 dx 寄存器的值。 pop cx: 从堆栈中恢复 cx 寄存器的值。 pop ax: 从堆栈中恢复 ax 寄存器的值。 retn 4: 返回调用者,并清理堆栈中的4个字节(两个参数)。(平衡堆栈)
|
函数调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| MyStack segment stack db 256 dup(?) MyStack ends
MyData segment MY_MSG1 db "Hello World1!",0dh,0ah,'$' MY_MSG2 db "Hello World2!",0dh,0ah,'$' MyData ends
MyCode segment MAIN: mov ax,MyData mov ds,ax mov es,ax
mov ax,09h ;参数2 push ax mov ax,offset MY_MSG1 ;参数1 从右往左边入栈 push ax call SHOW_HELLO ; add sp 4 ;stdcall调用约定
mov ax,09h push ax mov ax,offset MY_MSG2 push ax call SHOW_HELLO ; add sp 4
mov ax,4c00h int 21h
SHOW_HELLO: push ax ;保存环境 push cx push dx
mov bp,sp mov cx,0 mov dx,word ptr [bp+8] ;访问参数1 ;[bp+8]由于前面存在三行push mov ah,byte ptr [bp+10] ;访问参数2 int 21h
pop dx pop cx pop ax ;恢复环境 ; pop ax ; jmp ax ==> pop ip
; pop ax ; add sp,4 ; push ax ; ret retn 4 ;mov ip,[sp] add sp,4 c调用约定
MyCode ends
end MAIN
|
运行结果:
调试
运行完call
指令后,IP
的地址改为call
后面的地址,堆栈中存放了call
指令接下来的指令的地址,还有前面压入栈的两个参数
ax
、cx
、dx
的值被保存到堆栈
从堆栈中取出参数,运行函数得出结果
然后堆栈被平衡,ax
、cx
、dx
的值被还原,程序回到函数调用后要执行的指令的地方