大理寺少卿 发表于 2024-12-24 19:50:38

Win32汇编学习笔记02.RadAsm和联合编译

### 汇编使用资源

汇编使用资源的方式和C的一样,也是把资源文件 rc 编译成**res** 再链接进去,汇编没有自己的资源编辑器,需要借助vc6.0或者 vs

主要是把头文件 .h 转化为对应的 .inc

#### 使用vc6.0建立资源文件

!(./notesimg/1653114806937-9be049e1-1a07-4a2a-a9d9-6e827f52e167.png)

!(./notesimg/1653115098065-a3726add-8066-4d47-8809-7c108a587bc0.png)

!(./notesimg/1653115127377-b51b4db3-44b2-4427-ae50-4beb2eed08de.png)

!(./notesimg/1653115360355-0f1ce451-f1e3-44f7-8012-2d31bbc9ec93.png)

!(./notesimg/1653115446902-eaf75ab8-458c-495a-aa0d-b6384bbfa500.png)

!(./notesimg/1653116701676-b2171544-da6d-4b5b-a0da-af3d20ba4a6c.png)

!(./notesimg/1653117009592-9b5b7dd9-b893-44a4-ae18-9897326a609d.png)

#### 用vs建立资源文件

1. 新建一个桌面向导

!(./notesimg/1653121559376-b10ad7b4-0336-4a72-a813-53c7c42de48c.png)

!(./notesimg/1653121593498-82cbb51e-ec94-40d0-ad23-fe0189594dfa.png)

生成文件之后,再打开 rc 文件,生成为

!(./notesimg/1653122349635-71a0397a-cf52-46a5-9a4c-e6b01bfb4530.png)

!(./notesimg/1653122383860-c65bda64-d39d-4732-805e-157e594b3f22.png)

编译链接文件时要讲资源文件加进去

```
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
```

!(./notesimg/1653117571722-9a66ee9c-d640-4fa6-bf11-d93a8fd4ff2d.png)

汇编代码

```
.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 prochwndDlg: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库

#### 动态库使用

!(./notesimg/1653123146662-2e3f8952-6ac8-4a09-9700-6b47d5f6deb9.png)

可以看出,C库函数声明 在 msvcrt.inc中并在前面加crt_

!(./notesimg/1653123177058-1959e571-a6b6-409d-a407-e3c54155f69d.png)

!(./notesimg/1653123256435-f8eceb81-49bc-4f7b-a60b-ffdfe7a8eb4f.png)

```
.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
```

!(./notesimg/1653124359793-83e1e23d-c696-4258-9461-8877b7fe54d0.png)

静态库使用

```
.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 PROCsz:dword
    invoke MessageBox,NULL,sz,offsetg_sz,MB_OK;调用弹窗,显示传入的字符串
    ret
TestFunc ENDP

;防止程序把START 当做程序入口,直接退出程序
;START:
    ;invoke ExitProcess,0   ;退出程序
;end START

end
```

查看 生成的obj 文件, 我们的 函数的名字

!(./notesimg/1653125939590-e03c8747-5102-4143-893a-2f497b5509e7.png)

调用

1. 新建一个 32位的桌面应用程序

!(./notesimg/1653126006842-b9bc546c-9c8d-44f7-880f-30337ca24311.png)

1. 把obj文件复制到工程目录下
2. 再导入.bobj文件

!(./notesimg/1653126333853-bc2c18ec-20fc-4dfa-acba-0e5d40980644.png)

!(./notesimg/1653126416290-1988f446-4c11-4dba-b377-8ff430c9635f.png)

1. 去掉预编译头

!(./notesimg/1653126933316-c794bdfd-c771-418d-9219-f2b199797420.png)

```
extern "C" void __stdcall TestFunc(char* sz);   //对函数进行声明,注意调用约定


int main(int argc, char* argv[])
{

TestFunc("你好");   //调用汇编obj里面的函数

printf("Hello World!\n");
return 0;
}

```

!(./notesimg/1653127002300-2ff56624-c61e-41e1-af40-7c990fbe2f36.png)

##### 用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 PROCsz:dword
    invoke MessageBox,NULL,sz,offsetg_sz,MB_OK
    ret
TestFunc ENDP

end


````

把汇编生成的 obj 文件 放入项目

!(./notesimg/1653129657700-6b7b019d-7701-4f9a-a1be-c7d53be678f0.png)

!(./notesimg/1653130576344-4c166de7-fb34-4012-adb0-8542ad528b49.png)

!(./notesimg/1653130589363-f8461d1e-3683-4032-9663-e897f40ca999.png)

汇编使用C语言的 obj

1新建一个c的源文件

!(./notesimg/1653127181008-1af54761-fb42-497b-b383-e3047a49721c.png)

2取消预编译头

!(./notesimg/1653127398874-26da2229-ae28-45be-a0f3-5d6a93c01b65.png)

```
#include <windows.h>

void Func(char* sz)
{

MessageBox(NULL,sz,"这是c函数",MB_OK);
}
```

!(./notesimg/1653127455711-cffdd546-a7cc-47a3-bce4-3eb15b644ef5.png)

!(./notesimg/1653127526616-f7e2f23b-feaa-4afb-a7c6-525e0ea928ed.png)

把 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 PROCsz:dword
    invoke MessageBox,NULL,sz,offsetg_sz,MB_OK
    ret
TestFunc ENDP

START:
    invoke Func, offset g_sz
    invoke ExitProcess,0   ;退出程序
end START


```

!(./notesimg/1653129150282-490b2dfa-b4b5-4f8f-9945-2b316efb9112.png)

解决办法,把 vs 或者vc6的 lib 加入环境变量

!(./notesimg/1653268156407-d15a5cdc-fc15-4dd1-aedb-ad6da2f125fa.png)

###### 汇编使用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 PROCsz:dword
    invoke MessageBox,NULL,sz,offsetg_sz,MB_OK
    ret
TestFunc ENDP

START:
    invoke Func, offset g_sz
    invoke ExitProcess,0   ;退出程序
end START

```

此时去编译链接项目 会有一大堆 外部符号找不到, 这些 库是vs 的 ,解决办法是把这些库加进来,或者使用 vs 的命令提示符.很麻烦,还要关掉一堆的检查,最好的方法使用dll

#### 使用dll

dll的接口是很标准的,从dll出现为止,基本没变过

##### 汇编使用 C 的 dll

1. 用vs创建一个动态链接库

   ```
   // dllmain.cpp : 定义 DLL 应用程序的入口点。
   #include "pch.h"


   #include <windows.h>

   extern "C" void Func(char* sz)   //声明函数和调用约定
   {

       MessageBoxA(NULL, sz, "这是c函数", MB_OK);
   }



   BOOL APIENTRY DllMain(HMODULE hModule,
       DWORDul_reason_for_call,
       LPVOID lpReserved
   )
   {
       switch (ul_reason_for_call)
       {
       case DLL_PROCESS_ATTACH:
       case DLL_THREAD_ATTACH:
       case DLL_THREAD_DETACH:
       case DLL_PROCESS_DETACH:
         break;
       }
       return TRUE;
   }

   ```

   导出函数

   !(./notesimg/1653132014234-6a8c1c6c-b8a2-470f-811b-a7107dbb298d.png)

   把生成 的dll和 lib 文件放到汇编同目录下

   ```
   .386
   .model flat, stdcall
   option casemap:none

   include windows.inc
   include user32.inc
   include kernel32.inc

   includelibMyDll.lib ;导入lib库

   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 PROCsz:dword
       invoke MessageBox,NULL,sz,offsetg_sz,MB_OK
       ret
   TestFunc ENDP

   START:
       invoke Func, offset g_sz
       invoke ExitProcess,0   ;退出程序
   end START
   ```

   !(./notesimg/1653132370223-3b054a66-a9a4-47bb-a043-58f124550729.png)

   !(./notesimg/1653132335050-8715f016-4379-4096-b1bd-c5b3793a1248.png)

   ##### C使用汇编的DLL

   汇编也可以写成dll    .注意字符集


   ```
   .386
   .model flat, stdcall
   option casemap:none

   include windows.inc
   include user32.inc
   include kernel32.inc

   includelib user32.lib
   includelib kernel32.lib

   .data
       g_sz db "这是测试", 0

   .code
   TestFunc proc sz:DWORD
       invoke MessageBox, NULL, sz, offset g_sz, MB_OK
       ret
   TestFunc endp

   ; dll 必须要有 DllMain
   DllMain proc hinstDLL:HINSTANCE,fdwReason:DWORD, lpvReserved:LPVOID

       mov eax, TRUE
       ret
   DllMain endp

   end DllMain   ;DllMain一般作为函数入口点

   ```

新建一个def 文件导出函数

!(./notesimg/1653132799718-8e9c5fe4-250c-448c-a8d3-5430ee5a110d.png)

!(./notesimg/1653132997705-be41bc4a-7e8d-493f-a724-f9be5ee3daa2.png)

此时dll中并没有导出函数,因为没有告诉链接器去用它

!(./notesimg/1653133077342-80eb10d7-9d03-498c-a2b1-83c393dc821f.png)

!(./notesimg/1653133295727-df2fbefc-bfa6-471a-bfa5-bb3706b7bac7.png)

!(./notesimg/1653133340272-99cb0ef0-7629-4fe8-bb2d-7e8efb91ade2.png)

这个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*)"你好");
}

```

!(./notesimg/1653133619591-4d4e249d-eb5b-40a8-bee7-5cf90f5b8c88.png)

### 内联汇编

#### 内联

在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
    }


```

!(./notesimg/1653133892842-b428ae71-1c0f-4076-8da3-a405c341f180.png)

```
// 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
    }
}
```

### 作业

#### 汇编版俄罗斯方块

[📎汇编版俄罗斯方块.zip](./汇编版俄罗斯方块.zip)
页: [1]
查看完整版本: Win32汇编学习笔记02.RadAsm和联合编译