2013-07-09 70 views
2

我的最終目標是通過在kernel32.dll中掛鉤文件api來跟蹤explorer.exe完成的文件操作,但是我還沒有得到那個工作(explorer.exe是沒有調用這些API,或者我的結果是錯誤的)。爲了弄清楚發生了什麼,我設定了一個目標,以便隨時記錄notepad.exe創建文件的情況,但由於某種原因,這也失敗了!我有3個Visual Studio 2012 C++項目:我的DLL,一個DLL注入器,將強制任何可執行文件加載我的DLL(儘管如果Unicode /多字節和32/64位設置不匹配,它可能會失敗),以及一個調用API的測試程序。我有一個批處理文件將啓動我的測試程序,然後使用注入程序將我的DLL加載到測試程序中。怪異的部分是輸出表明API正在測試程序(產生控制檯和打印的一切!),但沒有發生notepad.exe(沒有控制檯=沒有操作被捕獲)的跟蹤。在notepad.exe中掛鉤CreateFile不會捕獲API調用

這是鉤住API的DLL(使用mhook庫)。格式和概念取自this指南。需要注意的重要事項是,我掛鉤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; 
} 

我無法找到確切位置,我發現噴油器程序,但它是幾乎相同this指南。有些評論甚至是相同的,所以我很確定一個從另一個拿走(不確定哪個是誰)。無論如何,我只是做了小的修改,以便在編譯時使用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的期待是什麼(但沒有得到)。爲了記錄,該測試也適用於Multibyte,但是會按照預期給出'A'。 enter image description here

我曾經想過,也許explorer.exe和notepad.exe不會將這些API用於他們的文件I/O,但是我的研究表明否則。 This使用Detours在notepad.exe中掛鉤CreateFile並報告該應用程序的成功。此外,ProgramMonitor清楚地表明NOTEPAD.EXE一個Saveas操作過程中調用CreateFile(使用不同的參數很多失敗的請求後...): enter image description here

沒關係約explorer.exe的現在; 當我做Saveas時,爲什麼我的鉤子不能用於notepad.exe?

編輯:我忘了提及,我也與測試程序鉤MoveFile(A/W)和的CopyFile(A/W),但我除去來自DLL的代碼對這個職位贅述。

+0

你說,如果32/64位不匹配,它會失敗......你確定你注入的DLL具有與'notepad.exe'相同的_bitness_嗎?請注意,在64位Windows中,大多數包含的應用程序都是64位的。 – rodrigo

+0

我嘗試了32/64bit和Unicode/Multibyte的所有4種組合(儘管現在我認爲後者並不重要,因爲我特別定義了每個字符串是什麼)。 – Suedocode

+0

@rodrigo我把它拿回來;事實證明,問題在於我的注射器。由於某種原因,它與我的測試應用程序一起工作,但不是其他的東西。我只是找到了,因爲我下載了第三方注射器來測試它,並且它工作。我很好奇爲什麼我的注射器不工作... – Suedocode

回答

5

正如在OP的評論中指出的那樣,notepad.exe似乎使用了ASLR而您的測試程序沒有。由於LoadLibraryA的地址在每個進程中會有所不同,並且您的注入代碼失敗。

這種情況是,您在注入器地址空間中獲得了LoadLibraryA的地址,並假定它與目標進程中的地址相同。這通常是正確的,但ASLR是專門設計的,以使這種假設失敗。所以它確實......你創建的線程獲得最可能的無效地址,並且失敗。

3

有幾個原因:

的鉤子代碼
  1. 位數必須匹配目標。
  2. CreateFileA/W非常低,但還不夠低,無法抓住所有!

要解決2.你必須鉤住Ntdll.dll中的Zw/NtCreateFile,這是你在procmon中看到的。這些API在用戶用地上沒有任何差別。

+0

從我的初步測試,CreateFileA/W似乎是explorer.exe使用的。我沒有證實我是否捕捉到了所有procmon顯示的內容(最後,我可能不是),但是它可能足以確定文件何時被複制(在這種情況下調用WriteFile)並移動(SetRenameInformationFile似乎是相當指示性的,雖然根據[this](http://forum.sysinternals.com/setrenameinformationfile_topic26811.html)發佈它實際上被稱爲NtSetInformationFile。我希望這是Ntdll.dll,正如你所說,但我要進入無證的領域 – Suedocode

+1

正確的NtSetInformationFile用於重命名/移動 – paulm