PIMAGE_DOS_HEADER pDosHeader = PIMAGE_DOS_HEADER(hModule);
if (::IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) || IMAGE_DOS_SIGNATURE != pDosHeader->e_magic) {
return E_INVALIDARG;
}
PIMAGE_NT_HEADERS pNTHeaders = MakePtr(PIMAGE_NT_HEADERS, hModule, pDosHeader->e_lfanew);
if (::IsBadReadPtr(pNTHeaders, sizeof(IMAGE_NT_HEADERS)) || IMAGE_NT_SIGNATURE != pNTHeaders->Signature) {
return E_INVALIDARG;
}
HRESULT hr = E_UNEXPECTED;
// Locate the victim
IMAGE_DATA_DIRECTORY& impDir =
pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
PIMAGE_IMPORT_DESCRIPTOR pImpDesc =
MakePtr(PIMAGE_IMPORT_DESCRIPTOR, hModule, impDir.VirtualAddress),
pEnd = pImpDesc + impDir.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;
while (pImpDesc < pEnd) {
if (0 == ::lstrcmpiA(MakePtr(LPSTR, hModule, pImpDesc->Name), szVictim)) {
if (0 == pImpDesc->OriginalFirstThunk) {
// no import names table
return E_UNEXPECTED;
}
// Locate the entry
PIMAGE_THUNK_DATA pNamesTable =
MakePtr(PIMAGE_THUNK_DATA, hModule, pImpDesc->OriginalFirstThunk);
if (IS_INTRESOURCE(szEntry)) {
// By ordinal
while(pNamesTable->u1.AddressOfData) {
if (IMAGE_SNAP_BY_ORDINAL(pNamesTable->u1.Ordinal) && WORD(szEntry) == IMAGE_ORDINAL(pNamesTable->u1.Ordinal)) {
hr = S_OK;
break;
}
pNamesTable++;
}
} else {
// By name
while(pNamesTable->u1.AddressOfData) {
if (!IMAGE_SNAP_BY_ORDINAL(pNamesTable->u1.Ordinal)) {
PIMAGE_IMPORT_BY_NAME pName = MakePtr(PIMAGE_IMPORT_BY_NAME, hModule, pNamesTable->u1.AddressOfData);
if (0 == ::lstrcmpiA(LPSTR(pName->Name), szEntry)) {
hr = S_OK;
break;
}
}
pNamesTable++;
}
}
if (SUCCEEDED(hr)) {
// Get address
LPVOID *pProc = MakePtr(LPVOID *, pNamesTable, pImpDesc->FirstThunk - pImpDesc->OriginalFirstThunk);
// Save original handler
if (ppOrig) *ppOrig = *pProc;
// write to write-protected memory
return WriteProtectedMemory(pProc, &pHijacker, sizeof(LPVOID));
}
break;
}
pImpDesc++;
}
return hr;
}
HRESULT WriteProtectedMemory(LPVOID pDest, LPCVOID pSrc, DWORD dwSize) {
// Make it writable
DWORD dwOldProtect = 0;
if (::VirtualProtect(pDest, dwSize, PAGE_READWRITE, &dwOldProtect)) {
::MoveMemory(pDest, pSrc, dwSize);
// Restore protection
::VirtualProtect(pDest, dwSize, dwOldProtect, &dwOldProtect);
return S_OK;
}
return HRESULT_FROM_WIN32(GetLastError());
}
Впрочем, такой способ не будет работать если используется позднее связывание (delay load) или связывание во время исполнения (run-time load) с помощью ::GetProcAddress(). Это можно побороть если перехватить саму ::GetProcAddress(), и подменять возвращяемое значение при необходимости. А можно и подправить таблицу экспорта аналогичным способом:
HRESULT ApiHijackExports(HMODULE hModule, LPSTR szEntry, LPVOID pHijacker, LPVOID *ppOrig) {
// Check args
if ((!IS_INTRESOURCE(szEntry) && ::IsBadStringPtrA(szEntry, -1)) || ::IsBadCodePtr(FARPROC(pHijacker))) {
return E_INVALIDARG;
}
PIMAGE_DOS_HEADER pDosHeader = PIMAGE_DOS_HEADER(hModule);
if (::IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) || IMAGE_DOS_SIGNATURE != pDosHeader->e_magic) {
return E_INVALIDARG;
}
PIMAGE_NT_HEADERS pNTHeaders =
MakePtr(PIMAGE_NT_HEADERS, hModule, pDosHeader->e_lfanew);
if (::IsBadReadPtr(pNTHeaders, sizeof(IMAGE_NT_HEADERS)) || IMAGE_NT_SIGNATURE != pNTHeaders->Signature) {
return E_INVALIDARG;
}
HRESULT hr = E_UNEXPECTED;
IMAGE_DATA_DIRECTORY& expDir =
pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
PIMAGE_EXPORT_DIRECTORY pExpDir =
MakePtr(PIMAGE_EXPORT_DIRECTORY, hModule, expDir.VirtualAddress);
LPDWORD pdwAddrs = MakePtr(LPDWORD, hModule, pExpDir->AddressOfFunctions);
LPWORD pdwOrd = MakePtr(LPWORD, hModule, pExpDir->AddressOfNameOrdinals);
DWORD dwAddrIndex = -1;
if (IS_INTRESOURCE(szEntry)) {
// By ordinal
dwAddrIndex = WORD(szEntry) - pExpDir->Base;
hr = S_OK;
} else {
// By name
LPDWORD pdwNames = MakePtr(LPDWORD, hModule, pExpDir->AddressOfNames);
for (DWORD iName = 0; iName < pExpDir->NumberOfNames; iName++) {
if (0 == ::lstrcmpiA(MakePtr(LPSTR, hModule, pdwNames[iName]), szEntry)) {
dwAddrIndex = pdwOrd[iName];
hr = S_OK;
break;
}
}
}
if (SUCCEEDED(hr)) {
if (pdwAddrs[dwAddrIndex] >= expDir.VirtualAddress && pdwAddrs[dwAddrIndex] < expDir.VirtualAddress + expDir.Size) {
// We have a redirection
LPSTR azRedir = MakePtr(LPSTR, hModule, pdwAddrs[dwAddrIndex]);
ATLASSERT(!IsBadStringPtrA(azRedir, -1));
LPSTR azDot = strchr(azRedir, '.');
int nLen = azDot - azRedir;
LPSTR azModule = (LPSTR)alloca(nLen);
memcpy(azModule, azRedir, nLen);
azModule[nLen] = '\x0';
// Try to patch redirected function
return ApiHijackExports(
::GetModuleHandle(azModule), azDot + 1, pHijacker, ppOrig);
}
if (ppOrig)
*ppOrig = MakePtr(LPVOID, hModule, pdwAddrs[dwAddrIndex]);
DWORD dwOffset = DWORD_PTR(pHijacker) - DWORD_PTR(hModule);
// write to write-protected memory
hr = WriteProtectedMemory(pdwAddrs + dwAddrIndex, &dwOffset, sizeof(LPVOID));
}
return hr;
}
Имейте в виду, под Windows9x нельзя честно подменить экспорты для разделяемых библиотек, таких как user32.dll, kernel32.dll и gdi32.dll. Это связано с тем, что область памяти начиная с адреса 7FC00000h и выше совместно используестя всеми процессами в системе, и модификация сказалась бы на каждом из них. А это нежелательно, поскольку память, занимаемая нашей функцией-перехватчиком, наоборот, принадлежит только нашему процессу. Во всех остальных процессах в системе ::GetProcAddress(), после подмены таблицы экспорта, вернула бы неправильный указатель. Тем не менее, если нельзя, но очень хочется, то можно. Для этого нам придется вручную создать новый дескриптор в GDT (вот тут-то у Windows9x проблем не возникает) и используя этот дескриптор произвести необходимые изменения. Но будьте готовы к тому, что понадобится написать свою разделяемую библиотеку, установить ее в системе и проверять ID процесса при каждом обращении. Рабочий пример есть на internals.com.
Читать дальше