Function Interception
Posted 2007. 6. 19. 15:15, Filed under: Study/Computer ScienceFunction Interception
Notice the DLL project in the zip file. This function is in HookApi.h.
// Macro for adding pointers/DWORDs together without C arithmetic interfering // -- Taken from Matt Pietrek's book // Thought it'd be great to use.. #define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue))//This code is very similar to Matt Pietrek's, except that it is written //according to my understanding... //And Matt Pietrek's also handles Win32s //--(Because they it has some sort of a problem)PROC WINAPI HookImportedFunction(HMODULE hModule, //Module to intercept calls from PSTR FunctionModule, //The dll file that contains the function you want to //hook(ex: "USER32.dll") PSTR FunctionName, //The function that you want to hook //(ex: "MessageBoxA") PROC pfnNewProc) //New function, this gets called instead { PROC pfnOriginalProc; //The intercepted function's original location IMAGE_DOS_HEADER *pDosHeader; IMAGE_NT_HEADERS *pNTHeader; IMAGE_IMPORT_DESCRIPTOR *pImportDesc; IMAGE_THUNK_DATA *pThunk;// Verify that a valid pfn was passed if ( IsBadCodePtr(pfnNewProc) ) return 0; pfnOriginalProc = GetProcAddress(GetModuleHandle(FunctionModule), FunctionName); if(!pfnOriginalProc) return 0;pDosHeader = (PIMAGE_DOS_HEADER)hModule; //kindly read the ImgHelp function reference //in the Image Help Library section in MSDN //hModule is the Process's Base address (GetModuleHandle(0)) //even if called in the dll, it still gets the hModule of the calling process //---That's you should save the hInstance of the DLL as a global variable, //in DllMain(), because it's the only way to get it(I think)// Tests to make sure we're looking at a module image (the 'MZ' header) if ( IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) ) return 0; if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE ) //Image_DOS_SIGNATURE is a WORD (2bytes, 'M', 'Z' 's values) return 0;// The MZ header has a pointer to the PE header pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew); //it's like doing pDosHeader + pDosHeader->e_lfanew // e_lfanew contains a RVA to the 'PE\0\0' Header...An rva means, offset, // relative to the BaseAddress of module // -pDosHeader is the base address..and e_lfanew is the RVA, // so summing them, will give you the Virtual Address..// More tests to make sure we're looking at a "PE" image if ( IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) ) return 0; if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE ) //IMAGE_NT_SIGNATURE is a DWORD (4bytes, 'P', 'E', '\0', '\0' 's values) return 0;// We now have a valid pointer to the module's PE header. // Now get a pointer to its imports section pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDosHeader, //IMAGE_IMPORT_DESCRIPTOR *pImportDesc; pNTHeader->OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); //What i just did was get the imports section by getting the RVA of it //(like i did above), then adding the base addr to it. //// pNTHeader->OptionalHeader.DataDirectory /// [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress //// IMAGE_DIRECTORY_ENTRY_IMPORT==1 -- Look at that PE documentation. //// Pietrek's articles in MSJ and MSDN Magazine will be real helpful!//Go out if imports table doesn't exist if ( pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader ) return 0; //pImportDesc will ==pNTHeader. //if the RVA==0, cause pNTHeader+0==pNTHeader -> stored in pImportDesc //Therefore, pImportDesc==pNTHeader// Iterate through the array of imported module descriptors, looking // for the module whose name matches the FunctionModule parameter while ( pImportDesc->Name ) //Name is a DWORD (RVA, to a DLL name) { PSTR pszModName = MakePtr(PSTR, pDosHeader, pImportDesc->Name); if ( stricmp(pszModName, FunctionModule) == 0 ) //str"i"cmp,,, suggest you to ignore cases when comparing, break; //or strcmpi() in some compilerspImportDesc++; // Advance to next imported module descriptor }// Get out if we didn't find the Dll name. // pImportDesc->Name will be non-zero if we found it. if ( pImportDesc->Name == 0 ) return 0;// Get a pointer to the found module's import address table (IAT) // =====IMAGE_THUNK_DATA *pThunk; pThunk = MakePtr(PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk); //This is what i was talkin about earlier... //In pThunk, if it was image loaded in memory, you'll get the address to //entry point of functions //but in a disk file, It's a function name // Look through the table of import addresses, of the found // DLL, looking for the function's entry point that matches // the address we got back from GetProcAddress above.while ( pThunk->u1.Function ) { if ( (DWORD)pThunk->u1.Function == (DWORD)pfnOriginalProc ) { // We found it! Overwrite the original address with the // address of the interception function. Return the original // address to the caller so that they can chain on to it. pThunk->u1.Function = (PDWORD)pfnNewProc; // pfnNewProc is in the parameters of the function //pfnOriginalProc = (PROC)(DWORD)pdw1; return pfnOriginalProc; } pThunk++; // Advance to next imported function address }return 0; //function not found!!!!! }
PE 파일의 import address table을 직접 수정하는 방식으로 로드된 라이브러리가 호출하는 함수를 변경하고 있다.
자신이 작성한 코드를 수행한 후에 원래의 함수로 제어를 돌려주는 식으로 코드를 작성한 후에 원래 함수를 대체하는 방식으로 사용할 수 있겠다.