0 0 0

科锐学习笔记-第三阶段-32位汇编 02radasm和联合编译

admin
203 0

本文共计10942个字,预计阅读时长43.8分钟。

目录

汇编使用资源 

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

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

使用vc6.0建立资源文件 

img 

img 

img 

img 

img 

img 

img 

用vs建立资源文件 

1.  新建一个桌面向导 

img 

img 

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

img 

img 

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

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

![img](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 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库 

#### 动态库使用 

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

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

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

![img](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 
``` 

![image.png](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 PROC  sz:dword 
   invoke MessageBox,NULL,sz,offset  g_sz,MB_OK  ;调用弹窗,显示传入的字符串 
   ret 
TestFunc ENDP 

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

end   
``` 

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

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

调用   

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

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

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

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

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

1\.  去掉预编译头 

![img](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; 
} 

``` 

![img](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 PROC  sz:dword 
   invoke MessageBox,NULL,sz,offset  g_sz,MB_OK 
   ret 
TestFunc ENDP 

end 

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

image.png 

image.png 

image.png 

汇编使用C语言的 obj  

1新建一个c的源文件 

image.png 

2取消预编译头 

image.png 

#include <windows.h> 

void Func(char* sz) 
{ 

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

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

![img](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 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 

img 

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

img 

汇编使用  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 

使用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, 
       DWORD  ul_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; 
   } 

   导出函数 

   img 

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

   .386 
   .model flat, stdcall 
   option casemap:none 

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

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

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

   ![img](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 文件导出函数 

image.png 

image.png 

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

image.png 

image.png 

image.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*)"你好"); 
} 

img 

内联汇编 

内联 

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

img 

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


最新回复 ( 0 )
全部楼主