首页
实用工具
我的旅程
在线壁纸
更多
✒️ 问题反馈
📦 文章统计
🌍 国内镜像
🎬 次元视界
📒 流水账本
🎨 在线 PS
推荐
🕵️ 开源情报
🌆 图片压缩
🍭 资产清洗
💡 我的作品
👤 关于站长
⚔️ 次 元 剑
搜索
1
【工具分享】逆向工具箱 - 次元剑
89,538 阅读
2
【技术分享】PE文件结构分析 ( RVA转FOA )
7,350 阅读
3
【技术分享】NASM x86 Assembly Language
5,982 阅读
4
【技术分享】CK竞技之王游戏辅助制作
3,725 阅读
5
【每日随记】天涯明月刀无限飞修改思路
2,816 阅读
技术分享
CTF解题
英语笔记
数学笔记
网络通信
每日随记
攻防技术
工具分享
Search
标签搜索
Windows
Web安全
Python3
Linux
逆向工程
CTF
红队技术
人工智能
C/C++
黑客工具
Go
密码学
二进制安全
数学
漏洞挖掘
Android
eNSP
渗透测试
蓝队技术
黑客大会
发光的神
累计撰写
160
篇文章
累计收到
103
条评论
首页
栏目
技术分享
CTF解题
英语笔记
数学笔记
网络通信
每日随记
攻防技术
工具分享
页面
实用工具
我的旅程
在线壁纸
✒️ 问题反馈
📦 文章统计
🌍 国内镜像
🎬 次元视界
📒 流水账本
🎨 在线 PS
推荐
🕵️ 开源情报
🌆 图片压缩
🍭 资产清洗
💡 我的作品
👤 关于站长
⚔️ 次 元 剑
搜索到
1
篇与
的结果
2024-09-30
【技术分享】NASM x86 Assembly Language
留言复习x86汇编基础,首先为什么要学习汇编语言呢?在二进制程序逆向中是必不可少的,在二进制程序在没有源代码的情况下,只能进行反汇编逆向分析程序的执行流程。所谓 "学而不思则罔,思而不学则殆",只有真正的理解了是如何运作的,才能更好的进行后面的分析。本教程使用 intel 语法来进行编写,编译器使用的是开源的 NASM,就你叫x86汇编呀,直接干就完了!简介本文章会简单的讲解下Windows下的x86汇编,内容比较简短都是示例,x86汇编语言编程也很混乱。因为有很多不同的汇编器:MASM、NASM、gas、as86、TASM、a86、Terse 等,导致语法都不相同。出于对 汇编语言发展 的兴趣,本教程不是为了教你汇编,让你大概学习下x86汇编基础语法方便为后期逆向做准备。当然感兴趣的可以自己研究,这里推荐几个好的教程网站: Linux 汇编语言教程Putting the "You" in CPU汇编器与链接器不管使用哪种汇编器、目标文件格式、链接器或操作系统,编程过程始终是相同的。Nasm 编译器安装Nasm win64 下载 - | - MinGW 下载 首先需要下载编译环境,Nasm 与 MinGW 下载好后解压出来,配置好环境变量方便终端进行调用。这里我推荐几个链接器:ld、alink、golink、gcc 各有秋色,我会演示他们的用法,前三者打包都是程序最小的状态,而 gcc 会使程序大小变大,因为它加了一些东西。NASM 汇编语法输出 Hello, World! - Windowsextern _printf ; 声明外部的 printf 函数 section .data msg db 'Hello, World!', 0 ; 定义字符串常量 section .text global _main ; 设置入口函数为 _main _main: push msg ; 将字符串地址推入栈中,作为参数传递给 printf call _printf ; 调用 printf 函数 add esp, 4 ; 清理栈上的参数 ret ; 函数返回随便创建一个文件,名称.asm 以.asm 后缀结尾,当然后缀什么的都不重要,汇编代码是对的一切都是 ok 的,通过以下代码在 Windows 进行生成再到链接最终生成可执行程序。/* ------ 参数说明 -f win32 指定 32位 生成目标文件 test.o -lmsvcrt 指定链接器需要 C运行库 要用到 printf ------ 编译命令 nasm -f win32 demo.asm -o test.o ld test.o -o test.exe -lmsvcrt */生成完毕后,可以发现程序大小只有 4kb 左右,非常小那是因为用汇编进行编写的,没有其他一些没用的函数打包进去。通过 IDA 打开生成的程序,发现只有 _main 和 _printf 函数,而且代码也是我们写的那一部分。通过 x64dbg 发现,运行直接到 OEP 时就能看到我们写的代码, 程序入口点是 401000 从这里开始到 40100D 结束(自己写的代码范围)。反汇编代码中 push 0x402000 是把内存地址存储的字符串压入到堆栈中,然后通过 call Printf 调用进行输出 Hello, World!。输出 Hello, World! - Linuxsection .data ; 定义一个名为 hello 的字符串,用 db 伪指令来定义 ; db 是 define bytes,用来指定一组字节 hello db 'Hello, World!', 0 ; 字符串用单引号或双引号括起来,末尾要添加一个零终止符 \0 section .text global _start ; 设置入口函数为 _start _start: ; 往标准输出 (stdout) 写入字符串 ; 用系统调用 write mov eax, 4 ; 系统调用号 4 (write) 存储到寄存器 eax mov ebx, 1 ; 描述符 1 (stdout) 存储到寄存器 ebx mov ecx, hello ; 字符串的地址存储到寄存器 ecx mov edx, 13 ; 把字符串的长度(以字节为单位)存储到寄存器 edx int 0x80 ; 系统调用 sys_write ; 退出程序 ; 用系统调用 exit mov eax, 1 ; 系统调用号 1 (exit) 存储到寄存器 eax xor ebx, ebx ; 寄存器 ebx 设置为 0,表示返回值为 0 int 0x80 ; 系统调用 sys_exit/* ------ 参数说明 -f elf32 指定32位生成目标文件main.o -m elf_i386 指定生成32位可执行elf程序 ------ 编译命令 nasm -f elf32 main.asm -o main.o ld -m elf_i386 main.o -o main */通过IDA打开,可以看到只有一个 _start 主入口函数,也是没有其他任何函数,通过 sys_write 输出再到 sys_exit 退出结束。前面的操作让我们实现了编译环境搭建,和第一个汇编程序的编写到运行,现在让我们正式开始吧。(本教程在 Windows 操作系统进行讲解)视频教程{dplayer src="https://www.52tt.pro/usr/uploads/2023/09/10.1.mp4"/}PE文件节section .text // 代码段:用来存储程序的可执行指令(代码)。 section .data // 数据段:用来存储已经初始化的静态变量和全局变量。 section .rdata // 只读数据段:用来存储只读数据,字符串常量等。 section .bss // 未初始化数据段:用来存储未初始化的静态变量和全局变量。 section .data // 可读写数据段:用来存储可以被程序读取或写入的数据。 section .data? // 未定义的数据段 /* 可执行数据段: */ section .text // 代码段:用来存储程序的可执行指令(代码)。 section .idata // 导入数据段:用来存储从其他动态链接库(DLL)中导入的符号等信息。 section .edata // 导出数据段:用来存储导出给其他模块的符号等信息。 section .rsrc // 资源数据段:用来存储可执行文件中的资源数据,如图标、位图、菜单和对话框等。代码注释; 单行注释(分号)字符串输出 - utf-8extern _printf, _system section .data cmd db 'chcp 65001', 0 ; 设置终端输出编码为 utf-8 msg db '你好,世界!', 10, 0 msg2 db 'Hello, World!', 0 section .text global _main ; 设置入口函数为 _main _main: push cmd call _system push msg ; 将字符串地址推入栈中,作为参数传递给 printf call _printf ; 调用 printf 函数 push msg2 call _printf add esp, 12 ; 清理栈上的参数 ret ; 函数返回局部变量 - local varsection .text global _main extern _printf _main: push ebp ; 保存旧的基址寄存器值 mov ebp, esp ; 设置新的基址寄存器值 sub esp, 4 ; 分配 4 字节大小的空间作为局部变量 mov dword [ebp-4], 5 ; 将值 5 存储到局部变量中 mov eax, dword [ebp-4] ; 将局部变量值加载到寄存器 eax 中 push eax ; 将局部变量值推入栈中,作为参数传递给 printf push msg ; 将格式化字符串的地址也推入栈中 call _printf ; 调用 printf 函数 add esp, 12 ; 清理栈上的参数 pop ebp ; 恢复旧的基址寄存器值 ret ; 函数返回 section .data msg db 'local variable is %d', 10, 0 ; 格式化字符串.数据类型 - Data type整型(Integer) 浮点型(Float) 布尔型(Boolean) 字符型(Character)section .data format db "int: %d", 0 ; 格式化输出模板,%d 整数占位符 num dd 100 ; 存储整数值100 msg db "string: Hello", 0 ; 要输出的字符串 newline db 10, 0 ; 换行符和结尾符 section .text global _main extern _printf _main: push ebp ; 保存当前的ebp值 mov ebp, esp ; 把当前的堆栈指针esp的值赋给ebp,建立函数的堆栈帧 push dword [num] ; 压入num变量的值,准备进行格式化输出 push format ; 压入format变量的地址,作为格式化输出的模板 call _printf ; 调用外部函数_printf进行格式化输出 add esp, 8 ; 清除堆栈上的两个参数,恢复堆栈指针 push dword newline ; 压入newline变量的值,输出换行符 call _printf ; 调用_printf函数输出换行符 add esp, 4 ; 清除堆栈上的一个参数 push msg ; 压入msg变量的地址,准备输出字符串 call _printf ; 调用_printf函数输出字符串 Hello add esp, 4 ; 清除堆栈上的一个参数 mov esp, ebp ; 恢复函数调用前的堆栈状态,将堆栈指针esp的值设为ebp的值 pop ebp ; 恢复之前保存在堆栈中的ebp寄存器的值 ret ; 函数返回条件语句 - if ... elsesection .data format db "%d", 0 num dd 1 format2 db "%d", 0 num2 dd 0 section .text global _main extern _printf _main: push ebp mov ebp, esp mov eax, 5 mov ebx, 5 cmp eax, ebx ; 比较是否相等 je print_num push dword [num2] push format call _printf add esp, 8 jmp end print_num: ; 成立则跳转到这 mov eax, dword [num] push eax push format2 call _printf add esp, 8 end: mov esp, ebp pop ebp ret ; eax, ebx 寄存器值 都等于 5 所以成立输出 1条件循环 - forsection .data count equ 10 msg db "Loop iteration.", 10, 0 section .text global _main extern _printf _main: mov ebx, count ; 设置计数器为循环次数 loop_start: cmp ebx, 0 ; 检查计数器是否为零 je loop_end ; 如果为零,跳转到循环结束处 push msg call _printf ; 输出消息 add esp, 4 ; 清除栈上的参数 dec ebx ; 计数器减一 jmp loop_start ; 跳转回循环起始点 loop_end: mov eax, 0 ret条件循环 - whilesection .data msg db "Loop iteration.", 10, 0 section .text global _main extern _printf _main: loop_start: push msg call _printf ; 输出消息 add esp, 4 ; 清除栈上的参数 jmp loop_start ; 跳转回循环起始点整型数组 - Arraysection .data array dd 1, 2, 3, 4, 5, 10 ; 整数数组 len equ ($ - array) / 4 ; 数组长度 section .text global _main extern _printf _main: xor esi, esi ; 将数组索引初始化为 0 loop: cmp esi, len ; 检查是否到达数组末尾 jge end_loop mov eax, [array + esi * 4] ; 获取数组元素的值 push eax ; 将元素值推入堆栈 push format ; 将格式化字符串推入堆栈 call _printf ; 调用 printf 函数来打印元素值 add esp, 8 ; 清理堆栈 inc esi ; 增加数组索引 jmp loop end_loop: ret ; 返回函数 section .data format db "%d ", 0 ; 格式化字符串,用于打印整数switch 语句extern _printf ; 引入 printf 函数 section .data value db 2 ; 定义 value 变量,初始值为 2 msg1 db "1", 0 ; 定义三个输出信息 msg2 db "2", 0 msg3 db "3", 0 section .text global _main _main: cmp byte [value], 1 je case_1 ; 比较 value 和 1 的值,如果相等,跳转到 case_1 标签处 cmp byte [value], 2 je case_2 ; 比较 value 和 2 的值,如果相等,跳转到 case_2 标签处 cmp byte [value], 3 ; 比较 value 和 3 的值,如果相等,跳转到 case_3 标签处 je case_3 ; 如果都不相等,跳转到 case_default 标签处 jmp case_default case_1: push msg1 ; 将 msg1 的地址压入栈中,并调用 printf 函数 call _printf add esp, 4 ; 回收栈空间 jmp end_switch ; 跳转到 end_switch 标签处 case_2: push msg2 ; 将 msg2 的地址压入栈中,并调用 printf 函数 call _printf add esp, 4 ; 回收栈空间 jmp end_switch ; 跳转到 end_switch 标签处 case_3: push msg3 ; 将 msg3 的地址压入栈中,并调用 printf 函数 call _printf add esp, 4 ; 回收栈空间 jmp end_switch ; 跳转到 end_switch 标签处 case_default: jmp end_switch ; 跳转到 end_switch 标签处 end_switch: mov eax, 0 ; 将返回值设为 0,保证程序正常退出 ret ; 返回上一级调用函数调用约定方法名采用这样的命名方式,就像 _GetStdHandle@4,是因为在 Win32 中,函数名称会经过编码(mangled 或 decorated)以符合特定的约定。这种约定是为了确保函数在编译后可以正确地对外提供和使用。Win32 使用的调用约定称为 __stdcall,它规定了函数在被调用时参数的传递方式、栈的清理方式等。根据 __stdcall 的规定,函数名前面会加上一个下划线(_),然后是函数名本身,后面紧跟着一个 at 符号(@),最后是参数列表中的字节数(以十进制表示)。Cdecl(C Declaration)参数从右向左入栈。调用者负责清理堆栈。支持可变参数。在多数 C 编译器和 Microsoft Windows、Linux 系统上广泛使用。Stdcall(Standard Call)参数从右向左入栈。被调用函数负责清理堆栈。常用于 Win32 API。Fastcall一部分参数通过寄存器传递,其余的参数从右向左入栈。被调用函数负责清理堆栈。具体实现可能因编译器和平台而异。HANDLE OpenProcess( [in] DWORD dwDesiredAccess, [in] BOOL bInheritHandle, [in] DWORD dwProcessId );函数参数是从右向左压入到堆栈的,那么第一个参数在汇编中就是 dwProcessId(进程id),以此类推压入完成进行调用。extern _OpenProcess@12 ; 设置调用API加@12,说明函数有3个参数都是 dword(4字节)。 section .data PROCESS_ALL_ACCESS equ 0x1F0FF section .text global _main _main: push dword 0 ; dwProcessId,进程 ID push dword 0 ; bInheritHandle,不继承句柄 push dword PROCESS_ALL_ACCESS ; dwDesiredAccess,使用标准读取权限 call _OpenProcess@12 ; @12 这么写会自动清空堆栈参数,无需写清除参数代码。以上是加了 @12 参数自动从堆栈清除的过程,可以发现当执行完 call 时堆栈参数自动被清除。标准用户输入 - scanfsection .data prompt db "Please enter a string: ", 0 format db "%s", 0 input db 255 dup(0) ; 申请长度为 255 字节内存字符串数组,初始值都为 0; section .text global _main extern _printf, _scanf, _puts _main: push ebp mov ebp, esp push dword prompt call _printf ; 输出提示信息 add esp, 4 ; 接收用户输入 push dword input push dword format call _scanf add esp, 8 push dword input call _puts ; 显示输入的字符串 add esp, 4 end: mov esp, ebp pop ebp ret调用系统命令 - systemsection .data cmd db 'cmd /c dir', 0 ; 存储 cmd 执行命令 section .text global _main extern _system _main: push ebp mov ebp, esp push dword cmd ; 压入参数到堆栈 call _system ; 调用 system add esp, 4 ; 清除参数 end: mov esp, ebp pop ebp ret调用弹窗 - MessageBoxAsection .data caption db "Hello", 0 message db "Welcome to the MessageBox!", 0 section .text global _main extern _MessageBoxA@16 _main: push 0 ; 确定:0 消息:48 警告:64 push caption push message push 0 ; NULL (HWND) call _MessageBoxA@16 ; 调用MessageBox函数显示消息框nasm -f win32 demo.asm -o test.obj ld -m i386pe -o test.exe test.obj -luser32 -lkernel32 // -luser32 -lkernel32 导入动态链接库终止进程 - TerminateProcesssection .data PROCESS_ALL_ACCESS equ 0x1F0FF section .text global _main extern _OpenProcess@12, _TerminateProcess@8, _printf _main: push dword 21108 ; dwProcessId,进程 ID push dword 0 ; bInheritHandle,不继承句柄 push dword PROCESS_ALL_ACCESS ; dwDesiredAccess,使用标准读取权限 call _OpenProcess@12 mov ebx, eax ; 将句柄保存到 EBX 中 push ebx ; 将句柄值压入堆栈 push format_str ; 格式化字符串 call _printf ; 调用 printf 函数 add esp, 8 push 0 push ebx call _TerminateProcess@8 ; 调用 TerminateProcess 函数 xor ebx, ebx ; 清空 EBX 寄存器 ret section .data format_str db "Handle value: %d", 0 ; 使用 %d 格式输出整数伪代码指令 - import再次提到,对于编译后一个 Win32 函数的名称如何被修饰或装饰这个问题,在这个级别上了解似乎要求过高了。毕竟所有的 C 程序员都可以愉快地写出 "ExitProcess",而不是 "_ExitProcess@4"。很赞同这个说法,有的时候 下划线(_)加艾特(@)的确不是很好,所以可以借助下伪指令,这样就可以直接导入 Windows api 函数了,不需要再修改名称。Alink链接器这个 Alink 链接器上一次修改还是在 1999 年,真的是上古链接器了,估计作者也不更新了不过好在还可以用。Alink 下载 伪指令这些语法,必须生成 obj 然后用 alink 链接生成才行,对于 gcc 和 ld 都是会报错的导致编译不成功,就像以下这样:简单调用 printfextern printf import printf msvcrt.dll section .data msg db "hey Actdream!", 0 section .text use32 ..start: push msg call [printf] add esp, 4 ret输出 Hello, World!extern ExitProcess ; 声明 ExitProcess 外部函数。 import ExitProcess kernel32.dll ; 引入 ExitProcess 函数在 kernel32.dll 动态链接库里。 extern GetStdHandle import GetStdHandle kernel32.dll extern WriteFile import WriteFile kernel32.dll segment .data use32 ; 定义 32 位数据段,用来存储程序中使用的数据。 Text db "Hello, World!",0 ; 在数据段里定义了一个以空字符结尾的字符串 "Hello, World!"。 segment .code use32 ; 定义一个 32 位代码段,在这里定义了程序的主要逻辑。 ..start: ; 定义一个程序入口点标签 push -11 ; 把参数 -11 压入栈中 call [GetStdHandle] ; 调用 GetStdHandle 函数,并将返回值存储在 EAX 寄存器里 mov ebx, eax ; 把句柄存储在 EBX 寄存器里 push dword 0 ; 压入参数 0 lea eax, [ebp-4] ; 指向文本字符串的指针存储在 EAX 寄存器里 push eax ; 把 EAX 寄存器中的指针地址压入栈中 push (message_end - message) ; 把字符串的长度压入栈里(不包括结尾的空字符) push message ; 把字符串的起始地址压入栈里 push ebx ; 获取到的标准输出句柄压入栈中 call [WriteFile] ; 最后调用 WriteFile 函数,把字符串写入到标准输出。 push dword 0 ; 压入参数 0 call [ExitProcess] ; 调用 ExitProcess 函数,来终止程序的运行 hlt ; 暂停 CPU 的运行 message: ; 定义消息字符串的起始标签 db 'Hello, World!', 10 ; 在数据段里中定义了一个字符串 "Hello, World!"。 message_end: ; 定义消息字符串的结束标签/* ------ 参数说明 -fobj 指定输出格式为 obj 目标文件格式 -subsys console 指定生成的是控制台可执行文件 -oPE 指定输出文件格式 PE 可执行文件 ------ 编译命令 nasm -fobj main.asm alink -subsys console -oPE main.obj */调用弹窗 - MessageboxA[BITS 32] ; 声明代码为32位模式 extern ExitProcess import ExitProcess kernel32.dll ; 导入ExitProcess函数 extern MessageBoxA import MessageBoxA user32.dll ; 导入MessageBoxA函数 segment .data use32 Caption db 'From Assembly',0 ; 定义标题字符串 Text db "Hello, World",0 ; 定义消息字符串 segment .code use32 ..start: push dword 0 ; 参数0(消息框类型)压入栈中 push dword Caption ; 标题字符串的地址压入栈中 push dword Text ; 消息字符串的地址压入栈中 push dword 0 ; 参数0(父窗口句柄)压入栈中 call [MessageBoxA] ; 调用MessageBoxA函数显示消息框 push dword 0 ; 参数0(退出代码)压入栈中 call [ExitProcess] ; 调用ExitProcess函数终止程序的运行读进程内存数据BOOL WINAPI ReadProcessMemory( _In_ HANDLE hProcess, _In_ LPCVOID lpBaseAddress, _Out_ LPVOID lpBuffer, _In_ SIZE_T nSize, _Out_ SIZE_T *lpNumberOfBytesRead );读内存要用到 ReadProcessMemory 系统提供的API函数。编译后只有一个 start 入口直接就是我们写的代码,很干净的一个程序只有 2.5 kb 大小。extern OpenProcess import OpenProcess kernel32.dll extern ReadProcessMemory import ReadProcessMemory kernel32.dll extern printf import printf msvcrt.dll extern CloseHandle import CloseHandle kernel32.dll %define PROCESS_ALL_ACCESS 0x1F0FF section .data processId dd 27676 desiredAccess dd PROCESS_ALL_ACCESS inheritHandle dd 0 lpBaseAddress dd 0x4a4610 nSize dd 128 lpNumberOfBytesRead dd 0 readSuccess db 'Read bytes: %d', 0 section .bss buffer resb 128 hProcess resd 1 section .text use32 ..start: push dword [processId] push dword [inheritHandle] push dword [desiredAccess] call [OpenProcess] ; 打开进程句柄 mov [hProcess], eax lea eax, [lpNumberOfBytesRead] push eax push dword [nSize] lea eax, [buffer] push eax push dword [lpBaseAddress] push dword [hProcess] call [ReadProcessMemory] ; 读内存数据 push dword [buffer] push readSuccess call [printf] ; 输出数据 add esp, 8 push dword [hProcess] call [CloseHandle] ; 关闭句柄 ret写进程内存数据BOOL WriteProcessMemory( [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten );extern OpenProcess import OpenProcess kernel32.dll extern WriteProcessMemory import WriteProcessMemory kernel32.dll extern CloseHandle import CloseHandle kernel32.dll extern ExitProcess import ExitProcess kernel32.dll %define PROCESS_ALL_ACCESS 0x1F0FFF section .data processId dd 27676 desiredAccess dd PROCESS_ALL_ACCESS inheritHandle dd 0 lpBaseAddress dd 0x4a4610 nSize dd 4 writeValue dd 10 section .bss hProcess resd 1 bytesWritten resd 1 section .text use32 ..start: push dword [processId] push dword [inheritHandle] push dword [desiredAccess] call [OpenProcess] mov [hProcess], eax push 0 push dword [nSize] lea eax, [writeValue] push eax push dword [lpBaseAddress] push dword [hProcess] call [WriteProcessMemory] push dword [hProcess] call [CloseHandle] retGolink链接器Goasm 与 GoLink,都是由 Jeremy Gordon 开发,是用来与 GoAsm 配合使用的链接器。主要特点:轻量级高效:专为快速和高效的链接过程设计。兼容性:它可以链接其他汇编器(比如 NASM 或 FASM)还有某些 C/C++ 编译器生成的对象文件。格式的支持:GoLink 对 Windows PE 格式有良好的支持。用 NASM 原生语法,导入动态链接库函数不需要以下划线 _ 开头,比如以下:这里插一嘴:我推荐使用这个链接器,方便且轻巧。官网地址 | Golink 下载extern printf section .data msg db 'Hello, World!', 0 section .text global main main: push msg call printf add esp, 4 ret/* ------ 参数说明 /entry 指定入口函数 /console 指定为控制台程序 ------ 编译命令 nasm -f win32 main.asm -o main.obj golink /entry main main.obj msvcrt.dll /console */简单的窗口程序{collapse}{collapse-item label="NASM汇编代码" close}extern printf extern ExitProcess extern RegisterClassExA extern CreateWindowExA extern MessageBoxA extern GetModuleHandleA extern PeekMessageA extern TranslateMessage extern DispatchMessageA extern PostQuitMessage extern DefWindowProcA extern ShowWindow extern UpdateWindow extern GetStockObject extern BeginPaint extern EndPaint extern Rectangle extern Ellipse extern MoveToEx extern LineTo extern DrawTextA extern SetBkMode extern SetTextColor extern CreateSolidBrush extern CreatePen extern SelectObject extern DeleteObject extern LoadIconA extern LoadCursorA %define WS_OVERLAPPED 0x00000000 %define WS_POPUP 0x80000000 %define WS_CHILD 0x40000000 %define WS_MINIMIZE 0x20000000 %define WS_VISIBLE 0x10000000 %define WS_DISABLED 0x08000000 %define WS_CLIPSIBLINGS 0x04000000 %define WS_CLIPCHILDREN 0x02000000 %define WS_MAXIMIZE 0x01000000 %define WS_CAPTION 0x00C00000 %define WS_BORDER 0x00800000 %define WS_DLGFRAME 0x00400000 %define WS_VSCROLL 0x00200000 %define WS_HSCROLL 0x00100000 %define WS_SYSMENU 0x00080000 %define WS_THICKFRAME 0x00040000 %define WS_GROUP 0x00020000 %define WS_TABSTOP 0x00010000 %define WS_MINIMIZEBOX 0x00020000 %define WS_MAXIMIZEBOX 0x00010000 %define WS_TILED WS_OVERLAPPED %define WS_ICONIC WS_MINIMIZE %define WS_SIZEBOX WS_THICKFRAME %define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW %define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ WS_CAPTION | \ WS_SYSMENU | \ WS_THICKFRAME | \ WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX) %define PM_REMOVE 1h %define WM_QUIT 0x0012 %define WM_PAINT 0x000F %define WM_CLOSE 0x0010 %define CS_VREDRAW 0x0001 %define CS_HREDRAW 0x0002 %define CS_DBLCLKS 0x0008 %define CS_OWNDC 0x0020 %define CS_CLASSDC 0x0040 %define CS_PARENTDC 0x0080 %define CS_NOCLOSE 0x0200 %define CS_SAVEBITS 0x0800 %define CS_BYTEALIGNCLIENT 0x1000 %define CS_BYTEALIGNWINDOW 0x2000 %define CS_GLOBALCLASS 0x4000 %define IDI_APPLICATION 32512 %define IDC_ARROW 32512 section .text global START START: push 0 call GetModuleHandleA mov dword [Instance], eax mov dword[OurWindowclass], 48 mov dword[OurWindowclass+4], CS_OWNDC|CS_HREDRAW|CS_VREDRAW mov dword[OurWindowclass+8], WindowProc mov dword[OurWindowclass+12], 0 mov dword[OurWindowclass+16], 0 mov eax, dword[Instance] mov dword[OurWindowclass+20], eax push IDI_APPLICATION push 0 call LoadIconA mov dword[OurWindowclass+24], eax push IDI_APPLICATION push 0 call LoadCursorA mov dword[OurWindowclass+28], eax mov dword[OurWindowclass+32], 5 mov dword[OurWindowclass+36], 0 mov dword[OurWindowclass+40], Windowclassname mov dword[OurWindowclass+44], 0 lea eax, [OurWindowclass] push eax call RegisterClassExA cmp eax, 0 jne Lcreate push 0 push Windowname push string_reg_failed push 0 call MessageBoxA jmp exit Lcreate: push 0 push dword[Instance] push 0 push 0 push 250 ; Height push 380 ; Width push 140 ; Top push 360 ; Left push WS_VISIBLE+WS_OVERLAPPEDWINDOW push Windowname push Windowclassname push 0 call CreateWindowExA mov dword[Windowhandle], eax push 1 push dword[Windowhandle] call ShowWindow push dword[Windowhandle] call UpdateWindow messloop: push PM_REMOVE push 0 push 0 push 0 push MessageBuffer call PeekMessageA cmp eax, 0 je nextloop cmp dword[MessageBuffer+4], WM_QUIT je exit push MessageBuffer call TranslateMessage push MessageBuffer call DispatchMessageA nextloop: jmp near messloop exit: push dword 0 call ExitProcess ret global WindowProc WindowProc: push ebp mov ebp, esp cmp dword[ebp+12], WM_CLOSE jne Ldefault push 0 call PostQuitMessage Ldefault: push dword[ebp+20] push dword[ebp+16] push dword[ebp+12] push dword[ebp+8] call DefWindowProcA mov esp, ebp pop ebp ret section .data Windowclassname db 'MyClass', 0 Windowname db 'My Window', 0 string_reg_failed db 'Failed to register main window', 0 Instance dd 0 Windowhandle dd 0 section .bss MessageBuffer resb 28 OurWindowclass resb 48{/collapse-item}{/collapse}/* ------ 编译命令 nasm -f win32 window.asm -o window.obj golink window.obj kernel32.dll user32.dll */隐藏DOS窗口/* ------ 参数说明 -subsystem windows 即可隐藏DOS窗口 ------ 编译命令 nasm -f win32 main.asm -o main.o ld main.o -o main.exe -lkernel32 -lmsvcrt -luser32 -subsystem windows */ 生成图标程序/* ------ 文件生成 创建文件 resource.rc 内容:IDI_ICON ICON "mu.ico" 内容写到里面 使用 windres 生成资源目标文件 打包资源命令:windres resource.rc resource.o ------ 编译命令 nasm -f win32 main.asm -o main.obj ld main.obj resource.o -o main.exe -lmsvcrt */添加UAC清单创建UAC清单文件:创建一个XML文件名为 nac.manifest 内容填写以下代码。<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges> <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> </assembly>创建资源文件:编写一个资源脚本文件名为: src.rc 用来引用清单文件,内容填写以下代码。#include "winuser.h" 1 RT_MANIFEST "nac.manifest" IDI_ICON ICON "00.ico"这里我添加了自定义图标在 src.rc 写了 IDI_ICON 当然不需要也可以去掉。/* ------ 文件生成 nasm -f win32 main.asm -o main.obj windres src.rc -O coff -o src.res ------ 编译命令 ld -o main.exe main.obj src.res -lkernel32 -luser32 -lmsvcrt */vmp加壳问题如果你尝试用 alink 进行链接生成程序,就会发现区段的命名方式,不以 . 开头,导致加壳的时候识别错误。而使用 ld、gcc、golink 进行连接就不会出现这种情况了,就能正常加壳了。调用第三方DLL这里我写了一个自定义的dll,里面就导出了一个 initGo 函数,功能是弹出一个弹窗。section .data dllName db "Bin32.dll", 0 functionName db "initGo", 0 section .text extern LoadLibraryA, GetProcAddress global main main: push dllName call LoadLibraryA mov ebx, eax ; DLL 句柄 push functionName push ebx call GetProcAddress mov esi, eax ; 函数指针 call esi ; 调用 initGo 函数 mov ebx, 0 ret/* ------ 编译命令 nasm -f win32 main.asm -o main.obj golink /entry main main.obj Bin32.dll Kernel32.dll /console */
2024年09月30日
5,982 阅读
0 评论
958 点赞
0:00