C++言語を使用している際に、直接アセンブリで記述した機械語(=マシン語)を実行します。
はじめに
本例は、文字列比較を行うstrcmpと同じ動作を行う機械語を示します。直前の
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, 0xF1, // mov rsi,rcx 0x48, 0x8B, 0xFA, // mov rdi,rdx 0xFC, // cld 0x8A, 0x06, // mov al,byte ptr [rsi] 0x84, 0x07, // test byte ptr [rdi],al 0x74, 0x1D, // je 02D 0xA6, // cmps byte ptr [rsi],byte ptr [rdi] 0x74, 0xF7, // je 00A 0x48, 0xFF, 0xCE, // dec rsi 0x48, 0xFF, 0xCF, // dec rdi 0x8A, 0x06, // mov al,byte ptr [rsi] 0x2A, 0x07, // sub al,byte ptr [rdi] 0xA9, 0x80, 0x00, // test eax,80h 0x00, 0x00, 0x75, 0x0E, // jne 032 0x48, 0xC7, 0xC0, 0x01, // mov rax,1 0x00, 0x00, 0x00, 0xEB, 0x0C, // jmp 039 0x48, 0x33, 0xC0, // xor rax,rax 0xEB, 0x07, // jmp 039 0x48, 0xC7, 0xC0, 0xFF, // mov rax,-1 0xFF, 0xFF, 0xFF, 0x5F, // pop rdi 0x5E, // pop rsi 0xC3 // ret }; try { void* mem = allocAndCopyIt(code, sizeof(code)); int (*func)(char*, char*) = (int (*)(char*, char*))mem; char src[] = "This is a pen.", dst[256]; strcpy(dst, src); cout << "src: \"" << src << "\"" << endl; cout << "dst: \"" << dst << "\"" << endl; cout << " asm: " << func(dst, src) << endl; // asm cout << " C++: " << strcmp(dst, src) << endl; // C++ src[10] = 'z'; cout << "src: \"" << src << "\"" << endl; cout << "dst: \"" << dst << "\"" << endl; cout << " asm: " << func(dst, src) << endl; // asm cout << " C++: " << strcmp(dst, src) << endl; // C++ src[10] = '0'; cout << "src: \"" << src << "\"" << endl; cout << "dst: \"" << dst << "\"" << endl; cout << " asm: " << func(dst, src) << endl; // asm cout << " C++: " << strcmp(dst, src) << endl; // C++ freeMem(mem); } catch (const char* str) { cerr << str << endl; return -1; } return 0; }
allocAndCopyIt関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
freeMem関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
実行
機械語で作成した関数を呼び出し、結果を表示します。
実行結果
C:\>cl /O2 /EHsc strcmp.cpp
...
C:\>.\strcmp
src: "This is a pen."
dst: "This is a pen."
asm: 0
C++: 0
src: "This is a zen."
dst: "This is a pen."
asm: -1
C++: -1
src: "This is a 0en."
dst: "This is a pen."
asm: 1
C++: 1
機械語
unsigned char code[]が機械語の定義を行っている部分です。これを実行可能なメモリーへ転送し、C++から直接制御を渡します。この例では、以下のアセンブリ言語で記述したソースコードを機械語化したものです。
include ksamd64.inc _TEXT segment public asmCode asmCode proc frame ; prolog rex_push_reg rsi ; push rsi push_reg rdi ; push rdi .endprolog ; mov rsi, rcx ; rsi = rcx str0 mov rdi, rdx ; rdi = rdx str1 cld loop1: mov al, [rsi] ; load [rsi] test al, [rdi] ; al or byte [rdi] == null jz equend ; if equal, goto equend cmpsb ; compare [rdi], [rsi] and then inc rdi, rsi je loop1 ; if equal, goto loop1 dec rsi ; if not equal dec rdi ; dec rdi strend: ; set ret valiue mov al, [rsi] ; byte [rdi]をalに代入 sub al, [rdi] test eax, 80h jnz minus mov rax,1 ; set ret valiue = 1 jmp procend equend: xor rax, rax jmp procend minus: mov rax, -1 ; set ret valiue = -1 procend: ; 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, 0xF1, // mov rsi,rcx 0x48, 0x8B, 0xFA, // mov rdi,rdx 0xFC, // cld 0x8A, 0x06, // mov al,byte ptr [rsi] 0x84, 0x07, // test byte ptr [rdi],al 0x74, 0x1D, // je 02D 0xA6, // cmps byte ptr [rsi],byte ptr [rdi] 0x74, 0xF7, // je 00A 0x48, 0xFF, 0xCE, // dec rsi 0x48, 0xFF, 0xCF, // dec rdi 0x8A, 0x06, // mov al,byte ptr [rsi] 0x2A, 0x07, // sub al,byte ptr [rdi] 0xA9, 0x80, 0x00, // test eax,80h 0x00, 0x00, 0x75, 0x0E, // jne 032 0x48, 0xC7, 0xC0, 0x01, // mov rax,1 0x00, 0x00, 0x00, 0xEB, 0x0C, // jmp 039 0x48, 0x33, 0xC0, // xor rax,rax 0xEB, 0x07, // jmp 039 0x48, 0xC7, 0xC0, 0xFF, // mov rax,-1 0xFF, 0xFF, 0xFF, 0x5F, // pop rdi 0x5E, // pop rsi 0xC3 // ret };