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

关于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!