C++言語を使用している際に、直接アセンブリで記述した機械語(=マシン語)を実行します。
はじめに
本例は、バッファー比較を行うmemcmpと同じ動作を行う機械語を示します。以前の
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 0x49, 0x8B, 0xC8, // mov rcx,r8 0xFC, // cld 0xF3, 0xA6, // repe cmps byte ptr [rsi],byte ptr [rdi] 0x75, 0x05, // jne 0000000000000016 0x48, 0x33, 0xC0, // xor rax,rax 0xEB, 0x21, // jmp 0000000000000037 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, 0x00, // test eax,80h 0x00, 0x75, 0x09, // jne 0000000000000030 0x48, 0xC7, 0xC0, 0x01, // mov rax,1 0x00, 0x00, 0x00, 0xEB, 0x07, // jmp 0000000000000037 0x48, 0xC7, 0xC0, 0xFF, // mov rax,0FFFFFFFFFFFFFFFFh 0xFF, 0xFF, 0xFF, 0x5F, // pop rdi 0x5E, // pop rsi 0xC3 // ret }; try { void* mem = allocAndCopyIt(code, sizeof(code)); int (*func)(char*, char*, size_t) = (int (*)(char*, char*, size_t))mem; char src[] = "This is a pen.", dst[256]; const size_t length = strlen(src) + 1; strcpy(dst, src); cout << "src: \"" << src << "\"" << endl; cout << "dst: \"" << dst << "\"" << endl; cout << " asm: " << func(dst, src, length) << endl; // asm cout << " C++: " << memcmp(dst, src, length) << endl; // C++ src[10] = 'z'; cout << "src: \"" << src << "\"" << endl; cout << "dst: \"" << dst << "\"" << endl; cout << " asm: " << func(dst, src, length) << endl; // asm cout << " C++: " << memcmp(dst, src, length) << endl; // C++ src[10] = '0'; cout << "src: \"" << src << "\"" << endl; cout << "dst: \"" << dst << "\"" << endl; cout << " asm: " << func(dst, src, length) << endl; // asm cout << " C++: " << memcmp(dst, src, length) << endl; // C++ freeMem(mem); } catch (const char* str) { cerr << str << endl; return -1; } return 0; }
allocAndCopyIt関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
freeMem関数
以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。
実行
機械語で作成した関数を呼び出し、結果を表示します。
実行結果
C:\>cl /O2 /EHsc memcmp.cpp
...
C:\>.\memcmp
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 ; str0 address mov rdi, rdx ; str1 address mov rcx, r8 ; length cld repe cmpsb jne notequ ; if not equal, goto notequ xor rax, rax ; equal all jmp procend ; set ret valiue = 0 notequ: ; memcmpと互換を持たせる処理 dec rsi dec rdi mov al, [rsi] sub al, [rdi] test eax, 80h jnz minus mov rax,1 ; set ret valiue = 1 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 0x49, 0x8B, 0xC8, // mov rcx,r8 0xFC, // cld 0xF3, 0xA6, // repe cmps byte ptr [rsi],byte ptr [rdi] 0x75, 0x05, // jne 0000000000000016 0x48, 0x33, 0xC0, // xor rax,rax 0xEB, 0x21, // jmp 0000000000000037 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, 0x00, // test eax,80h 0x00, 0x75, 0x09, // jne 0000000000000030 0x48, 0xC7, 0xC0, 0x01, // mov rax,1 0x00, 0x00, 0x00, 0xEB, 0x07, // jmp 0000000000000037 0x48, 0xC7, 0xC0, 0xFF, // mov rax,0FFFFFFFFFFFFFFFFh 0xFF, 0xFF, 0xFF, 0x5F, // pop rdi 0x5E, // pop rsi 0xC3 // ret };