首页
社区
课程
招聘
Win32汇编学习笔记02.RadAsm和联合编译
发表于: 2025-1-2 23:14 1746

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

2025-1-2 23:14
1746

汇编使用资源

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

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

使用vc6.0建立资源文件

img

img

img

img

f40K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1M7s2y4W2L8X3c8Q4x3X3g2F1k6i4c8Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4e0q4Q4x3X3b7I4i4K6u0V1x3g2)9J5k6h3S2@1L8h3H3`.

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

汇编代码

.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

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

img

img

.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

静态库使用

.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

调用

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

img

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

img

img

  1. 去掉预编译头

img

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


int main(int argc, char* argv[])
{
  
  TestFunc("你好");   //调用汇编obj里面的函数

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

img

用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

img

把 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

    img

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

作业



[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回