#include "precompiled.h" const size_t CSysModule::BASE_OFFSET = 0x01D00000; const module_handle_t CSysModule::INVALID_HANDLE = (module_handle_t)0; CSysModule::CSysModule() : m_handle(INVALID_HANDLE), m_base(nullptr), m_size(0), m_free(true) { } CSysModule::~CSysModule() { } bool CSysModule::Init(const char *szModuleName, const char *pszFile) { m_handle = GetModuleHandle(szModuleName); if (!m_handle) { return false; } MODULEINFO module_info; if (GetModuleInformation(GetCurrentProcess(), m_handle, &module_info, sizeof(module_info))) { m_base = (byteptr_t)module_info.lpBaseOfDll; m_size = module_info.SizeOfImage; } m_debug.SetFile(pszFile); return true; } const char *CSysModule::getFileName() { static char szFileName[MAX_PATH] = ""; if (m_handle == INVALID_HANDLE) { return ""; } GetModuleFileName(m_handle, szFileName, sizeof(szFileName)); return szFileName; } void CSysModule::Printf(const char *fmt, ...) { va_list argptr; static char string[4096]; va_start(argptr, fmt); Q_vsnprintf(string, sizeof(string), fmt, argptr); va_end(argptr); m_debug.Printf("%s", string); } void CSysModule::TraceLog(const char *fmt, ...) { va_list argptr; static char string[4096]; va_start(argptr, fmt); Q_vsnprintf(string, sizeof(string), fmt, argptr); va_end(argptr); m_debug.TraceLog("%s", string); } module_handle_t CSysModule::load(void *addr) { if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(addr), &m_handle)) { return INVALID_HANDLE; } MEMORY_BASIC_INFORMATION mem; if (!VirtualQuery(m_handle, &mem, sizeof(mem))) return INVALID_HANDLE; if (mem.State != MEM_COMMIT) return INVALID_HANDLE; if (!mem.AllocationBase) return INVALID_HANDLE; IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)mem.AllocationBase; IMAGE_NT_HEADERS *pe = (IMAGE_NT_HEADERS *)((uintptr_t)dos + (uintptr_t)dos->e_lfanew); if (pe->Signature != IMAGE_NT_SIGNATURE) return INVALID_HANDLE; m_free = false; m_base = (byteptr_t)mem.AllocationBase; m_size = (size_t)pe->OptionalHeader.SizeOfImage; return m_handle; } module_handle_t CSysModule::find(void *addr) { module_handle_t hHandle = INVALID_HANDLE; if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(addr), &hHandle)) { return INVALID_HANDLE; } return hHandle; } module_handle_t CSysModule::load(const char *filepath) { if (!m_handle) { m_handle = LoadLibrary(filepath); MODULEINFO module_info; if (GetModuleInformation(GetCurrentProcess(), m_handle, &module_info, sizeof(module_info))) { m_base = (byteptr_t)module_info.lpBaseOfDll; m_size = module_info.SizeOfImage; } } return m_handle; } bool CSysModule::unload() { if (m_handle == INVALID_HANDLE) { return false; } bool ret = true; if (m_free) { ret = FreeLibrary(m_handle) != ERROR; } m_handle = INVALID_HANDLE; m_base = 0; m_size = 0; return ret; } bool CSysModule::is_opened() const { return m_handle != INVALID_HANDLE; } byteptr_t CSysModule::find_pattern(byteptr_t pStart, size_t range, const char *pattern) const { return m_pattern.find(pStart, range, (byteptr_t)pattern, Q_strlen(pattern)); } byteptr_t CSysModule::find_pattern_back(byteptr_t pStart, size_t range, const char *pattern) const { return m_pattern.find_back(pStart, range, (byteptr_t)pattern, Q_strlen(pattern)); } byteptr_t CSysModule::find_string(const char *string, int8_t opcode) const { auto ptr = m_pattern.find(m_base, m_size, (byteptr_t)string, Q_strlen(string) + 1); if (!ptr) { return nullptr; } return m_pattern.find_ref(m_base, m_base + m_size - 5, (uint32_t)ptr, opcode); } byteptr_t CSysModule::getsym(const char *name) const { return m_handle ? (byteptr_t)GetProcAddress(m_handle, name) : nullptr; } module_handle_t CSysModule::gethandle() const { return m_handle; } byteptr_t CSysModule::getbase() const { return m_base; } size_t CSysModule::getsize() const { return m_size; } const char *CSysModule::getloaderror() { #ifdef _WIN32 static char buf[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, sizeof(buf) - 1, nullptr); return buf; #else return dlerror(); #endif } bool CSysModule::CPattern::compare(byteptr_t pStart, const byteptr_t pattern, size_t len) const { for (auto c = pattern, pattern_end = pattern + len; c < pattern_end; c++, pStart++) { if (*c == *pStart || *c == OP_ANY) { continue; } return false; } return true; } byteptr_t CSysModule::CPattern::find(byteptr_t pStart, size_t range, const byteptr_t pattern, size_t len) const { for (auto c = pStart + range - len; pStart < c; pStart++) { if (compare(pStart, pattern, len)) return pStart; } return nullptr; } byteptr_t CSysModule::CPattern::find_back(byteptr_t pStart, size_t range, const byteptr_t pattern, size_t len) const { for (auto c = pStart - range - len; pStart > c; pStart--) { if (compare(pStart, pattern, len)) return pStart; } return nullptr; } byteptr_t CSysModule::CPattern::find_ref(byteptr_t pStart, byteptr_t pEnd, uint32_t ref, int8_t opcode, bool relative) const { for (; pStart < pEnd; pStart++) { if (opcode != OP_ANY && *pStart != opcode) { continue; } if (relative) { if ((uint32_t)pStart + 5 + *(uint32_t *)(pStart + 1) == ref) return pStart; } else { if (*(uint32_t *)(pStart + 1) == ref) return pStart; } } return nullptr; } byteptr_t CSysModule::CPattern::find_ref_back(byteptr_t pStart, byteptr_t pBase, uint32_t ref, int8_t opcode, bool relative) const { for (; pStart > pBase; pStart--) { if (opcode != OP_ANY && *pStart != opcode) { continue; } if (relative) { if ((uint32_t)pStart + 5 + *(uint32_t *)(pStart + 1) == ref) return pStart; } else { if (*(uint32_t *)(pStart + 1) == ref) return pStart; } } return nullptr; } bool CSysModule::SetHook(byteptr_t addr, hook_t *hook) { if (!addr) { return false; } hook->SetFunc(addr); // copy original bytes Q_memcpy(hook->m_originalBytes, addr, sizeof(hook->m_originalBytes)); // make patch return patch(addr, &hook->m_jmpBytes, sizeof(hook->m_jmpBytes)); } bool CSysModule::set(hook_t *hook) { if (!hook->m_addr) return false; return patch(hook->m_addr, &hook->m_jmpBytes, hook->m_size); } bool CSysModule::unset(hook_t *hook) { if (!hook->m_addr) return false; return patch(hook->m_addr, hook->m_originalBytes, hook->m_size); } bool CSysModule::patch(void *dst, void *src, size_t size) { static HANDLE process = 0; DWORD OldProtection = 0; DWORD NewProtection = PAGE_EXECUTE_READWRITE; if (!process) process = GetCurrentProcess(); auto ret = false; if (VirtualProtect(dst, size, NewProtection, &OldProtection)) { Q_memcpy(dst, src, size); ret = !!VirtualProtect(dst, size, OldProtection, &NewProtection); } FlushInstructionCache(process, dst, size); return ret; } // Prints assembler code in the runtime like IDA Pro // but not all opcodes can correctly to handle void CSysModule::StartDumpBytes(byteptr_t pStart, size_t range) { m_debug.TraceLog(" -> START DUMP: (%p)\n", pStart); size_t nPos; for (nPos = 0; nPos < range; nPos++) { pStart += dumpBytes(pStart, true); } m_debug.TraceLog(" -> END DUMP: (%p), pos: (%d)\n", pStart, nPos); } size_t CSysModule::dumpBytes(byteptr_t pStart, bool collect_nopBytes) const { static bool prevNopBytes = false; byteptr_t pBytes = pStart; // print address if (!prevNopBytes || pBytes[0] != 0x90) { m_debug.TraceLog("%s0%X: ", prevNopBytes ? "\n" : "", (pStart - m_base + BASE_OFFSET)); prevNopBytes = false; } int iNumBytesOnLines = 1; switch (pBytes[0]) { case 0x68: // push iNumBytesOnLines = 5; break; case 0xE8: // call case 0xE9: // jmp case 0xEB: // jmp case 0xB8: // mov case 0xBF: // mov case 0xA2: // mov iNumBytesOnLines = 5; break; case 0x8D: // lea case 0x81: // cmp case 0xD8: // fcomp case 0xD9: // fld case 0x0F: // jnz iNumBytesOnLines = 6; break; case 0xC6: // mov iNumBytesOnLines = 7; break; case 0x75: // jnz case 0x72: // jb case 0x84: // test case 0xDF: // fnstsw case 0x7A: // jp iNumBytesOnLines = 2; break; case 0x8B: // mov { if (pBytes[1] == 0x5D) { iNumBytesOnLines = 3; break; } else if (pBytes[1] == 0x35) { iNumBytesOnLines = 6; break; } iNumBytesOnLines = 2; break; } case 0x33: // xor case 0x83: // add case 0xF6: // test iNumBytesOnLines = 3; break; case 0x90: if (!collect_nopBytes) { iNumBytesOnLines = 1; break; } m_debug.TraceLog(" 90"); prevNopBytes = true; return 1; default: iNumBytesOnLines = 1; break; } for (int i = 0; i < iNumBytesOnLines; i++) { //m_pattern.find(nullptr, 0, nullptr, 1); m_debug.Printf(""); m_debug.TraceLog(" %s%X", (pBytes[i] <= 0x0A) ? "0" : "", pBytes[i]); } if (!prevNopBytes) { m_debug.TraceLog("\n"); } return iNumBytesOnLines; } CSysModule::CMemDebug::CMemDebug() { m_DebugFile[0] = '\0'; } void CSysModule::CMemDebug::SetFile(const char *pszFileName) { Q_strlcpy(m_DebugFile, pszFileName); } void CSysModule::CMemDebug::TraceLog(const char *fmt, ...) const { va_list argptr; static char string[4096]; va_start(argptr, fmt); Q_vsnprintf(string, sizeof(string), fmt, argptr); va_end(argptr); if (m_DebugFile[0] == '\0') { Printf(string); return; } FILE *fp = fopen(m_DebugFile, "a+"); if (!fp) { return; } fprintf(fp, "%s", string); fclose(fp); } void CSysModule::CMemDebug::Printf(const char *fmt, ...) const { va_list argptr; static char string[4096]; va_start(argptr, fmt); Q_vsnprintf(string, sizeof(string), fmt, argptr); va_end(argptr); printf("%s", string); }