C++言語を使用している際に、直接アセンブリで記述した機械語(=マシン語)を実行します。
はじめに
本例は、文字列長を求めるstrlenと同じ動作を行う機械語を示します。文字の扱いは複雑ですが、ここではASCII文字のみを考えます。以前の
arques.hatenablog.com
を改良します。
C++コード
C++で記述したソースコードへ機械語を取り込みます。プログラムは以前のものと同じ構造です。
include <iostream> #include <windows.h> using namespace std; // alloc memory and copy code to it void* allocAndCopyIt(unsigned char code[], size_t size) { void* mem = VirtualAlloc( NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!mem) throw "VirtualAlloc failed."; memcpy(mem, code, size); return mem; } // free memory void freeMem(void* mem) { if (!VirtualFree(mem, 0, MEM_RELEASE)) throw "VirtualFree failed"; } int main(void) { unsigned char code[] = { 0x48, 0x57, // push rdi 0x48, 0x8B, 0xF9, // mov rdi,rcx 0x48, 0xC7, 0xC1, 0xFF, // mov rcx,7FFFFFFFh 0xFF, 0xFF, 0x7F, 0x32, 0xC0, // xor al,al 0xFC, // cld 0xF2, 0xAE, // repne scas byte ptr [rdi] 0x48, 0xC7, 0xC0, 0xFE, // mov rax,7FFFFFFFh 0xFF, 0xFF, 0x7F, 0x48, 0x2B, 0xC1, // sub rax,rcx 0x5F, // pop rdi 0xC3 // ret }; try { void* mem = allocAndCopyIt(code, sizeof(code)); int (*func)(char*) = (int (*)(char*))mem; char src[] = "This is a pen."; int length = func(src); cout << "strlen(\"" << src << "\") = " << length << endl; freeMem(mem); } catch (const char* str) { cerr << str << endl; return -1; } return 0; }
allocAndCopyIt関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
freeMem関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
実行
機械語で作成した関数を呼び出し、結果を表示します。
実行結果
C:\>cl /O2 /EHsc strlen.cpp
...
C:\>.\strlen
strlen("This is a pen.") = 14
機械語
unsigned char code[]が機械語の定義を行っている部分です。これを実行可能なメモリーへ転送し、C++から直接制御を渡します。この例では、以下のアセンブリ言語で記述したソースコードを機械語化したものです。
include ksamd64.inc INT_MAX = 7fffffffh ; 2^31 - 1 _TEXT segment public asmCode asmCode proc frame ; prolog rex_push_reg rdi ; push rdi .endprolog ; mov rdi, rcx ; source address mov rcx, INT_MAX ; max string length xor al, al ; al = NULL char cld ; clear direction flag repne scasb ; search NULL char mov rax, INT_MAX-1 sub rax, rcx ; length of string ; epilog pop rdi ; pop rdi ret asmCode endp _TEXT ends end
機械語への変換はml64コマンドとdumpbin コマンドを使うと簡単に得られます。得られた16進数の値を以下に示します。コメントを参照すると何を行っているか分かるでしょう。
unsigned char code[] = { 0x48, 0x57, // push rdi 0x48, 0x8B, 0xF9, // mov rdi,rcx 0x48, 0xC7, 0xC1, 0xFF, // mov rcx,7FFFFFFFh 0xFF, 0xFF, 0x7F, 0x32, 0xC0, // xor al,al 0xFC, // cld 0xF2, 0xAE, // repne scas byte ptr [rdi] 0x48, 0xC7, 0xC0, 0xFE, // mov rax,7FFFFFFFh 0xFF, 0xFF, 0x7F, 0x48, 0x2B, 0xC1, // sub rax,rcx 0x5F, // pop rdi 0xC3 // ret };