C++言語を使用している際に、直接アセンブリで記述した機械語(=マシン語)を実行します。
はじめに
本例は、文字列コピーを行うstrcpyと同じ動作を行う機械語を示します。以前の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, 0x56, // push rsi 0x57, // push rdi 0x48, 0x8B, 0xFA, // mov rdi,rdx 0x4C, 0x8B, 0xC1, // mov r8,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, 0xFF, // mov rax,7FFFFFFFh 0xFF, 0xFF, 0x7F, 0x48, 0x2B, 0xC1, // sub rax,rcx 0x48, 0x8B, 0xC8, // mov rcx,rax 0x49, 0x8B, 0xF8, // mov rdi,r8 0x48, 0x8B, 0xF2, // mov rsi,rdx 0xF3, 0xA4, // rep movs byte ptr [rdi],byte ptr [rsi] 0x5F, // pop rdi 0x5E, // pop rsi 0xC3 // ret }; try { void* mem = allocAndCopyIt(code, sizeof(code)); void (*func)(char*, char*) = (void (*)(char*, char*))mem; char src[] = "This is a pen.", dst[256]; func(dst, src); cout << "in : \"" << src << "\"" << endl; cout << "out: \"" << dst << "\"" << endl; freeMem(mem); } catch (const char* str) { cerr << str << endl; return -1; } return 0; }
allocAndCopyIt関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
freeMem関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
実行
機械語で作成した関数を呼び出し、結果を表示します。
実行結果
C:\>cl /O2 /EHsc strcpy.cpp
...
C:\>.\strcpy
in : "This is a pen."
out: "This is a pen."
機械語
unsigned char code[]が機械語の定義を行っている部分です。これを実行可能なメモリーへ転送し、C++から直接制御を渡します。この例では、以下のアセンブリ言語で記述したソースコードを機械語化したものです。
include ksamd64.inc INT_MAX = 7fffffffh ; 2^31 - 1 _TEXT segment public asmCode asmCode proc frame ; prolog rex_push_reg rsi ; push rsi push_reg rdi ; push rdi .endprolog ; mov rdi, rdx ; srch string address mov r8, rcx ; save destination address mov rcx, INT_MAX ; length xor al, al ; al = null cld ; clear direction flag repne scasb ; srch NULL char mov rax, INT_MAX sub rax, rcx ; rax = length(include w/ null) mov rcx, rax ; rcx = rax mov rdi, r8 ; restore destination address mov rsi, rdx ; restore source address rep movsb ; move it ; epilog pop rdi ; pop rdi pop rsi ; pop rsi ret asmCode endp _TEXT ends end
機械語への変換はml64コマンドとdumpbin コマンドを使うと簡単に得られます。得られた16進数の値を以下に示します。コメントを参照すると何を行っているか分かるでしょう。
unsigned char code[] = { 0x48, 0x56, // push rsi 0x57, // push rdi 0x48, 0x8B, 0xFA, // mov rdi,rdx 0x4C, 0x8B, 0xC1, // mov r8,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, 0xFF, // mov rax,7FFFFFFFh 0xFF, 0xFF, 0x7F, 0x48, 0x2B, 0xC1, // sub rax,rcx 0x48, 0x8B, 0xC8, // mov rcx,rax 0x49, 0x8B, 0xF8, // mov rdi,r8 0x48, 0x8B, 0xF2, // mov rsi,rdx 0xF3, 0xA4, // rep movs byte ptr [rdi],byte ptr [rsi] 0x5F, // pop rdi 0x5E, // pop rsi 0xC3 // ret };