This commit is contained in:
1 parent
b9655a5ab1
commit
e292824161
122 files changed
+90002
-69
No files matched your search
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* snowcat-hook.dll —— 观测版
|
||||
*
|
||||
* 目的:搞清楚 QQ 9.9.37 到底是谁、在什么时机读取 resources\app\package.json,
|
||||
* 以及读取走的是 kernel32!CreateFileW 还是 ntdll!NtCreateFile(NT 层)。
|
||||
*
|
||||
* 只记录,不改内容。结果写到 D:\SnowCat.Portable\hook-trace.txt
|
||||
*/
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include "MinHook.h"
|
||||
|
||||
#define TRACE_PATH L"D:\\SnowCat.Portable\\hook-trace.txt"
|
||||
|
||||
static HANDLE (WINAPI *origCreateFileW)(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE) = NULL;
|
||||
static NTSTATUS (NTAPI *origNtOpenFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG) = NULL;
|
||||
static NTSTATUS (NTAPI *origNtCreateFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
|
||||
PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG) = NULL;
|
||||
|
||||
static CRITICAL_SECTION g_lock;
|
||||
static volatile LONG g_subsReady = 0;
|
||||
|
||||
static void trace(const wchar_t* api, const wchar_t* path) {
|
||||
EnterCriticalSection(&g_lock);
|
||||
HANDLE h = CreateFileW(TRACE_PATH, FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
char buf[2048];
|
||||
int n = WideCharToMultiByte(CP_UTF8, 0, path, -1, buf, sizeof(buf) - 1, NULL, NULL);
|
||||
if (n <= 0) { buf[0] = 0; }
|
||||
char line[2304];
|
||||
int len = snprintf(line, sizeof(line), "%s\t%s\n", (const char*)api, buf);
|
||||
DWORD w = 0;
|
||||
SetFilePointer(h, 0, NULL, FILE_END);
|
||||
WriteFile(h, line, (DWORD)len, &w, NULL);
|
||||
CloseHandle(h);
|
||||
}
|
||||
LeaveCriticalSection(&g_lock);
|
||||
}
|
||||
|
||||
static int interesting(const wchar_t* p) {
|
||||
if (!p) return 0;
|
||||
if (wcsstr(p, L"package.json")) return 1;
|
||||
if (wcsstr(p, L"application.asar")) return 1;
|
||||
if (wcsstr(p, L"loadNapCat")) return 1;
|
||||
if (wcsstr(p, L"app_launcher")) return 1;
|
||||
if (wcsstr(p, L"qqnt")) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static wchar_t g_fakePkg[MAX_PATH];
|
||||
static wchar_t g_patchedAsar[MAX_PATH];
|
||||
static wchar_t g_qqDir[MAX_PATH];
|
||||
|
||||
static void buildSubstitutes(void) {
|
||||
// QQ 目录 = QQ.exe 所在目录(反射注入器是父进程拉起的,模块表里主 exe 可查)
|
||||
wchar_t exePath[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, exePath, MAX_PATH);
|
||||
wchar_t* slash = wcsrchr(exePath, L'\\');
|
||||
if (slash) *slash = 0;
|
||||
wcscpy(g_qqDir, exePath);
|
||||
|
||||
// 假 package.json:优先 snowcat-fake-pkg.json(真字段+main 覆写合并版)
|
||||
wchar_t fakePath[MAX_PATH];
|
||||
swprintf(fakePath, MAX_PATH, L"%s\\snowcat-fake-pkg.json", g_qqDir);
|
||||
WIN32_FILE_ATTRIBUTE_DATA fa2;
|
||||
if (GetFileAttributesExW(fakePath, GetFileExInfoStandard, &fa2)) {
|
||||
wcscpy(g_fakePkg, fakePath);
|
||||
} else {
|
||||
DWORD n = GetEnvironmentVariableW(L"NAPCAT_PATCH_PACKAGE", g_fakePkg, MAX_PATH);
|
||||
if (n == 0) {
|
||||
swprintf(g_fakePkg, MAX_PATH, L"%s\\qqnt.json", g_qqDir);
|
||||
}
|
||||
}
|
||||
// patched asar:snowcat-patched.asar(若存在)
|
||||
swprintf(g_patchedAsar, MAX_PATH, L"%s\\snowcat-patched.asar", g_qqDir);
|
||||
WIN32_FILE_ATTRIBUTE_DATA fa;
|
||||
if (!GetFileAttributesExW(g_patchedAsar, GetFileExInfoStandard, &fa)) {
|
||||
g_patchedAsar[0] = 0; // 无副本则不替换 asar
|
||||
}
|
||||
}
|
||||
|
||||
static int isPkg(const wchar_t* p) {
|
||||
return p && wcsstr(p, L"resources") && wcsstr(p, L"package.json");
|
||||
}
|
||||
static int isAsar(const wchar_t* p) {
|
||||
return p && wcsstr(p, L"application.asar");
|
||||
}
|
||||
|
||||
static HANDLE WINAPI detourCreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
if (interesting(lpFileName)) trace(L"CreateFileW", lpFileName);
|
||||
if (g_subsReady) {
|
||||
if (isPkg(lpFileName)) return origCreateFileW(g_fakePkg, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||
if (isAsar(lpFileName) && g_patchedAsar[0]) return origCreateFileW(g_patchedAsar, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
return origCreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
|
||||
static NTSTATUS NTAPI detourNtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
|
||||
POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess,
|
||||
ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength) {
|
||||
if (g_subsReady && ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||
wchar_t* bp = ObjectAttributes->ObjectName->Buffer;
|
||||
/* NT 路径 \??\C:\... —— 匹配用宽松 wcsstr 即可 */
|
||||
if (isPkg(bp)) {
|
||||
static UNICODE_STRING us;
|
||||
static wchar_t ntpath[MAX_PATH + 8];
|
||||
swprintf(ntpath, MAX_PATH + 8, L"\\\\??\\\\%s", g_fakePkg);
|
||||
us.Buffer = ntpath;
|
||||
us.Length = (USHORT)(wcslen(ntpath) * 2);
|
||||
us.MaximumLength = us.Length + 2;
|
||||
ObjectAttributes->ObjectName = &us;
|
||||
trace(L"NtCreateFile→FAKE-PKG", bp);
|
||||
return origNtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
||||
}
|
||||
if (isAsar(bp) && g_patchedAsar[0]) {
|
||||
static UNICODE_STRING us2;
|
||||
static wchar_t ntpath2[MAX_PATH + 8];
|
||||
swprintf(ntpath2, MAX_PATH + 8, L"\\\\??\\\\%s", g_patchedAsar);
|
||||
us2.Buffer = ntpath2;
|
||||
us2.Length = (USHORT)(wcslen(ntpath2) * 2);
|
||||
us2.MaximumLength = us2.Length + 2;
|
||||
ObjectAttributes->ObjectName = &us2;
|
||||
trace(L"NtCreateFile→PATCHED-ASAR", bp);
|
||||
return origNtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
||||
}
|
||||
}
|
||||
if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||
if (interesting(ObjectAttributes->ObjectName->Buffer)) {
|
||||
trace(L"NtCreateFile", ObjectAttributes->ObjectName->Buffer);
|
||||
}
|
||||
}
|
||||
return origNtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize,
|
||||
FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
||||
}
|
||||
|
||||
static NTSTATUS NTAPI detourNtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
|
||||
POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
ULONG ShareAccess, ULONG OpenOptions) {
|
||||
if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||
trace(L"NtOpenFile", ObjectAttributes->ObjectName->Buffer);
|
||||
}
|
||||
return origNtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static DWORD WINAPI initThread(LPVOID param) {
|
||||
(void)param;
|
||||
InitializeCriticalSection(&g_lock);
|
||||
buildSubstitutes();
|
||||
g_subsReady = 1;
|
||||
trace(L"===", L"injected");
|
||||
|
||||
if (MH_Initialize() != MH_OK) { trace(L"===ERR", L"MH_Initialize failed"); return 1; }
|
||||
|
||||
HMODULE k32 = GetModuleHandleW(L"kernel32.dll");
|
||||
HMODULE nt = GetModuleHandleW(L"ntdll.dll");
|
||||
void* pCreateFileW = (void*)GetProcAddress(k32, "CreateFileW");
|
||||
void* pNtCreateFile = (void*)GetProcAddress(nt, "NtCreateFile");
|
||||
|
||||
if (pCreateFileW && MH_CreateHook(pCreateFileW, &detourCreateFileW, (LPVOID*)&origCreateFileW) == MH_OK)
|
||||
trace(L"===", L"hook CreateFileW ok");
|
||||
if (pNtCreateFile && MH_CreateHook(pNtCreateFile, &detourNtCreateFile, (LPVOID*)&origNtCreateFile) == MH_OK)
|
||||
trace(L"===", L"hook NtCreateFile ok");
|
||||
|
||||
void* pNtOpenFile = (void*)GetProcAddress(nt, "NtOpenFile");
|
||||
if (pNtOpenFile && MH_CreateHook(pNtOpenFile, &detourNtOpenFile, (LPVOID*)&origNtOpenFile) == MH_OK)
|
||||
trace(L"===", L"hook NtOpenFile ok");
|
||||
|
||||
MH_EnableHook(MH_ALL_HOOKS);
|
||||
trace(L"===", L"hooks enabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) {
|
||||
(void)reserved;
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
HANDLE th = CreateThread(NULL, 0, initThread, NULL, 0, NULL);
|
||||
if (th) CloseHandle(th);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
Reference in new issue
Block a user