【汇编】 用汇编写了个贪吃蛇
前言软件的逆向分析和破解需要扎实的汇编基础,为了提高自己的汇编水平所以用汇编写了个贪吃蛇.
这份代码也能给有类似想法的人提供一个参考
对于我的代码有任何意见或建议请务必提出来
代码已上传github,但不知道这是否符合版规就不放链接了
实现细节
我代码里除了main函数外所有的函数都遵循了stdcall的调用约定,个人认为这种调用约定比cdecl要方便很多.简直就是为了汇编而生的调用约定
蛇本身使用了一个双向链表,每个表项都描述了当前块的坐标,上一个和下一个块的首地址,布局是:
DWORD X
DWORD Y
DWORD next
DWORD prev
编译
用nasm编译然后用gcc链接
链接方式不唯一,不一定必须要用gcc来链接但nasm是必要的
nasm -f win32 Retro_Snaker.asm -o Retro_Snaker.o
gcc Retro_Snaker.o -o Retro_Snaker.exe
代码:
extern _printf
extern _kbhit
extern _getch
extern _putchar
extern _malloc
extern _free
extern _time
extern _srand
extern _rand
extern _exit
extern _GetStdHandle@4
extern _SetConsoleCursorPosition@8
extern _SetConsoleCursorInfo@8
extern _MessageBoxA@16
extern _Sleep@4
segment .data
pszNewLine db 0x0D, 0x0A, 0
pszScore db "Score:%d", 0
pszTarget db "Target: %d", 0
pszTitle db "Snake", 0
pszFailed db "You lost", 0
pszWon db "You win", 0
segment .bss
AreaX equ 70
AreaY equ 24
UP equ 0
DOWN equ 1
LEFT equ 2
RIGHT equ 3
Target equ 100
pszMapbuffer resb AreaX+1
pxySnakeHead resd 1
pxySnakeTail resd 1
nSnakeLenth resd 1
nxySnakeFoodX resd 1
nxySnakeFoodY resd 1
uniCurDir resw 1 ;0->上; 1->下; 2->左; 3->右
segment .text
global _main
;添加蛇头部
;参数: word x
; word y
_add_snake_head@8:
push ebp
mov ebp, esp
sub esp, 4
mov dword , 16
call _malloc
mov edx,
mov dword , edx ;x
mov edx,
mov dword , edx ;y
mov edx,
mov dword , eax ;prev
mov dword , edx ;next
mov dword , eax ;prev->prev
mov dword , eax ;更新蛇头位置
push '*'
push dword
push dword
call _printchar@12
leave
ret 8
;添加蛇尾部
;参数: dword x
; dword y
_add_snake_tail@8:
push ebp
mov ebp, esp
sub esp, 4
mov dword , 16
call _malloc
mov edx,
mov dword , edx ;x
mov edx,
mov dword , edx ;y
mov dword , 0 ;next
mov ebx,
mov dword , ebx ;prev
mov dword , eax ;prev->next = eax
mov dword , eax
push '*'
push dword
push dword
call _printchar@12
leave
ret 8
;按情况去掉蛇尾
_remove_snake_tail@0:
push ebp
mov ebp, esp
;开始比较蛇尾和蛇尾的上一节的x和y是否相等
;这将决定是否在屏幕上擦除蛇尾
xor cx, cx
mov eax, ;蛇尾首地址
mov ebx, ;蛇尾上一节的首地址
mov edx, ;edx = tail->x
cmp , edx
setne cl
mov edx,
cmp , edx
setne ch
cmp bx, 0
jz LRemoveTail
push ' '
push dword
push dword
call _printchar@12
LRemoveTail:
mov eax, ;eax = tail
mov ebx, ;ebx = tail->prev
mov dword , ebx ;tail = ebx
mov dword , 0 ;ebx->next=0
push eax
call _free
add esp, 4
leave
ret 0
;初始化蛇
_init_snake@0:
push ebp
mov ebp, esp
sub esp, 4
mov word , RIGHT ;初始方向向右
mov dword , 3 ;蛇长度初始为3
;开始初始化蛇头
mov dword , 16
call _malloc
mov dword , eax
mov dword , eax
mov dword , 5 ;x
mov dword , 1 ;y
mov dword , 0 ;next
mov dword , eax ;prev
;打印蛇头
push '*'
push 1
push 5
call _printchar@12
;添加蛇身
mov ecx,
dec ecx
LInitPrintSnake:
push ecx
push 1
mov eax, ecx
add eax, 2
push eax
call _add_snake_tail@8
pop ecx
loop LInitPrintSnake
leave
ret 0
;检测指定坐标是否在蛇身内
;参数: dword x
; dword y
; dword isCheckHead
_is_pos_in_snake@12:
push ebp
mov ebp, esp
mov eax,
cmp dword , 0 ;是否跳过头部检测
je LCheck
mov eax,
LCheck:
xor cx, cx
mov edx, dword
cmp , edx
setne cl
mov edx, dword
cmp , edx
setne ch
cmp cx, 0
je LInBody
cmp dword , 0
je LNotInBody
mov eax,
jmp LCheck
LNotInBody:
mov eax, 0
jmp LIsPosInSnakeEnd
LInBody:
mov eax, 1
LIsPosInSnakeEnd:
leave
ret 12
;检测是否吃到了食物并决定是否增长蛇
_check_food@0:
push ebp
mov ebp, esp
xor dx, dx
mov eax,
mov bx,
cmp bx,
setne dl
mov bx,
cmp bx,
setne dh
cmp dx, 0
jne LCheckFoodEnd
inc dword
call _gen_food@0
mov eax,
push dword
push dword
call _add_snake_tail@8
LCheckFoodEnd:
leave
ret 0
;检测是否赢了
_check_won@0:
push ebp
mov ebp, esp
mov eax, dword
sub eax, 3
cmp eax, Target
jl LFailCheckEnd
call _free_all@0
push 0
push pszTitle
push pszWon
push 0
call _MessageBoxA@16
mov dword , 0
call _exit
LCheckWonEnd:
leave
ret 0
;检测是否已经输了
_check_failed@0:
push ebp
mov ebp, esp
sub esp, 4
mov edx,
;检测蛇头是否碰到了蛇身
push 1 ;跳过头部检测
push dword
push dword
call _is_pos_in_snake@12
cmp eax, 0
je LCheckXZ
jmp LFailed
;下面检测是否碰壁
LCheckXZ:
mov edx,
cmp dword , -1
jne LCheckYZ
jmp LFailed
LCheckYZ:
cmp dword , -1
jne LCheckXE
jmp LFailed
LCheckXE:
cmp dword , AreaX-2
jne LCheckYE
jmp LFailed
LCheckYE:
cmp dword , AreaY-2
jne LFailCheckEnd
jmp LFailed
;下面处理输了的情况
LFailed:
call _free_all@0
push 0
push pszTitle
push pszFailed
push 0
call _MessageBoxA@16
mov dword , 0
call _exit
LFailCheckEnd:
leave
ret 0
;根据按键改变蛇的方向
_get_new_dir@0:
push ebp
mov ebp, esp
call _kbhit
cmp eax, 0
je LGetNewDirEnd
call _getch
cmp eax, 'w'
jne LK_A
cmp word , DOWN
je LGetNewDirEnd ;防止蛇直接掉头
mov word , UP
jmp LGetNewDirEnd
LK_A:
cmp eax, 'a'
jne LK_S
cmp word , RIGHT
je LGetNewDirEnd
mov word , LEFT
jmp LGetNewDirEnd
LK_S:
cmp eax, 's'
jne LK_D
cmp word , UP
je LGetNewDirEnd
mov word , DOWN
jmp LGetNewDirEnd
LK_D:
cmp eax, 'd'
jne LGetNewDirEnd
cmp word , LEFT
je LGetNewDirEnd
mov word , RIGHT
LGetNewDirEnd:
leave
ret 0
;使蛇移动一步
_move_snake@0:
push ebp
mov ebp, esp
sub esp, 8
;保存当前蛇头坐标
mov eax,
mov ebx,
mov dword , ebx ;x
mov ebx,
mov dword , ebx ;y
mov eax,
cmp word , UP
je LUP
cmp word , DOWN
je LDOWN
cmp word , LEFT
je LLEFT
cmp word , RIGHT
je LRIGHT
jmp LDIREND
LUP:
dec dword
jmp LDIREND
LDOWN:
inc dword
jmp LDIREND
LLEFT:
dec dword
jmp LDIREND
LRIGHT:
inc dword
LDIREND:
call _add_snake_head@8
call _remove_snake_tail@0
leave
ret 0
;生成食物
_gen_food@0:
push ebp
mov ebp, esp
cmp dword , (AreaX-2)*(AreaY-2)
jge LGenFoodEnd
LGenFood:
push 0
xor edx, edx
call _rand
mov ebx, AreaY-3
div ebx
mov dword , edx
push edx
xor edx, edx
call _rand
mov ebx, AreaX-3
div ebx
mov dword , edx
push edx
call _is_pos_in_snake@12
cmp eax, 0
jne LGenFood
push '#'
push dword
push dword
call _printchar@12
call _print_score@0
LGenFoodEnd:
leave
ret 0
;基于窗口原点移动光标
;参数: byte x
; byte y
_gotoxy@4:
push ebp
mov ebp, esp
push word
push word
push -11
call _GetStdHandle@4
push eax
call _SetConsoleCursorPosition@8
leave
ret 4
;基于地图原点移动光标
;参数: word x
; word y
_map_gotoxy@8:
push ebp
mov ebp, esp
sub esp, 4
and dword , 0
mov al, byte
inc al
mov byte , al
mov al, byte
inc al
mov byte , al
call _gotoxy@4
leave
ret 8
;基于地图原点打印一个字符
;参数: dword x
; dword y
; dword char
_printchar@12:
push ebp
mov ebp, esp
sub esp, 4
push dword
push dword
call _map_gotoxy@8
mov eax,
mov , eax
call _putchar
leave
ret 12
;打印地图
_printmap@0:
push ebp
mov ebp, esp
sub esp, 8
;定位光标到(0,0)处
push 0
call _gotoxy@4
mov ecx, AreaY
LLines:
push ecx
mov ecx, AreaX
;根据行的位置决定打印什么
cmp dword , 1
je LHeaderLine
cmp dword , AreaY
je LHeaderLine
mov byte , '|'
mov byte , ' '
jmp LPrintLine
LHeaderLine:
mov byte , '+'
mov byte , '-'
;构建一行
LPrintLine:
mov al,
mov byte , al
mov ecx, AreaX-2
LCenter:
mov al, byte
mov byte , al
loop LCenter
mov al, byte
mov byte , al
;打印构建好的行
push pszMapbuffer
call _printf
add esp, 4
;行末尾打印换行
LineEnd:
push pszNewLine
call _printf
add esp, 4
pop ecx
dec ecx
jnz LLines
leave
ret 0
;打印分数
_print_score@0:
push ebp
mov ebp, esp
sub esp, 8
push word AreaX-12
push word AreaY
call _gotoxy@4
mov eax,
sub eax, 3
mov dword , eax
mov dword , pszScore
call _printf
push word AreaX-12
push word AreaY+1
call _gotoxy@4
mov dword , Target
mov dword , pszTarget
call _printf
leave
ret 0
;释放整条蛇,用于游戏结束后的资源释放
_free_all@0:
push ebp
mov ebp, esp
sub esp, 4
mov ebx,
LFreeAll:
mov eax, ebx
mov ebx,
mov dword , eax
call _free
cmp ebx, 0
je LGenFoodEnd
jmp LFreeAll
LFreeAllEnd:
leave
ret 0
_main:
push ebp
mov ebp, esp
sub esp, 32
;隐藏光标
mov dword , 1
mov dword , 0
lea eax,
push eax
push -11
call _GetStdHandle@4
push eax
call _SetConsoleCursorInfo@8
;更新随机数种子
mov dword , 0
call _time
mov dword , eax
call _srand
;初始化
call _printmap@0
call _init_snake@0
call _gen_food@0
;游戏主循环
LGameMainLoop:
call _get_new_dir@0
call _move_snake@0
call _check_food@0
call _check_won@0
call _check_failed@0
push 100
call _Sleep@4
jmp LGameMainLoop
leave
ret 此用户无法显示 发表于 2020-3-6 10:21
!!!还能手撸汇编写游戏!!!想知道要能够破解程序要达到怎样的水平,而且看网上这方面教程少得可怜,楼 ...
这要看你要破的是啥程序了,一些比较简单的你只要稍微有点汇编基础,还会点OD的常用操作就没问题了。一些保护的比较厉害的程序就难说了。论坛里应该有这方面的视频教程,你能吃透的话大多数程序应该是没问题的 longsui48 发表于 2020-3-3 19:33
大佬 有没有好的汇编知道 最近想学这方面的东西
我当初的入门书个人感觉不适合新手,我的第一个汇编程序是跑在MBR上的。。
即便如此我还是告诉你我的入门书吧,《x86 汇编语言:从实模式到保护模式 》
入门之后最好学着看intel的白皮书,上面有任何你想知道的
放GitHub不违规的{:17_1068:} 厉害了老哥 学习了,楼主,赞一个 厉害,学习到东西了。 大佬牛批,太厉害了{:1_921:} 不明觉厉,请允许我把大佬的照片挂起来膜拜。 善哉善哉,老衲要给楼主三十二个赞! 楼主,厉害啊,我也学过汇编写程序,不简单啊