3

最終的な目標は、kernel32.dll のファイル API をフックすることで explorer.exe によって行われたファイル操作を追跡することですが、まだ機能していません (explorer.exe が API を呼び出していないか、私の側で何か問題が発生しています)。何が起こっているのかを把握するために、notepad.exe がファイルを作成するたびに追跡するという目標を設定しましたが、これも何らかの理由で失敗しました!

私は 3 つの Visual Studio 2012 C++ プロジェクトを持っています: 私の DLL、実行可能ファイルに私の dll を強制的にロードさせる DLL インジェクター (ただし、Unicode/マルチバイトと 32/64 ビットの設定が一致しない場合は失敗する可能性があります)、およびテスト プログラムです。 API を呼び出します。テスト プログラムを起動し、インジェクタ プログラムを使用して DLL をテスト プログラムにロードするバッチ ファイルがあります。奇妙な点は、API がテスト プログラムで追跡されていることを出力示していることです (コンソールが生成され、すべてが出力されます!)。

API をフックする DLL を次に示します (mhook ライブラリを使用します)。形式と概念は、このガイドから引用されています。注意すべき重要な点は、CreateFile(A/W) をフックし、コンソールを生成して、最初の操作が発生したときにファイル I/O ログを出力することです。

#include "stdafx.h"
#include "mhook/mhook-lib/mhook.h"

//////////////////////////////////////////////////////////////////////////
// Defines and typedefs
typedef HANDLE (WINAPI *CreateFileWFP)(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    );
typedef HANDLE (WINAPI *CreateFileAFP)(
    _In_ LPCSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    );

//////////////////////////////////////////////////////////////////////////
// Original function
CreateFileWFP OriginalCreateFileW = (CreateFileWFP)::GetProcAddress(::GetModuleHandle(TEXT("kernel32")), "CreateFileW");
CreateFileAFP OriginalCreateFileA = (CreateFileAFP)::GetProcAddress(::GetModuleHandle(TEXT("kernel32")), "CreateFileA");

//////////////////////////////////////////////////////////////////////////
// Some Helper Stuff

struct Console{
    HANDLE handle;

    Console(){ handle = INVALID_HANDLE_VALUE; }

    void write(LPCWSTR text){
        if(handle==INVALID_HANDLE_VALUE){
            AllocConsole();
            handle = GetStdHandle(STD_OUTPUT_HANDLE);
        }
        DWORD numCharsWritten = 0;
        WriteConsoleW(handle, text, (DWORD)wcslen(text), &numCharsWritten,NULL);
    }
    void write(LPCSTR text){
        if(handle==INVALID_HANDLE_VALUE){
            AllocConsole();
            handle = GetStdHandle(STD_OUTPUT_HANDLE);
        }
        DWORD numCharsWritten = 0;
        WriteConsoleA(handle, text, (DWORD)strlen(text), &numCharsWritten,NULL);
    }
} console;

void operationPrint(LPCWSTR left, LPCWSTR middle, LPCWSTR right){
    console.write(left);
    console.write(middle);
    console.write(right);
    console.write(L"\n");
}
void operationPrint(LPCSTR left, LPCSTR middle, LPCSTR right){
    console.write(left);
    console.write(middle);
    console.write(right);
    console.write(L"\n");
}

//////////////////////////////////////////////////////////////////////////
// Hooked function

HANDLE HookedCreateFileW(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    ){
        HANDLE out = OriginalCreateFileW(
                        lpFileName,
                        dwDesiredAccess,
                        dwShareMode,
                        lpSecurityAttributes,
                        dwCreationDisposition,
                        dwFlagsAndAttributes,
                        hTemplateFile);
        if(out == INVALID_HANDLE_VALUE) return out; //ignore failiures
        operationPrint(L"CreatedW file",L" at ",lpFileName);
        return out;
}
HANDLE HookedCreateFileA(
    _In_ LPCSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    ){
        HANDLE out = OriginalCreateFileA(
                        lpFileName,
                        dwDesiredAccess,
                        dwShareMode,
                        lpSecurityAttributes,
                        dwCreationDisposition,
                        dwFlagsAndAttributes,
                        hTemplateFile);
        if(out == INVALID_HANDLE_VALUE) return out; //ignore failiures
        operationPrint("CreatedA file"," at ",lpFileName);
        return out;
}

//////////////////////////////////////////////////////////////////////////
// Entry point

BOOL WINAPI DllMain(
    __in HINSTANCE  hInstance,
    __in DWORD      Reason,
    __in LPVOID     Reserved
    )
{        
    switch (Reason)
    {
    case DLL_PROCESS_ATTACH:
        Mhook_SetHook((PVOID*)&OriginalCreateFileW, HookedCreateFileW);
        Mhook_SetHook((PVOID*)&OriginalCreateFileA, HookedCreateFileA);
        break;

    case DLL_PROCESS_DETACH:
        FreeConsole();
        Mhook_Unhook((PVOID*)&OriginalCreateFileW);
        Mhook_Unhook((PVOID*)&OriginalCreateFileA);
        break;
    }

    return TRUE;
}

インジェクター プログラムを見つけた場所を正確に見つけることはできませんが、このガイドとほぼ同じです。コメントの中には同じものもあるので、一方が他方から取ったものだと確信しています(誰からのものかはわかりませんが)。いずれにせよ、Unicode または Multibyte でコンパイルしたときに動作するように、わずかな変更を加えただけです。スペースの無駄だと思うので、要求されない限り、ここにコード全体を投稿することはしませんが、重要な部分はここにあります。

#include "Injector.h"
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <conio.h>
#include <stdio.h> 
#include "DebugPrint.h"
#include <atlbase.h>

using namespace std;

Injector::Injector(void)
{
}


Injector::~Injector(void)
{
} 

#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ) 

bool Injector::Inject(string procName, string dllName){
   DWORD pID = GetTargetThreadIDFromProcName(procName.c_str()); 
   return Inject(pID,dllName);
}

bool Injector::Inject(DWORD pID, string dllName){
   const char* DLL_NAME = dllName.c_str();

   HANDLE Proc = 0; 
   HMODULE hLib = 0; 
   LPVOID RemoteString, LoadLibAddy; 

   if(!pID) 
      return false; 

   Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID); 
   if(!Proc) 
   { 
      DEBUG_PRINT("OpenProcess() failed: %d", GetLastError()); 
      return false; 
   } 

   LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA"); 

   // Allocate space in the process for our <strong class="highlight">DLL</strong> 
   RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 

   // Write the string name of our <strong class="highlight">DLL</strong> in the memory allocated 
   WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, strlen(DLL_NAME), NULL); 

   // Load our <strong class="highlight">DLL</strong> 
   CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL); 

   CloseHandle(Proc); 
   return true; 
}

DWORD Injector::GetTargetThreadIDFromProcName(const char* ProcName)
{
    PROCESSENTRY32 pe;
    HANDLE thSnapShot;
    BOOL retval, ProcFound = false;

    thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(thSnapShot == INVALID_HANDLE_VALUE)
    {
        //MessageBox(NULL, "Error: Unable to create toolhelp snapshot!", "2MLoader", MB_OK);
        DEBUG_PRINT("Error: Unable to create toolhelp snapshot!");
        return false;
    }

    pe.dwSize = sizeof(PROCESSENTRY32);

    retval = Process32First(thSnapShot, &pe);
    while(retval)
    {
#ifdef _UNICODE
        char peSzExeFile[MAX_PATH];
        wcstombs_s(NULL,peSzExeFile,MAX_PATH,pe.szExeFile,MAX_PATH);
#else
        const char* peSzExeFile = pe.szExeFile;
#endif
        DEBUG_PRINT("\nSearching for process: %s ",peSzExeFile);
        if(!strcmp(peSzExeFile, ProcName))
        {
            DEBUG_PRINT(" Found!\n\n");
            return pe.th32ProcessID;
        }
        retval = Process32Next(thSnapShot, &pe);
    }
    return 0;
}

最後に、私のテスト プログラムはいくつかのファイル API を呼び出して、挿入された DLL がそれらをキャッチできるかどうかを確認します。前に述べたように、それは呼び出しを非常にうまくキャッチします。

#include <windows.h>
#include <tchar.h>

using namespace std;

#ifdef _UNICODE
#define printf(X,...) wprintf(TEXT(X),__VA_ARGS__); //I don't want to have to keep replacing printf whenever I switch to Unicode or Multibyte
#endif

#ifdef _DEBUG
int _tmain(int argc, TCHAR* argv[]){ //makes a console.  printf() will have a place to go in this case
#else
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ //no console
#endif
    Sleep(2000); //let DLL finish loading

    LPCTSTR fileSrc = TEXT("C:\\Users\\jajoach\\Desktop\\hi.txt");
    LPCTSTR fileDst = TEXT("C:\\Users\\jajoach\\Desktop\\hi\\hi.txt");

    printf("Moving file from %s to %s\n",fileSrc,fileDst);
    MoveFile(fileSrc,fileDst);
    Sleep(1000);

    printf("Moving file from %s to %s\n",fileSrc,fileDst);
    MoveFile(fileDst,fileSrc);
    Sleep(1000);

    printf("Copying file from %s to %s\n",fileSrc,fileDst);
    CopyFile(fileSrc,fileDst,true);
    Sleep(1000);

    printf("Deleting file %s\n",fileDst);
    DeleteFile(fileDst);
    Sleep(1000);

    printf("Creating file %s\n",fileDst);
    HANDLE h=CreateFile(fileDst,0,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
    Sleep(1000);

    printf("Deleting file %s\n",fileDst);
    CloseHandle(h);
    DeleteFile(fileDst);

    Sleep(5000);

    return 0;
}

これは、Unicode(「W」で示されている)とリリースモードの確認済み出力、およびnotepad.exeとexplorer.exeの両方から期待していた(しかし得られなかった)ものです。記録のために、テストはマルチバイトでも機能しますが、代わりに期待どおりに「A」を返します。 ここに画像の説明を入力

私は、explorer.exe と notepad.exe がファイル I/O にこれらの API を使用しないのではないかと考えていましたが、私の調査ではそうではありません。 この投稿では、Detours を使用して notepad.exe の CreateFile をフックし、そのアプリケーションの成功を報告します。さらに、ProgramMonitor は、Saveas操作中に Notepad.exe が CreateFile を呼び出していることを明確に示しています (さまざまなパラメーターで多くの要求が失敗した後...)。 ここに画像の説明を入力

今のところ、explorer.exe は気にしないでください。Saveas を実行すると、フックが notepad.exe に対して機能しないのはなぜですか?

編集:テスト プログラムで MoveFile(A/W) と CopyFile(A/W) もフックすることを忘れていましたが、簡潔にするためにこの投稿の DLL からそのコードを削除しました。

4

2 に答える 2

5

OPのコメントで述べたように、それnotepad.exeASLRを使用し、テストプログラムは使用していないようです。そのため、LoadLibraryA のアドレスはプロセスごとに異なり、インジェクション コードは失敗します。

状況はLoadLibraryA、インジェクターのアドレス空間でのアドレスを取得しており、それがターゲット プロセスのアドレスと同じであると想定しているということです。それは通常は正しいことですが、ASLR はその仮定が失敗するように特別に設計されています。そして、そうです...作成したスレッドは、おそらく無効なアドレスを取得し、失敗します。

于 2013-07-09T21:30:41.213 に答える
3

いくつかの理由:

  1. フック コードのビット数は、ターゲットと一致する必要があります。
  2. CreateFileA/W は非常に低いですが、すべてをキャッチするほど低くはありません!

2. を解決するには、procmon に表示されている Ntdll.dll から Zw/NtCreateFile をフックする必要があります。ユーザーランドではこれらの API に勝るものはありません。

于 2013-07-09T21:38:06.153 に答える