-
-
Win32汇编学习笔记02.RadAsm和联合编译
-
发表于: 2025-1-2 23:14 1746
-
汇编使用资源汇编使用资源的方式和C的一样,也是把资源文件 rc 编译成 res 再链接进去,汇编没有自己的资源编辑器,需要借助 vc6.0或者 vs 主要是把 头文件 .h 转化为对应的 .inc 使用vc6.0建立资源文件用vs建立资源文件
生成文件之后,再打开 rc 文件,生成为 编译链接文件时要讲资源文件加进去 ml /c /coff %1.asm if errorlevel 1 goto err rc %1.rc ;如果用的是是vs自己生成的 res文件,就不需要 if errorlevel 1 goto err link /subsystem:windows %1.obj %1.res if errorlevel 1 goto err OllyICE.exe %1.exe :err pause 汇编代码 .386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc ;引入资源的头文件 include resource.inc includelib user32.lib includelib kernel32.lib .data .code ;打开窗口的过程函数 DialogProc proc hwndDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg == WM_COMMAND mov eax, wParam .IF ax == BTN_TEST ;调用弹窗的函数 invoke MessageBox, NULL, NULL, NULL, MB_OK .ENDIF .ELSEIF uMsg == WM_CLOSE invoke EndDialog, hwndDlg, 0 .ENDIF mov eax, FALSE ;返回结果 ret DialogProc endp WinMain proc hInstance:HINSTANCE ;打开窗口函数 实例句柄必须给,不然找不到资源 invoke DialogBoxParam, hInstance, DLG_TEST, NULL, offset DialogProc, 0 ret WinMain endp START: invoke GetModuleHandle, NULL invoke WinMain, eax invoke ExitProcess, 0 end START 使用C库动态库使用可以看出,C库函数声明 在 msvcrt.inc中 并在前面加 crt_ .386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc include msvcrt.inc ;c库头文件 includelib user32.lib includelib kernel32.lib includelib msvcrt.lib ;c库实现文件 .data g_szFmt db "%s %d %08X", 0dh, 0ah, 0 g_sz db "这是测试", 0 .code main proc C invoke printf, offset g_szFmt, offset g_sz, 4096, 4096 ;invoke strlen, offset g_sz ret main endp START: invoke main invoke ExitProcess, 0 end START 静态库使用 .386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib includelib libc.lib ;C库的静态库 public main ;使用静态库必用要有 main函数 printf PROTO C :VARARG ; 函数声明 VARARG 变参类型 strlen PROTO C :DWORD ; 函数声明 .data g_szFmt db "%s %d %08X", 0dh, 0ah, 0 g_sz db "这是测试", 0 .code main proc C ;调用约定必须是c invoke strlen, offset g_sz ;调用c库函数 ret main endp START: invoke main ;调用main函数 invoke ExitProcess, 0 end START 汇编和C的互调ml /c /coff test.asm link /subsystem:console test.obj link /subsystem:windows test.obj 使用obj文件从语法上来将,c和汇编的语法是相互不兼容的,所以想在源码上进行相互间的调用是不可能的事情, 所以只能退而求其次 使用obj文件, 把 c 的 obj文件给汇编去用,把 汇编的 obj 文件 给 c 去用 c调用汇编的 obj文件.386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib .data g_szFmt db "%s %d %08X", 0dh, 0ah, 0 g_sz db "这是测试", 0 .code TestFunc PROC sz:dword invoke MessageBox,NULL,sz,offset g_sz,MB_OK ;调用弹窗,显示传入的字符串 ret TestFunc ENDP ;防止程序把 START 当做程序入口,直接退出程序 ;START: ;invoke ExitProcess,0 ;退出程序 ;end START end 查看 生成的obj 文件, 我们的 函数的名字 调用
extern "C" void __stdcall TestFunc(char* sz); //对函数进行声明,注意调用约定 int main(int argc, char* argv[]) { TestFunc("你好"); //调用汇编obj里面的函数 printf("Hello World!\n"); return 0; } 用vs调用 汇编obj.386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib Func proto c sz:DWORD ;将函数声明改成汇编形式 .data g_szFmt db "%s %d %08X", 0dh, 0ah, 0 g_sz db "这是测试", 0 .code TestFunc PROC sz:dword invoke MessageBox,NULL,sz,offset g_sz,MB_OK ret TestFunc ENDP end 把汇编生成的 obj 文件 放入项目 汇编使用C语言的 obj 1新建一个c的源文件 2取消预编译头 #include <windows.h> void Func(char* sz) { MessageBox(NULL,sz,"这是c函数",MB_OK); } 把 obj文件放到汇编代码同目录 .386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib Func proto c sz:DWORD ;将函数声明改成汇编形式 .data g_szFmt db "%s %d %08X", 0dh, 0ah, 0 g_sz db "这是测试", 0 .code ;调用 c 的obj文件必须有,虽然没什么用 main proc c ret main endp TestFunc PROC sz:dword invoke MessageBox,NULL,sz,offset g_sz,MB_OK ret TestFunc ENDP START: invoke Func, offset g_sz invoke ExitProcess,0 ;退出程序 end START 解决办法,把 vs 或者vc6的 lib 加入环境变量 汇编使用 vs 的 obj在vs项目中添加一个 Func.c 文件 代码如下,生成解决方案 #include <windows.h> void Func(char* sz) { MessageBox(NULL, sz, "这是c函数", MB_OK); } 把obj文件放到同目录 .386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib Func proto c sz:DWORD ;将函数声明改成汇编形式 .data g_szFmt db "%s %d %08X", 0dh, 0ah, 0 g_sz db "这是测试", 0 .code ;调用 c 的obj文件必须有,虽然没什么用 main proc c ret main endp TestFunc PROC sz:dword invoke MessageBox,NULL,sz,offset g_sz,MB_OK ret TestFunc ENDP START: invoke Func, offset g_sz invoke ExitProcess,0 ;退出程序 end START 此时去编译链接项目 会有一大堆 外部符号找不到, 这些 库是vs 的 ,解决办法是把这些库加进来,或者使用 vs 的命令提示符.很麻烦,还要关掉一堆的检查,最好的方法使用dll 使用dlldll的接口是很标准的,从dll出现为止,基本没变过 汇编使用 C 的 dll
新建一个def 文件导出函数 此时dll中并没有导出函数,因为没有告诉链接器去用它 这个dll就可以给 c程序用了 把 生成 的 dll 和lib 放到正确位置 #include "framework.h" #include "Project2.h" #pragma comment(lib, "asmdll.lib") 使用生成的 lib库 extern "C" void __stdcall TestFunc(char* sz); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { TestFunc((char*)"你好"); } 内联汇编内联在c,c++中写汇编代码 ,在内联汇编中,宏汇编是无法使用的,但是部分伪指令是可以用的,而且只有 32位程序可以写,64位程序是无法内联汇编的 当我们想把某个函数注入到对方进程里面,这样就要求我们的代码是纯汇编的,因为一旦用到vs的代码,vs就会加一堆检查 格式: 关键字 __asm __asm mov eax, eax __asm mov eax, n __asm { mov eax, n0 mov n0, 10 push MB_OK push sz push NULL push NULL call MessageBoxA mov eax, size n mov eax, length n mov eax, offset wWinMain } // Project2.cpp : 定义应用程序的入口点。 // #include "framework.h" #include "Project2.h" #pragma comment(lib, "asmdll.lib") extern "C" void __stdcall TestFunc(char* sz); void TestAsm(int n, char* sz) { int n0 = 9; __asm { mov eax, eax mov eax, n mov eax, n0 mov n0, 10 push MB_OK push sz push NULL push NULL call MessageBoxA //汇编里面调函数 ,宏汇编是无法使用的 //使用伪指令 mov eax, size n mov eax, length n mov eax, offset wWinMain } } //裸函数 __declspec(naked) void Test(int n, char* sz) { //裸函数申请栈空间 __asm { push ebp mov ebp, esp sub esp, 0x40 } ;裸函数不允许初始化变量 int n0; float f0; n0 = 9; f0 = 3.14f; if (n0 == n) { MessageBoxA(NULL, NULL, NULL, NULL); } __asm { mov esp, ebp pop ebp ret } } int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { Test(99, (char*)"hello world"); TestAsm(99, (char*)"hello world"); //TestFunc((char*)"你好"); return 0; } 裸函数 __declspec(naked)正常一个空函数什么都不写.都回会有一些初数化操作,但是如果不要,可以声明 它是一个裸函数,就不会帮其生成代码,需要自己去生成 //裸函数 __declspec(naked) void Test(int n, char* sz) { __asm { push ebp mov ebp, esp sub esp, 0x40 } int n0; float f0; n0 = 9; f0 = 3.14f; if (n0 == n) { MessageBoxA(NULL, NULL, NULL, NULL); } __asm { mov esp, ebp pop ebp ret } } 作业 |