C++で機械語を実行(文字列コピー, strcpy)

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関数

以前説明したものと同じです、処理についてはソースコード、あるいは以前の説明を参照してください。

main関数

本関数の処理を順次説明します。
unsigned char code[]が機械語を保持するunsigned charの配列です。

allocAndCopyIt関数を呼び出し、必要な実行可能メモリーを割り当て、そこへ機械語をコピーします。返却値はvoid*ですので、関数のシグネチャへ合わせたいためポインタの代入を行います。これによってfuncを通常の関数と同じように呼び出すことができます。
func関数に2つの文字列を与え、元の文字列を転送先の文字列へコピーします。

機械語

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
};