有趣的二进制-逆向工程

体验一下软件分析

  1. 常用的分析工具
  • Stirling-二进制编辑器
  • Process Monitor-文件和注册表监控
  • Wireshark-网络监控
  1. 运行chap01\sample_mal\Release目录中的sample_mal.exe文件,运行后会弹出一个对话框:
    有趣的二进制-逆向工程_第1张图片
  2. 通过Process Monitor的日志来确认程序的行为
  • 点击Filter
    有趣的二进制-逆向工程_第2张图片
    在这里插入图片描述
  • 通过跟踪Process Monitor的日志,我们可以发现程序在C:\Users\alice\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup位置进行了CreateFile的操作。
  • 存放在启动文件夹中的程序,会随着windows启动自动运行。
  • 示例程序连续执行CreateFile、WriteFile和CloaseFile几个操作,这些操作加起来的功能相当于“在指定文件夹创建并写入一个名为0.exe的文件”。
    有趣的二进制-逆向工程_第3张图片
  • 用Stirling打开0.exe和sample_mal.exe程序,查看其二进制代码:
    有趣的二进制-逆向工程_第4张图片
  • 可以看到两者的内容完全一致,sample_exe只是将自己拷贝了一份。

查看注册表(win10下写入注册表显示权限拒绝)

  1. 注册表是Windows系统提供的一个用于保存配置信息的数据库,其中保存的数据包括浏览器设置、文件类型关联、用户密码等。
  2. 进一步分析日志,我们发现程序想要往注册的Run目录下写入一个文件。
  3. 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run和启动文件夹一样,其中注册的程序会在Windows重启时自动运行。
  4. Windows重启时自动运行的程序可以注册在下列任一注册表的位置:
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion
  • HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion
  1. 此时我们还发现,在文档目录文件夹下还创建了一个名为1.exe的文件
    有趣的二进制-逆向工程_第5张图片
  • 和0.exe一样,1.exe也是sample_mal.exe的一个副本
  • 1.exe的路径已经被注册在注册表下面,HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Current Version\Run,Windows启动时就会自动运行1.exe。
  • 查看注册表内容window+R->regedit
  • sample.exe只会弹出一个Hello Malware的对话框,并没有进行任何操作。我们只要将“启动”文件夹、“我的文档”以及注册表中新增的内容删除,系统环境就可以完全恢复原状了。

什么是逆向工程

  1. sample_mal.exe程序会执行以下操作:
  • 修改注册表以便系统重启时自动运行
  • 将自己复制到启动文件夹以便在系统重启时自动运行
  1. 对于上面这样软件进行分析并搞清楚其行为的工作就是逆向工程,逆向工程是指一般意义上的软件分析,其对象不仅限于恶意软件,因此也不一定和计算机安全相关。
  2. 逆向工程原本是指通过拆解机器装置并观察其运行情况来推导其制造方法、工作原理和原始设计的行为,但是在软件领域,逆向工程主要指的是阅读反汇编后的代码,以及使用调试器分析软件行为等工作。

尝试静态分析

  1. 软件分析方法上大体可以分为“静态分析”和“动态分析”两种:
  • 静态分析:在不运行目标程序的情况下进行分析
  • 动态分析:在运行目标程序的同时进行分析
  1. 静态分析主要包括以下两种方法:
  • 阅读反汇编代码
  • 提取可执行文件中的字符串,分析使用了哪些单词。
  1. 对chap01\wsample01a\Release\wsample01.exe进行静态分析,wsample01a.exe的运行结果如下所示:
    有趣的二进制-逆向工程_第6张图片
  2. 用二进制编辑器查看文件内容
    有趣的二进制-逆向工程_第7张图片
  • 屏幕上显示一串十六进制字符,这就是Windows可执行文件格式,即“PE格式“的文件内容。
  • 我们能够发现下面这些内容

字符串MESSAGE和Hello!Windows
文件路径C:\Documents and Settings\XPMUser…
字符串KERNEL32.dll、MessageBoxW

  1. 使用IDA PRO来进行反汇编分析
  • 打开wsample01.exe文件
    有趣的二进制-逆向工程_第8张图片
  • 左键显示的是程序使用的所有函数名称,右侧可以点击空格在Text View或Graph View视图中进行切换。
  • 在Graph View视图中IDA会显示出调出的函数以及传递的参数。
  1. wWinMain函数里面的逻辑除了Hello!Windows、MESSAGE、MessageBoxW等字符串外,还包含:
  • 2012
  • lstrcmpW
  • GetActiveWindow
  1. Hello!Windows和Hello!2012这两个字符串实在不同的条件分支中显示的。
  2. 通过命令行输入2012作为参数来运行一下wsample01a.exe程序:
    有趣的二进制-逆向工程_第9张图片
  • 只要阅读了汇编语言代码,就可以发现其中使用了lstrcmoW对字符串2012和命令行参数进行了比较操作。
    有趣的二进制-逆向工程_第10张图片
  • 查看wsample01.cpp的真正源代码:
int APIENTRY _tWinMain(
	HINSTANCE hInstance, 
	HINSTANCE hPrevInstance, 
	LPTSTR    lpCmdLine, 
	int       nCmdShow)
{
	if(lstrcmp(lpCmdLine, _T("2012")) == 0){
		MessageBox(GetActiveWindow(), 
			_T("Hello! 2012"), _T("MESSAGE"), MB_OK);
	}else{
		MessageBox(GetActiveWindow(), 
			_T("Hello! Windows"), _T("MESSAGE"), MB_OK);
	}	
	return 0;
}
  • 通过IDA就可以将执行文件转换成C语言一样容易理解的汇编代码,尤其是它的Graph view十分强大,可以让我们十分清晰地看出程序的分支逻辑。

尝试动态分析

  1. 动态分析是在目标程序运行的同时跟踪其行为的方法,主要使用调试器来跟踪程序逻辑,除此之外,下面这些方法也被为动态分析:
  • 获取文件和注册表访问日志
  • 抓取网络包
  1. 开始分析一下chap01\wsample01b\Release的示例程序wsample01b.exe.
    有趣的二进制-逆向工程_第11张图片
  2. 使用Process monitor来查看wsample01b.exe文件和注册表的访问日志:
    在这里插入图片描述
  • 可以看到wsample01b.exe在启动目录复制了一个自身的拷贝
  1. 调试器是一种帮助发现程序问题和bug的软件,一般来说至少具备以下功能:
  • 断点
  • 单步跳入、跳出
  • 查看寄存器和内存数据
  1. 断点能够在任意位置中断和恢复运行,而每执行一条指令都中断一次就叫作单步跳入或跳出。通过单步运行功能,我们可以以一条指令或者一行代码为单位逐个运行程序中的逻辑,仔细检查内存和变量的状态,跳入和跳出的区别:
  • 跳入:调用函数时进入函数内部
  • 跳出:调用函数时不进入函数内部,而是将函数调用作为一条指令来执行。
  1. 将wsample01b.exe拖拽到OllyDbg中
  2. OllyDbg的画面主要包括以下几个部分:
  • 左上:主要反汇编窗口
  • 左下:内存数据窗口
  • 右上:寄存器
  • 右下:当前栈
  1. 按下Ctrl+G输入地址00401000,然后跳转到该指令对应的地址:
    有趣的二进制-逆向工程_第12张图片有趣的二进制-逆向工程_第13张图片
  2. 我们发现程序一次调用了GetModuleFileNameW、SHGetFolderPathW、lstrcatW、CopyFileW这几个函数。
  3. 刚才我们用Process Monitor已经发现程序会向“启动”文件夹复制文件,我们可以推测“上面的CopyFile函数就是用来执行这一操作”。
  4. 首选选中地址00401000所在的行,然后按F2键,00401000的背景会变红,说明我们已经在此位置设置了一个断点。
  5. 接下来按下F9键,或者在窗口上方菜单点击Debug->Run。这时,OllyDbg会启动wasmple01b.exe,当到达断点所在的位置00401000位置时,程序会暂停运行。
  6. 我们可以通过单步运行,逐条运行程序中的指令,按F7表示单步跳入,按F8表示单步跳出。
  7. 随着每次按下F8,程序都会执行一条指令,同时右上方的寄存器窗口和右下方的堆栈窗口的内容也会发生变化。
  8. 让程序一直运行到00401062的地方,也就是调用CopyFileW之前的位置,通过寄存器窗口和堆栈窗口,我们可以看到要复制的文件源路径和目标路径。
    在这里插入图片描述
  9. 如果我们再次按下F8键,程序就会调用CopyFileW函数,现在再看一下启动文件夹。
    在这里插入图片描述
  10. 什么是寄存器
  • 寄存器时位于CPU内部的存储空间,每个寄存器都有自己的名字,分别叫做EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI、EIP。
  • ESP和EBP用于管理堆栈,而EIP则指向当前执行的指令
  • OllyDbg右上方的寄存器窗口中会显示当前所有寄存器的值。
    有趣的二进制-逆向工程_第14张图片
  • 如果我们在OllyDbg中按下F8或者F7,程序就会执行一条指令,这是我们可以看到EIP的值会根据所执行指令的长度不断增加。
  • EIP的下方还有C、P、A、Z、S、T、D、O这几个字母,它们代表标志,一般我们会在这些字母后面加上一个字母F(Flag的首字母),写作CF、PF、AF、ZF,这些标志主要用于条件分支,比如:

若ZF=1则跳转。
若CF为1则不跳转。

  1. 程序源代码
int cpy(void)
{
	// 获取自身文件路径
	TCHAR szThis[2048];
	GetModuleFileName(NULL, szThis, sizeof(szThis));
	// 获取启动文件夹路径
	SHGetFolderPath(NULL, CSIDL_STARTUP, 
		NULL, SHGFP_TYPE_CURRENT, szStartup);
	lstrcat(szStartup, _T("\\wsample01b.exe"));
	// 将自身复制到启动文件夹	
	CopyFile(szThis, szStartup, FALSE);
	return 0;
}

int APIENTRY _tWinMain(
	HINSTANCE hInstance, 
	HINSTANCE hPrevInstance, 
	LPTSTR    lpCmdLine, 
	int       nCmdShow)
{
	cpy();
	MessageBox(GetActiveWindow(), 
		_T("Copied!"), _T("MESSAGE"), MB_OK);
	return 0;
}
  1. 在进行软件分析的时候一般使用Stirling和IDA看一下整体的样子,然后再用OllyDbg的单步运行来查看一些特别关注的点。

学习汇编指令

  1. 常用汇编指令
  • MOV EAX,ECX;将ECX的值存入EAX
  • ADD EAX,ECX; EAX+=ECX
  • SUB EAX,ECX; EAX-=ECX
  • INC EAX; EAX++
  • DEC EAX;EAX–
  • LEA EAX,[ECX+4]; 将ECX+4的值存入EAX
  • CMP EAX,ECX; 对两个值比较根据结果设置标志,EAX=ECX,ZF=1;EAX!=ECX,ZF=0.
  • TEST EAX,EAX;将值与0比较并根据结果设置标注,EAX=0,ZF=1;EAX!=0,ZF=0;
  • JE(JZ) 04001000;若ZF=1,则跳转到04001000
  • JNE(JNZ) 04001000;若ZF=0,跳转到0400100
  • JMP 04001000;无条件跳转到0400100
  • CALL lstrcmp;调用lstrcmpW
  • PUSH 00000001;将00000001入栈
  • POP EAX;出栈并将获取的值存入EAX
  1. 汇编语言如何实现条件分支的
  • 汇编语言中,则是通过控制标志的cmp、test指令,以及根据标志完成分支的跳转指令来实现的。
  • 在wsample01.exe中有个判断的逻辑,如果命令行传入参数2012,会显示不同的消息。
    有趣的二进制-逆向工程_第15张图片
  • 其中在0040101B的地方出现一个jnz指令,这就是分支指令。
  • test eax,eax;意思就是当eax为0时将ZF置为1,大多数情况下test指令都会跟着两个相同的寄存器名称,可以简单理解为“若寄存器值为0,则将ZF置为1”。
  • jnz指令的意思就是,ZF=0时则进行跳转,将jnz指令和test指令结合起来就实现了下面的逻辑:

若eax为0则不跳转
若eax为1则跳转

  • eax使0040100C的call lstrcmpW的返回值
  1. 参数存放在栈中
  • call指令是用来调用子程序的,它的返回值被存放在eax中。
  • 子程序的调用可以理解为下面的过程:
function(1,2,3);
//对应汇编代码
push 3
push 2
push 1
call function
  • 在汇编语言中,参数时按照从后往前的顺序入栈,其实这这方面的规则会根据CPU和编译器的不同而存在一些差异。
  • 例如,00401006内存位置对应的代码:
    在这里插入图片描述
  • 对应代码 eax = lstrcmpW(eax,“2012”);
  • lstrcmpW函数功能是,当参数中的两个字符串相同时,则返回0,否则返回非0;
    有趣的二进制-逆向工程_第16张图片
  • 00401019的test指令表示若eax为0则将ZF置为1,0040101B的jnz指令表示当ZF=0时跳转。因此,当ZF=1时程序不会进行跳转,而是继续执行0040101D的指令,结果就显示出了Hello!2012这条消息。

从汇编代码联想到C语言代码

  1. 再来查看以下wsample01b.exe汇编代码
.text:00401000 Filename        = word ptr -2004h
.text:00401000 pszPath         = word ptr -1004h
.text:00401000 var_4           = dword ptr -4
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 mov     eax, 2004h
.text:00401008                 call    __alloca_probe
.text:0040100D                 mov     eax, ___security_cookie
.text:00401012                 xor     eax, ebp
.text:00401014                 mov     [ebp+var_4], eax
.text:00401017                 push    1000h           ; nSize
.text:0040101C                 lea     eax, [ebp+Filename]
.text:00401022                 push    eax             ; lpFilename
.text:00401023                 push    0               ; hModule
.text:00401025                 call    ds:GetModuleFileNameW
.text:0040102B                 lea     ecx, [ebp+pszPath]
.text:00401031                 push    ecx             ; pszPath
.text:00401032                 push    0               ; dwFlags
.text:00401034                 push    0               ; hToken
.text:00401036                 push    7               ; csidl
.text:00401038                 push    0               ; hwnd
.text:0040103A                 call    ds:SHGetFolderPathW
.text:00401040                 push    offset String2  ; "\\wsample01b.exe"
.text:00401045                 lea     edx, [ebp+pszPath]
.text:0040104B                 push    edx             ; lpString1
.text:0040104C                 call    ds:lstrcatW
.text:00401052                 push    0               ; bFailIfExists
.text:00401054                 lea     eax, [ebp+pszPath]
.text:0040105A                 push    eax             ; lpNewFileName
.text:0040105B                 lea     ecx, [ebp+Filename]
.text:00401061                 push    ecx             ; lpExistingFileName
.text:00401062                 call    ds:CopyFileW
.text:00401068                 mov     ecx, [ebp+var_4]
.text:0040106B                 xor     ecx, ebp
.text:0040106D                 xor     eax, eax
.text:0040106F                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:00401074                 mov     esp, ebp
.text:00401076                 pop     ebp
.text:00401077                 retn
.text:00401077 sub_401000      endp
.text:00401077
.text:00401077 ; ---------------------------------------------------------------------------
.text:00401078                 align 10h
.text:00401080
.text:00401080 ; =============== S U B R O U T I N E =======================================
.text:00401080
.text:00401080
.text:00401080 ; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
.text:00401080 _wWinMain@16    proc near               ; CODE XREF: ___tmainCRTStartup+153↓p
.text:00401080
.text:00401080 hInstance       = dword ptr  4
.text:00401080 hPrevInstance   = dword ptr  8
.text:00401080 lpCmdLine       = dword ptr  0Ch
.text:00401080 nShowCmd        = dword ptr  10h
.text:00401080
.text:00401080                 call    sub_401000
.text:00401085                 push    0               ; uType
.text:00401087                 push    offset Caption  ; "MESSAGE"
.text:0040108C                 push    offset Text     ; "Copied!"
.text:00401091                 call    ds:GetActiveWindow
.text:00401097                 push    eax             ; hWnd
.text:00401098                 call    ds:MessageBoxW
.text:0040109E                 xor     eax, eax
.text:004010A0                 retn    10h
.text:004010A0 _wWinMain@16    endp
  • 在cpy函数开头的fileName、pszPath、var_4都是函数使用的局部变量。
  • lea eax, [ebp+Filename]中有个ebp+Filename就是将Filename的地址放入eax中。
  • 对应C语言应该时下面:
char fileName[2048];
eax=finName;

通过汇编指令洞察程序行为

  1. 使用OllyDbg打开sample_mal.exe,然后在反汇编窗口点击右键,选择:
    有趣的二进制-逆向工程_第17张图片
  • 从显示出的函数列表中找到类型为Export的RgesetValueExA函数,OllyDbg支持通过键盘快速查找,只要输入RegSetV…就可以定位到目标函数了。
    有趣的二进制-逆向工程_第18张图片
  • 双击函数就可以就会跳转到该函数的开头,接下来在下列函数位置处设置断点:

RegSetValueExA
RegSetValueExA
RegCreateKeyExA
CopyFileA

  • 上面的目标函数各有两种类型:一种是Export;另一种是Import,选择类型为Export的函数上双击被设置断点。
  • RegSetValueExA、RegSetValueExA、 RegCreateKeyExA位于ADVAPI32模块中,而CopyFileA位于kernel32模块中。
  • 设置好断点后,点击F9运行程序,程序会在断点处暂停运行
  • 按Ctrl+F9程序会继续运行到函数返回的地方
  • CopyFileA函数返回的地方
    有趣的二进制-逆向工程_第19张图片
  1. 反汇编并观察重要逻辑
  • 使用IDA打开sample_mal.exe,来看看复制0.exe和1.exe的地方
    在这里插入图片描述
  • 00401452处的代表SetRegValue函数,0040145A代表selDelete函数,它们分别用来设置注册表以及将自身删除。
  • 查看SetRegValue函数:
.text:00401310
.text:00401310 dwDisposition   = dword ptr -8
.text:00401310 phkResult       = dword ptr -4
.text:00401310 lpData          = dword ptr  8
.text:00401310 cbData          = dword ptr  0Ch
.text:00401310
.text:00401310                 push    ebp
.text:00401311                 mov     ebp, esp
.text:00401313                 sub     esp, 8
.text:00401316                 push    esi
.text:00401317                 lea     eax, [ebp+dwDisposition]
.text:0040131A                 push    eax             ; lpdwDisposition
.text:0040131B                 xor     esi, esi
.text:0040131D                 lea     ecx, [ebp+phkResult]
.text:00401320                 push    ecx             ; phkResult
.text:00401321                 push    esi             ; lpSecurityAttributes
.text:00401322                 push    0F003Fh         ; samDesired
.text:00401327                 push    esi             ; dwOptions
.text:00401328                 push    offset Class    ; lpClass
.text:0040132D                 push    esi             ; Reserved
.text:0040132E                 push    offset SubKey   ; "Software\\Microsoft\\Windows\\CurrentVe"...
.text:00401333                 push    80000002h       ; hKey
.text:00401338                 mov     [ebp+phkResult], esi
.text:0040133B                 call    ds:RegCreateKeyExA
.text:00401341                 test    eax, eax
.text:00401343                 jnz     short loc_401370
.text:00401345                 mov     edx, [ebp+cbData]
.text:00401348                 mov     eax, [ebp+lpData]
.text:0040134B                 mov     ecx, [ebp+phkResult]
.text:0040134E                 push    edx             ; cbData
.text:0040134F                 push    eax             ; lpData
.text:00401350                 push    1               ; dwType
.text:00401352                 push    esi             ; Reserved
.text:00401353                 push    offset ValueName ; "sample_mal"
.text:00401358                 push    ecx             ; hKey
.text:00401359                 call    ds:RegSetValueExA
.text:0040135F                 test    eax, eax
.text:00401361                 jnz     short loc_401366
.text:00401363                 lea     esi, [eax+1]
.text:00401366
.text:00401366 loc_401366:                             ; CODE XREF: sub_401310+51↑j
.text:00401366                 mov     edx, [ebp+phkResult]
.text:00401369                 push    edx             ; hKey
.text:0040136A                 call    ds:RegCloseKey
.text:00401370
.text:00401370 loc_401370:                             ; CODE XREF: sub_401310+33↑j
.text:00401370                 mov     eax, esi
.text:00401372                 pop     esi
.text:00401373                 mov     esp, ebp
.text:00401375                 pop     ebp
.text:00401376                 retn
.text:00401376 sub_401310      endp
  • 查看SelfDelete函数:
.text:00401220 sub_401220      proc near               ; CODE XREF: sub_401380+DA↓p
.text:00401220
.text:00401220 String1         = byte ptr -20Ch
.text:00401220 Filename        = byte ptr -108h
.text:00401220 var_4           = dword ptr -4
.text:00401220
.text:00401220                 push    ebp
.text:00401221                 mov     ebp, esp
.text:00401223                 sub     esp, 20Ch
.text:00401229                 mov     eax, ___security_cookie
.text:0040122E                 xor     eax, ebp
.text:00401230                 mov     [ebp+var_4], eax
.text:00401233                 push    104h            ; nSize
.text:00401238                 lea     eax, [ebp+Filename]
.text:0040123E                 push    eax             ; lpFilename
.text:0040123F                 push    0               ; hModule
.text:00401241                 call    ds:GetModuleFileNameA
.text:00401247                 test    eax, eax
.text:00401249                 jz      loc_4012F3
.text:0040124F                 push    104h            ; cchBuffer
.text:00401254                 lea     ecx, [ebp+Filename]
.text:0040125A                 push    ecx             ; lpszShortPath
.text:0040125B                 mov     edx, ecx
.text:0040125D                 push    edx             ; lpszLongPath
.text:0040125E                 call    ds:GetShortPathNameA
.text:00401264                 test    eax, eax
.text:00401266                 jz      loc_4012F3
.text:0040126C                 push    esi
.text:0040126D                 push    offset aCDel    ; "/c del "
.text:00401272                 lea     eax, [ebp+String1]
.text:00401278                 push    eax             ; lpString1
.text:00401279                 call    ds:lstrcpyA
.text:0040127F                 mov     esi, ds:lstrcatA
.text:00401285                 lea     ecx, [ebp+Filename]
.text:0040128B                 push    ecx             ; lpString2
.text:0040128C                 lea     edx, [ebp+String1]
.text:00401292                 push    edx             ; lpString1
.text:00401293                 call    esi ; lstrcatA
.text:00401295                 push    offset aNul     ; " >> NUL"
.text:0040129A                 lea     eax, [ebp+String1]
.text:004012A0                 push    eax             ; lpString1
.text:004012A1                 call    esi ; lstrcatA
.text:004012A3                 push    104h            ; nSize
.text:004012A8                 lea     ecx, [ebp+Filename]
.text:004012AE                 push    ecx             ; lpBuffer
.text:004012AF                 push    offset Name     ; "ComSpec"
.text:004012B4                 call    ds:GetEnvironmentVariableA
.text:004012BA                 pop     esi
.text:004012BB                 test    eax, eax
.text:004012BD                 jz      short loc_4012F3
.text:004012BF                 push    0               ; nShowCmd
.text:004012C1                 push    0               ; lpDirectory
.text:004012C3                 lea     edx, [ebp+String1]
.text:004012C9                 push    edx             ; lpParameters
.text:004012CA                 lea     eax, [ebp+Filename]
.text:004012D0                 push    eax             ; lpFile
.text:004012D1                 push    0               ; lpOperation
.text:004012D3                 push    0               ; hwnd
.text:004012D5                 call    ds:ShellExecuteA
.text:004012DB                 cmp     eax, 20h
.text:004012DE                 jle     short loc_4012F3
.text:004012E0                 mov     eax, 1
.text:004012E5                 mov     ecx, [ebp+var_4]
.text:004012E8                 xor     ecx, ebp
.text:004012EA                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:004012EF                 mov     esp, ebp
.text:004012F1                 pop     ebp
.text:004012F2                 retn
  1. 学习编写汇编代码
  • 安装NSAM汇编器,连接器ALINK
  • 新建一个文件,拓展名改为asm
extern MessageBoxA

section .text
global main

main:
         push dword 0
         push dword title
         push dword text
         push dword 0
         call MessageBoxA
         ret

section .data
title: db 'MessageBox', 0
text: db  'Hello World!', 0
  • MessageBoxA需要以下4个参数

父窗口句柄
要显示的消息
要显示的消息框标题
要显示的消息框类型

  • 函数调用过程:

要显示的消息:Hello World!
要显示的标题:MessageBox
将参数按照从后往前的顺序入栈
用call MessageBoxA调用函数

  • 用NASM加上-fwin32参数将代码汇编为.obj文件
  • 用Alink生产可执行文件

你可能感兴趣的:(有趣的二进制)