首页 > 解决方案 > 如何在另一个进程中挂钩托管库中的方法而不通过 dll 注入修改它们?

问题描述

我使用这个存储库将我的代码注入到另一个正在运行的进程 (c++) 中的特定方法中。一切正常,我可以运行示例,但是当我将情况更改为托管库(使用托管类库 (c#) 的控制台应用程序 (c#))时,它并不成功。

第一种情况:(正常工作)

目标:“B - 来自 DLL 的具有自由功能的目标”

#include <stdio.h>
#include <Windows.h>
#include <Psapi.h>

#define DLL_NAME "B2 - GetNum-DLL.dll"
#define DLL_FUNC_NAME "GetNum"


void GetPathToDLL(char* outPath, size_t outPathSize)
{
    char relPath[1024];
    char thisAppName[1024];
    GetModuleFileName(NULL, relPath, 1024);
    GetModuleBaseName(GetCurrentProcess(), NULL, thisAppName, 1024);
    char* replaceStart = strstr(relPath, thisAppName);
    const char* dllName = DLL_NAME;
    memcpy(replaceStart, dllName, strlen(dllName));
    memset(replaceStart + strlen(dllName), '\0', &relPath[1024] - (replaceStart + strlen(dllName)));

    _fullpath(outPath, relPath, outPathSize);
}

int main()
{
    char dllPath[1024];
    GetPathToDLL(dllPath, 1024);

    HMODULE sharedLib = LoadLibrary(dllPath);
    int(*getNum)() = (int(*)()) GetProcAddress(sharedLib, DLL_FUNC_NAME);

    while (1)
    {
        printf("GetNum: %i\n", getNum());
        Sleep(5000);
    }
    return 0;
}

目标导入的 dll:“B2 - GetNum-DLL”

#include "getnum-dll.h"

int GetNum()
{
    return 1;
}

注射器:“13 - Trampoline Imported Func With DLL Injection”

#include "..\hooking_common.h"

#define TARGET_APP_NAME "B - Target With Free Function From DLL.exe"
#define PAYLOAD_DLL_NAME "13B - Trampoline Imported Func DLL Payload.dll"
#define PAYLOAD_FUNC_NAME "GetNumPayload"

void InjectPayload(HANDLE process, const char* pathToPayloadDLL)
{
    //write the name of our dll to the target process' memory
    size_t dllPathLen = strlen(pathToPayloadDLL);
    void* dllPathRemote = VirtualAllocEx(
        process,
        NULL, //let the system decide where to allocate the memory
        dllPathLen,
        MEM_COMMIT, //actually commit the virtual memory
        PAGE_READWRITE); //mem access for committed page

    check(dllPathRemote);

    BOOL writeSucceeded = WriteProcessMemory(
        process,
        dllPathRemote,
        pathToPayloadDLL,
        dllPathLen,
        NULL);

    check(writeSucceeded);

    PTHREAD_START_ROUTINE loadLibraryFunc = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32.dll")), "LoadLibraryA");
    check(loadLibraryFunc);

    //create a thread in remote process that loads our target dll using LoadLibraryA
    HANDLE remoteThread = CreateRemoteThread(
        process,
        NULL, //default thread security
        0, //stack size for thread
        loadLibraryFunc, //pointer to start of thread function (for us, LoadLibraryA)
        dllPathRemote, //pointer to variable being passed to thread function
        0, //0 means the thread runs immediately after creation
        NULL); //we don't care about getting back the thread identifier

    check(remoteThread);

    // Wait for the remote thread to terminate
    WaitForSingleObject(remoteThread, INFINITE);

    //once we're done, free the memory we allocated in the remote process for the dllPathname, and shut down
    VirtualFreeEx(process, dllPathRemote, 0, MEM_RELEASE);
    CloseHandle(remoteThread);
}

//hacky way to get the path to the correct payload for
//whatever the active build config is... saves having to 
//provide the path on the command line, but is otherwise
//not particularly important
void GetPathToPayloadDLL(char* outBuff)
{
    char relPath[1024];
    char thisAppName[1024];
    GetModuleFileName(NULL, relPath, 1024);
    GetModuleBaseName(GetCurrentProcess(), NULL, thisAppName, 1024);
    char* replaceStart = strstr(relPath, thisAppName);
    const char* payloadDLLName = PAYLOAD_DLL_NAME;
    memcpy(replaceStart, payloadDLLName, strlen(payloadDLLName));
    memset(replaceStart + strlen(payloadDLLName), '\0', &relPath[1024] - (replaceStart + strlen(payloadDLLName)));

    _fullpath(outBuff, relPath, 1024);
}

int main(int argc, const char** argv)
{
    //5 check(argc == 2);

    DWORD processID = FindPidByName(TARGET_APP_NAME);
    check(processID);

    HANDLE remoteProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
    check(remoteProcessHandle);

    char fullPath[1024];
    GetPathToPayloadDLL(fullPath);
    HMODULE mod = FindModuleBaseAddress(remoteProcessHandle, fullPath);

    InjectPayload(remoteProcessHandle, fullPath);

    return 0;
}

注入的有效负载:“13B - Trampoline 导入的 Func DLL 有效负载”

#include "trampoline-imported-func-payload.h"
#include <stdio.h>
#include <stack>
#include <vector>
#include "capstone/x86.h"
#include "../hooking_common.h"
#include "../trampoline_common.h"
#include "capstone/capstone.h"

#define TARGET_APP_NAME "B - Target With Free Function From DLL.exe"
#define TARGET_DLL_NAME "B2 - GetNum-DLL.dll"
#define FUNC2HOOK_NAME "GetNum"

/**************************
 * HOOKING CODE           *
 **************************/
thread_local std::stack<uint64_t> hookJumpAddresses;
void PushAddress(uint64_t addr) //push the address of the jump target
{
    hookJumpAddresses.push(addr);
}

//we absolutely don't want this inlined
__declspec(noinline) void PopAddress(uint64_t trampolinePtr)
{
    uint64_t addr = hookJumpAddresses.top();
    hookJumpAddresses.pop();
    memcpy((void*)trampolinePtr, &addr, sizeof(uint64_t));
}

void InstallHook(void* func2hook, void* payloadFunc)
{
    SetOtherThreadsSuspended(true);

    DWORD oldProtect;
    VirtualProtect(func2hook, 1024, PAGE_EXECUTE_READWRITE, &oldProtect);

    //102 is the size of the "pre-payload" instructions that are written below
    //the trampoline will be located after these instructions in memory
    void* hookMemory = AllocatePageNearAddress(func2hook);

    uint32_t trampolineSize = BuildTrampoline(func2hook, (void*)((char*)hookMemory + 102));

    uint8_t* memoryIter = (uint8_t*)hookMemory;
    uint64_t trampolineAddress = (uint64_t)(memoryIter)+102;

    memoryIter += WriteSaveArgumentRegisters(memoryIter);
    memoryIter += WriteMovToRCX(memoryIter, trampolineAddress);
    memoryIter += WriteSubRSP32(memoryIter); //allocate home space for function call
    memoryIter += WriteAbsoluteCall64(memoryIter, &PushAddress);
    memoryIter += WriteAddRSP32(memoryIter);
    memoryIter += WriteRestoreArgumentRegisters(memoryIter);
    memoryIter += WriteAbsoluteJump64(memoryIter, payloadFunc);

    //create the relay function
    void* relayFuncMemory = memoryIter + trampolineSize;
    WriteAbsoluteJump64(relayFuncMemory, hookMemory); //write relay func instructions

    //install the hook
    uint8_t jmpInstruction[5] = { 0xE9, 0x0, 0x0, 0x0, 0x0 };
    const int32_t relAddr = int32_t((int64_t)relayFuncMemory - ((int64_t)func2hook + sizeof(jmpInstruction)));
    memcpy(jmpInstruction + 1, &relAddr, 4);
    memcpy(func2hook, jmpInstruction, sizeof(jmpInstruction));

    SetOtherThreadsSuspended(false);
}

/**************************
 * PAYLOAD CODE           *
 **************************/

int(*target)();// = nullptr;
int GetNumPayload()
{
    //this payload is used with the demo program "trampoline-imported-func-with-dll-injection
    //and is meant to be injected into the target app "target-with-functions-from-dll"
    //this payload hooks the "getNum" function found in the "getnum-dll" project
    printf("Trampoline Executed\n");

    PopAddress(uint64_t(&target));
    return target();
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD ul_reason_for_call, LPVOID lpvReserved)
{
    if (ul_reason_for_call == DLL_PROCESS_ATTACH)
    {
        printf("Function Payload Injected Successfully \n");
        HMODULE mod = FindModuleInProcess(GetCurrentProcess(), TARGET_DLL_NAME);

        void* localHookFunc = GetProcAddress(mod, FUNC2HOOK_NAME);
        InstallHook(localHookFunc, GetNumPayload);
    }
    return true;
}

输出:在每个目标方法调用之后,注入的方法被执行。

在此处输入图像描述

第二种情况:(不起作用)

我将目标更改为使用导入库的简单控制台应用程序。我在有效载荷和注入器中更改了 TARGET_APP_NAME、TARGET_DLL_NAME、FUNC2HOOK_NAME 并再次执行注入器。

目标:“TargetConsole.exe”

using System;
using System.Runtime.InteropServices;
using ImportedLib;

namespace TargetConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            ImportedLib.Class1 cl = new Class1();
            while (true)
            {
                Console.WriteLine(cl.MyMethod().ToString());
                System.Threading.Thread.Sleep(2000);
            }
        }
    }
}

目标导入的 dll:“ImportedLib.dll”

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ImportedLib
{
    public class Class1
    {
        public int MyMethod()
        {
            return 17;
        }
    }
}

输出:注入成功,但挂钩不成功。

在此处输入图像描述

标签: c#c++functionhookcode-injection

解决方案


推荐阅读