5

C++ を使用して、リモート プロセスを作成し、それに DLL を挿入するアプリケーションを作成しました。DLL からエクスポートされた関数を、それを作成したアプリケーションからリモート アプリケーションに実行させる方法はありますか? その関数にパラメーターを送信することは可能ですか? 私は DllMain 内で何もしないようにしていることに注意してください。

4

2 に答える 2

7

注:
より良い答えについては、以下に投稿された私の更新を参照してください!


さて、これを達成する方法は次のとおりです。

BOOL RemoteLibraryFunction( HANDLE hProcess, LPCSTR lpModuleName, LPCSTR lpProcName, LPVOID lpParameters, SIZE_T dwParamSize, PVOID *ppReturn )
{
    LPVOID lpRemoteParams = NULL;

    LPVOID lpFunctionAddress = GetProcAddress(GetModuleHandleA(lpModuleName), lpProcName);
    if( !lpFunctionAddress ) lpFunctionAddress = GetProcAddress(LoadLibraryA(lpModuleName), lpProcName);
    if( !lpFunctionAddress ) goto ErrorHandler;

    if( lpParameters )
    {
        lpRemoteParams = VirtualAllocEx( hProcess, NULL, dwParamSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
        if( !lpRemoteParams ) goto ErrorHandler;

        SIZE_T dwBytesWritten = 0;
        BOOL result = WriteProcessMemory( hProcess, lpRemoteParams, lpParameters, dwParamSize, &dwBytesWritten);
        if( !result || dwBytesWritten < 1 ) goto ErrorHandler;
    }

    HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpFunctionAddress, lpRemoteParams, NULL, NULL );
    if( !hThread ) goto ErrorHandler;

    DWORD dwOut = 0;
    while(GetExitCodeThread(hThread, &dwOut)) {
        if(dwOut != STILL_ACTIVE) {
            *ppReturn = (PVOID)dwOut;
            break;
        }
    }

    return TRUE;

ErrorHandler:
    if( lpRemoteParams ) VirtualFreeEx( hProcess, lpRemoteParams, dwParamSize, MEM_RELEASE );
    return FALSE;
}

//...
CStringA targetDll = "injected.dll"

    // Inject the target library into the remote process
PVOID lpReturn = NULL;
RemoteLibraryFunction( hProcess, "kernel32.dll", "LoadLibraryA", targetDll.GetBuffer(MAX_PATH), targetDll.GetLength(), &lpReturn );
HMODULE hInjected = reinterpret_cast<HMODULE>( lpReturn );

    // Call our exported function
lpReturn = NULL;
RemoteLibraryFunction( hProcess, targetDll, "Initialize", NULL, 0, &lpReturn );
BOOL RemoteInitialize = reinterpret_cast<BOOL>( lpReturn );


これは、構造体または共用体へのポインターを介してリモート関数にパラメーターを送信するためにも使用でき、DllMain に何かを書き込む必要がなくなります。

于 2012-11-19T03:19:05.947 に答える
2

したがって、いくつかの入念なテストの後、私の以前の回答は絶対確実ではなく
(さらに言えば、100% 機能することさえ)、クラッシュしやすいようです。少し考えた後、これに対してまったく異なるアプローチを取ることにしました... Interprocess Communicationを使用します。

注意してください...この方法は、のコードを利用していますDllMain
したがって、行き過ぎないでください。これを行うときは、デッドロックに陥らないように、安全な慣行に従ってください...

特に、Win32 API は次の便利な機能を提供します。

これらを使用すると、注入された dll 自体から直接、リモート init 関数が存在する正確な場所を Launcher プロセスに伝えることができます...


dllmain.cpp:

// Data struct to be shared between processes
struct TSharedData
{
    DWORD dwOffset = 0;
    HMODULE hModule = nullptr;
    LPDWORD lpInit = nullptr;
};
// Name of the exported function you wish to call from the Launcher process
#define DLL_REMOTEINIT_FUNCNAME "RemoteInit"
// Size (in bytes) of data to be shared
#define SHMEMSIZE sizeof(TSharedData)
// Name of the shared file map (NOTE: Global namespaces must have the SeCreateGlobalPrivilege privilege)
#define SHMEMNAME "Global\\InjectedDllName_SHMEM"
static HANDLE hMapFile;
static LPVOID lpMemFile;

BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
    TSharedData data;

    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hModule);

            // Get a handle to our file map
            hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, SHMEMSIZE, SHMEMNAME);
            if (hMapFile == nullptr) {
                MessageBoxA(nullptr, "Failed to create file mapping!", "DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
                return FALSE;
            }

            // Get our shared memory pointer
            lpMemFile = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
            if (lpMemFile == nullptr) {
                MessageBoxA(nullptr, "Failed to map shared memory!", "DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
                return FALSE;
            }

            // Set shared memory to hold what our remote process needs
            memset(lpMemFile, 0, SHMEMSIZE);
            data.hModule = hModule;
            data.lpInit = LPDWORD(GetProcAddress(hModule, DLL_REMOTEINIT_FUNCNAME));
            data.dwOffset = DWORD(data.lpInit) - DWORD(data.hModule);
            memcpy(lpMemFile, &data, sizeof(TSharedData));
        case DLL_THREAD_ATTACH:
            break;
        case DLL_THREAD_DETACH:
            break;
        case DLL_PROCESS_DETACH:
            // Tie up any loose ends
            UnmapViewOfFile(lpMemFile);
            CloseHandle(hMapFile);
            break;
    }
    return TRUE;
    UNREFERENCED_PARAMETER(lpReserved);
}


次に、Launcher アプリケーションから通常のCreateProcess+ VirtualAllocEx+CreateRemoteThreadトリックを実行して Dll をインジェクトし、適切なへのポインタをSECURITY_DESCRIPTORの 3 番目のパラメータCreateProcessとして渡し、CREATE_SUSPENDED6 番目のパラメータにフラグを渡します。

これは、子プロセスがグローバル共有メモリ名前空間を読み書きするための適切な権限を持つようにするためですが、これを実現する方法は他にもあります (または、グローバル パスをまったく使用せずにテストすることもできます)。

このフラグは、他のライブラリがロードされる前にCREATE_SUSPENDEDdllmain エントリ ポイント関数が共有メモリへの書き込みを完了していることを保証します。これにより、後で簡単にローカル フックを行うことができます...


Injector.cpp:

SECURITY_ATTRIBUTES SecAttr, *pSec = nullptr;
SECURITY_DESCRIPTOR SecDesc;

if (InitializeSecurityDescriptor(&SecDesc, SECURITY_DESCRIPTOR_REVISION) &&
    SetSecurityDescriptorDacl(&SecDesc, TRUE, PACL(nullptr), FALSE))
{
    SecAttr.nLength = sizeof(SecAttr);
    SecAttr.lpSecurityDescriptor = &SecDesc;
    SecAttr.bInheritHandle = TRUE;
    pSec = &SecAttr;
}

CreateProcessA(szTargetExe, nullptr, pSec, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);


DLL をターゲット プロセスに挿入した後、DLL プロジェクトから Launcher プロジェクトに同じ (多かれ少なかれ) ファイル マッピング コードを使用するだけです (もちろん、共有メモリの内容を設定する部分は除きます)。 .

次に、リモート関数を呼び出すのは、次の簡単なことです。

// Copy from shared memory
TSharedData data;
memcpy(&data, lpMemFile, SHMEMSIZE);
// Clean up
UnmapViewOfFile(lpMemFile);
CloseHandle(hMapFile);
// Call the remote function
DWORD dwThreadId = 0;
auto hThread = CreateRemoteThread(hProcess, nullptr, 0, LPTHREAD_START_ROUTINE(data.lpInit), nullptr, 0, &dwThreadId);

次にResumeThread、ターゲット プロセスのメイン スレッドで、またはリモート関数から実行できます。


追加のボーナスとして... この形式の通信を使用すると、ターゲット プロセスと直接通信できるようになるため、Launcher プロセスにいくつかの扉を開くこともできます。
ただし、繰り返しますが、あまり多くのことをしないように注意してください。可能な場合は、リモートの init 関数を使用して (たとえば、名前付きミューテックスDllMainを使用しても安全です)、別の共有メモリ マップを作成して続行します。そこからの連絡。

これが誰かを助けることを願っています! =)

于 2015-04-27T13:26:44.777 に答える