なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ

書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

2022/06/15

DLL インジェクションして任意の処理を差し込みたい 1

カテゴリー:WindowsC++

任意の Windows アプリケーションにたいして、 DLL Injection を行って処理を差し込んでみよう!という記事です。
あくまで DLL Injection についての解説記事であって、ウィルスの作成だったりチートを助長するような目的ではありませんのであしからず。

DLL Injection といってもいくつか方法があるようですが、今回は LoadLibraryCreateRemoteThread を使った方法の紹介です。
次回は任意の処理の置き換えについて書きますが、今回は Injection するところまで。

Inject させる側は、以下のようなコードで実現が可能です。

#include <clocale>
#include <cstdio>
#include <iostream>
#include <Windows.h>

int main(int argc, char* argv[])
{
    char path[MAX_PATH];
    GetCurrentDirectoryA(MAX_PATH, path);
    strcat_s(path, sizeof(path), "\\path\\to\\inject.dll");

    const auto pid = strtoul(argv[1], nullptr, 0);
    const auto hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!hProcess)
        return EXIT_FAILURE;

    const auto lpBaseAddress = VirtualAllocEx(hProcess, nullptr, sizeof(path) + 1, MEM_COMMIT, PAGE_READWRITE);
    if (!lpBaseAddress)
        return EXIT_FAILURE;

    WriteProcessMemory(hProcess, lpBaseAddress, path, sizeof(path) + 1, nullptr);

    const auto kernel32 = LoadLibraryW(L"kernel32");
    if (!kernel32)
        return EXIT_FAILURE;

    const auto address = GetProcAddress(kernel32, "LoadLibraryA");
    const auto hThread = CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)address, lpBaseAddress, 0, nullptr);

    if (!hThread)
        return EXIT_FAILURE;

    const auto hResult = WaitForSingleObject(hThread, INFINITE);
    if (!hResult)
        return EXIT_FAILURE;

    CloseHandle(hThread);

    return EXIT_SUCCESS;
}

やっていることとしては、

  1. OpenProcess で指定されたプロセスにたいして、プロセスをオープンしプロセスハンドル (hProcess) を取得する
  2. VirtualAllocEx で、上で取得したプロセス内で、メモリを確保する
  3. WriteProcessMemory で対象プロセスのメモリ上にロードしたい DLL のパスを書き込む
    1. 絶対パスで無いと動かないので注意してください
  4. kernel32.dllLoadLibraryCreateRemoteThread のスレッド開始ルーチンをして渡す
    1. LPTHREAD_START_ROUTINEDWORD WINAPI SomeFunction(LPVOID parameter) の形であれば良いので、同じ関数シグネチャーをもつ HMODULE LoadLibrary(PLCWSTR lpLibFileName) が実行可能
  5. WaitForSingleObject でスレッドの完了まで待つ

といった感じ。

Inject される側のコードは通常の DLL を作るのと同じで良いですが、あえて載せるとこんな感じ:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call) // NOLINT(hicpp-multiway-paths-covered)
    {
    case DLL_PROCESS_ATTACH:
        OutputDebugString("Hello, World!");
        break;

    default:
        break;
    }

    return TRUE;
}

デバッガーをアタッチするのが面倒なら、 MessageBoxA とかでも良いです。 これで、 injector.exe PID とかすると、指定されたプロセスに読み込まれ、 Hello, World! が何らかの方法で表示されます。

ということで、 DLL Injection の方法のメモでした。
次回は、任意の処理の置き換えについて書く予定です。