編程小知識之 JIT執行
- 2019 年 11 月 22 日
- 筆記
版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/tkokof1/article/details/103179521
本文簡單介紹了 JIT 如何動態執行 native code 的知識
自己之前對 DEP(數據執行保護) 有些依稀印象,簡單認為在一般情況下,程式是不能動態執行 native code 的,後來突然想到 JIT(簡單理解就是一種動態生成並執行 native code 的技術),雖然 JIT 的關注點大多都在如何生成 native code 上,但是自己卻對 JIT 如何動態執行 native code 更有興趣,因為這與我之前對 DEP 的認知相悖.
實際上, DEP 並不是完全禁止 native code 的動態執行,而是禁止"沒有執行許可權"的 native code 的動態執行,而所謂"沒有執行許可權"的 native code,就是指那些存儲於沒有執行許可權的記憶體區域的 native code,對於一般程式而言,其相關的堆棧(可操作記憶體區域)都僅有讀寫許可權,而沒有執行許可權,所以一般程式是不能動態執行 native code 的.
但是作業系統基本都提供了申請擁有可執行許可權的記憶體區域的方法,基於此,我們便可以動態執行 native code 了:(程式碼基本來自這裡,有一些改動)
// x86 Windows #include <stdio.h> #include <assert.h> #include <windows.h> typedef unsigned char byte; typedef void (*pfunc)(void); union funcptr { pfunc x; byte* y; }; int dynamic_multiply(int arg1, int arg2) { // alloc executable memory HANDLE process = GetCurrentProcess(); byte* buf = (byte*)VirtualAllocEx(process, 0, 1 << 16, MEM_COMMIT, PAGE_EXECUTE_READWRITE); assert(buf != 0); byte* p = buf; *p++ = 0x50; // push eax *p++ = 0x52; // push edx // mov eax, [arg2] *p++ = 0xA1; (int*&)p[0] = &arg2; p += sizeof(int*); *p++ = 0x92; // xchg edx,eax // mov eax, [arg1] *p++ = 0xA1; (int*&)p[0] = &arg1; p += sizeof(int*); // imul edx *p++ = 0xF7; *p++ = 0xEA; // mov [ret],eax int ret = 0; *p++ = 0xA3; (int*&)p[0] = &ret; p += sizeof(int*); *p++ = 0x5A; // pop edx *p++ = 0x58; // pop eax *p++ = 0xC3; // ret // call generated code (using union) funcptr func; func.y = buf; func.x(); // release executable memory VirtualFreeEx(process, buf, 0, MEM_RELEASE); return ret; }
由於 native code 的關係,上述程式碼是與架構和平台相關的(x86 Windows),在程式碼中,我們直接申請了擁有可執行許可權(並帶有讀寫許可權)的記憶體區域,一種更安全的做法是,首先申請擁有可執行許可權(並帶有讀寫許可權)的記憶體區域,生成完 native code 數據之後,再將該記憶體區域的寫許可權去除.
參考資料
- How to generate and run native code dynamically?
- execute binary machine code from C
- JIT compilation and DEP