的基本原理,C 語言主要常用的不外乎就是 stdcall、cdecl 與 fastcall,在 x86-64 的環境裡面
也曾經提到過全部統一用 fastcall,可是參數在傳遞的過程中,讀者有沒有想過,是否能夠將
資料直接嵌入在程式區段中,而不需要宣告資料區段呢?這就是今天的主題,敝人將示範一種
特別的技巧,讓資料直接混合在程式區段內傳遞給一條簡單的 Windows API 作為示範。
今天要討論的示範程式很簡單,使用 MessageBox API 顯示一個小小的對話方塊
用 C 語言寫很簡單,原始碼如下:
#include <windows.h>
static TCHAR g_szText[] = _T("Hi! I'm the fasm program");
static TCHAR g_szText[] = _T("Hi! I'm the fasm program");
int APIENTRY WinMain(HINSTANCE,HINSTANCE,LPSTR,INT)
{
MessageBox(
{
MessageBox(
HWND_DESKTOP, // A handle to the owner windows of the message box
g_szText, // The message to be displayed.
GetCommandLine(), // The dialog box title.
MB_OK); // The contents and behavior of the dialog box.
return 0
g_szText, // The message to be displayed.
GetCommandLine(), // The dialog box title.
MB_OK); // The contents and behavior of the dialog box.
return 0
}
這個程式執行結果如下:
現在把上面那段小小的 Win32 程式用 FASM 重寫,原始碼如下:
format PE GUI 4.0 entry start include 'win32a.inc' section '.text' code readable executable start: push ebp ; store old value of ebp mov ebp,esp ; create a stack frame push ebx esi edi ; store volatile registers invoke GetCommandLine push MB_OK push eax push g_szText push HWND_DESKTOP call [MessageBox] pop edi esi ebx leave invoke ExitProcess, 0 section '.data' data readable writeable g_szText db "Hi! I'm the fasm program!" section '.idata' import data readable writeable library kernel,'kernel32.dll', \ user,'user32.dll' import kernel, \ GetCommandLine,'GetCommandLineA', \ ExitProcess, 'ExitProcess' import user, \ MessageBox,'MessageBoxA'
讀者可以看到用 FASM 寫也是很簡單就可以完成一樣的功能,執行結果當然就跟剛剛上面
的圖一樣,只是還要自己手動寫 .idata 區段引入想要用的 API,讀者在這邊可以注意到吾
人改用了一條指令 leave,它其實跟另外兩條指令的組合等價:
傳統常用的寫法 新式寫法
mov esp,ebp leave
pop ebp
這是在函式的閉幕碼內常常出現的樣版,mov esp, ebp 可以先破壞函式內的區域變數堆疊
然後在恢復原本的 ebp,後來 x86 組合語言乾脆弄一條指令 leave,本身可讀性也必較好
新式的 Compiler 應該都會採用 leave,敝人自己在寫 FASM 也已經改用 leave。
然後讓我們來想想一個問題,有沒有可能直接把原本需要放在 .data 區段內的 g_szText
位址上的資料直接混合在 .text 區段內,也就是直接嵌入在程式區段裡面,要討論這個
問題之前,先回想一下 x86 組合語言指令 call 有甚麼特性。
還記得以前呼叫慣例系列文章有講過,call 不僅會把下一段返回後要執行的程式位址推入
堆疊中,它還有跳躍的功能,所以讀者發現了沒有,call 指令不光只是用在呼叫函式這種
正規用途 ( 也就是被呼叫函式內有要 ret 搭配 ),還可以拿來用於非正規用途,拿它投機取巧
把資料直接嵌入在程式區段,吾人下面先貼出不同處給讀者看看:
push MB_OK push eax call @F db "Hi! I'm the fasm program!",00h @@: push HWND_DESKTOP call [MessageBox]
什麼!!資料竟然跟程式碼混合在一塊,這就是 call 指令的另一種妙用,少數用 call 指令而沒
有搭配 ret 的情況,因為 call 把下一段位址先推入堆疊啦,然後巧妙跨過資料而直接跳躍到
push HWND_DESKTOP,所以這種技巧不會讓 CPU 執行程式的時候誤判,不過顯然除錯器
Ollydbg 也知道這種技巧,當在下把編譯後的執行檔放入 Ollydbg 反組譯後出現下圖:
讀者點圖放大看可以發現,Ollydbg 很聰明的發現了吾人使用了嵌入式的資料直接
跟程式區段混合在一起,本篇主要就是介紹這種技巧給讀者知道參數的傳遞能夠用
call 指令巧妙的將資料嵌入在程式區段中,對於一些短的顯示字串或是常數,也許
直接用此技巧嵌入在程式碼區段中會比較方便,也不用去花腦袋思考變數命名的問題
不過這種技巧在 Ollydbg 內遇到那段 call 指令記得要用 step into,別用 step over,因為
CPU 在執行程式碼遇到 call 基本上就是認為要跳進某個函式,所以你得要用 into,假如
你用 over,那就會發現程式一路往下走就跑完了,因為程式永遠遇不到 ret 指令。
後記:
基於類似的理由,個人就對 FASM 提供的匿名標籤功能充滿敬意,它讓我不用在去思考
label 的名稱到底要怎麼令,反正 @F 就會往下找最近的 @@,而 @B 就會往回找最近的
@@,這個功能實在很方便,但是 FASM 要提供這種功能所實現的程式碼可就不簡單了。
對組譯器技術有興趣的讀者,可以專研 FASM Assembler 的原始碼,該組譯器是完全用純
x86 Assembly 完成,要閱讀它的程式碼基本上難度很高,我的過來經驗是最好要能閱讀
用 C 寫成的 NASM Assembler 的原始碼在去看 FASM Assembler 的原始碼,因為該作者
用組語大概已經達到神人境界,程式碼裡面會用到很多只有組合語言才能表達的特殊技巧
而且沒有對應的 C 概念可以表達,本文的示範就是其中一例,讀者在 C 語言中就很難辦到
組合語言這種可以偷機取巧用 call 指令指把資料混合在程式區段。當然有過 DOS 低階編程
的 LKK 們或許就覺得沒啥新鮮,在 DOS 中還可以直接操作節區,直接讓 ds 就等於當前的
cs 呢,在 DOS 下要怎麼搞都可以。
LuckyClub Casino Site | Live dealer casino and a world class
回覆刪除LuckyClub Casino is one of the leading gambling portals that have been operating since 2006. It's the first of many online casino sites that cater to Asian 카지노사이트luckclub