Dll Proxying
通过让 exe 优先加载 proxy dll 来注入进程,或者拦截某个原 dll 中的函数。
DLL 的搜索路径: Dynamic-link library search order
注意 KnownDLLs 中的 dll 必须放在 System32 中才会生效。
程序 (a.exe, main.c) :
// cc main.c -lwininet -O2
#include <Windows.h>
#include <Wininet.h>
#include <stdio.h>
const char some_string[] = "Hello, World!";
int main() {
DWORD flags;
if (InternetGetConnectedState(&flags, 0)) {
printf("Connected to the internet\n");
} else {
printf("Not connected to the internet\n");
}
puts(some_string);
return 0;
}
DLL (wininet.dll, dll.c) :
// gcc -shared -static -static-libgcc -O2 -o wininet.dll dll.c m.def
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Psapi.h>
#include <wininet.h>
#include <processthreadsapi.h>
#include <memoryapi.h>
#include <stdlib.h>
#include <stdio.h>
const char orig_str[] = "Hello, World!";
const char new_str[] = "Goodbye World";
__declspec(dllexport)
BOOL __stdcall _InternetGetConnectedState(
_Out_ LPDWORD lpdwFlags,
_Reserved_ DWORD dwReserved) {
return FALSE;
}
void *memmem(const void *s, size_t len, const void *const ss, const size_t ssLen) {
if (s == NULL || ss == NULL || len == 0 || ssLen == 0)
return NULL;
for (const char *h = (const char *)s; len >= ssLen; ++h, --len) {
if (!memcmp(h, ss, ssLen))
return (void *)h;
}
return NULL;
}
void PatchMemory() {
HANDLE process = GetCurrentProcess();
HMODULE module = GetModuleHandleW(NULL);
MODULEINFO info;
if (!GetModuleInformation(process, module, &info, sizeof(info))) {
OutputDebugStringW(L"GetModuleInformation failed\n");
return;
}
BYTE* procImage = (BYTE *)malloc(sizeof(BYTE) * info.SizeOfImage);
SIZE_T bytesRead = 0;
ReadProcessMemory(process, info.EntryPoint, procImage, info.SizeOfImage, &bytesRead);
// expect error 299 here, ReadProcessMemory returns 0, just check bytesRead
if (bytesRead == 0) {
OutputDebugStringW(L"ReadProcessMemory failed\n");
free(procImage);
return;
}
void *pos = memmem(procImage, bytesRead, orig_str, ARRAYSIZE(orig_str));
if (pos == NULL) {
OutputDebugStringW(L"Patch not found\n");
free(procImage);
return;
}
void *realAddr = (void *)((uintptr_t)info.EntryPoint + ((uintptr_t)pos - (uintptr_t)procImage));
DWORD oldProtect;
VirtualProtectEx(process, realAddr, ARRAYSIZE(new_str), PAGE_EXECUTE_READWRITE, &oldProtect);
WriteProcessMemory(process, realAddr, new_str, ARRAYSIZE(new_str), NULL);
VirtualProtectEx(process, realAddr, ARRAYSIZE(new_str), oldProtect, NULL);
free(procImage);
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
PatchMemory();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
生成 module def :
import pefile
dll_name = 'wininet'
dll = pefile.PE(dll_name)
f = open('module.def', 'w')
f.write('EXPORTS\n')
i = 0
for sym in dll.DIRECTORY_ENTRY_EXPORT.symbols:
if sym.name is not None:
f.write(f' {sym.name.decode()}={dll_name}.{sym.name.decode()} @{sym.ordinal}\n')
i += 1
f.close()
DLL (wininet.dll, m.def) :
EXPORTS
AppCacheCheckManifest="C:\Windows\System32\WININET.AppCacheCheckManifest" @113
AppCacheCloseHandle="C:\Windows\System32\WININET.AppCacheCloseHandle" @114
AppCacheCreateAndCommitFile="C:\Windows\System32\WININET.AppCacheCreateAndCommitFile" @115
(...)
InternetGetConnectedState=_InternetGetConnectedState @294
关于Module-Definition File: MSVC, ld win32
有些 dll 中的函数只会通过 ordinal 导出,而没有名称。在 MSVC 中可以通过 some_func=other_module.#42
来代理,而 mingw 的 ld 不支持这种写法。
另外,代理系统 DLL 需要把路径写全,否则会报 0xc000007b (合理)。
运行:
PS C:\Users\Azuk\inet-test> .\a.exe
Connected to the internet
Hello, World!
(Build the proxy dll)
PS C:\Users\Azuk\inet-test> .\a.exe
Not connected to the internet
Hello, World!