#include #include #include #include "skse/skse_version.h" #include "skse/SafeWrite.h" #include "skse/Utilities.h" #include "skse/Hooks_Memory.h" #include "common/IFileStream.h" #include "loader_common/IdentifyEXE.h" #include // hack for VS2005, intrin.h can't be included with winnt.h #if _MSC_VER <= 1400 #define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVC2005_ERROR #define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVC2005_ERROR #endif #include IDebugLog gLog; HANDLE g_dllHandle; static void OnAttach(void); std::string GetAppPath(void) { char appPath[4096]; ASSERT(GetModuleFileName(GetModuleHandle(NULL), appPath, sizeof(appPath))); return appPath; } std::string GetAppDir(void) { std::string appPath = GetAppPath(); std::string::size_type slashOffset = appPath.rfind('\\'); if(slashOffset == std::string::npos) return appPath; return appPath.substr(0, slashOffset); } bool RunningEditor(void) { // ### this is not a good method of detection char pathBuffer[MAX_PATH] = {0}; strcpy_s(pathBuffer, sizeof(pathBuffer), GetAppPath().c_str()); _strlwr_s(pathBuffer, sizeof(pathBuffer)); return strstr(pathBuffer, "creationkit.exe") != NULL; } BOOL WINAPI DllMain(HANDLE procHandle, DWORD reason, LPVOID reserved) { if(reason == DLL_PROCESS_ATTACH) { g_dllHandle = procHandle; OnAttach(); } return TRUE; } typedef int (__stdcall * _HookedWinMain)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); _HookedWinMain g_hookedWinMain = NULL; std::string g_dllPath; int __stdcall OnHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { _MESSAGE("OnHook: thread = %d", GetCurrentThreadId()); // load main dll if(!LoadLibrary(g_dllPath.c_str())) { _ERROR("couldn't load dll (%08X)", GetLastError()); } // call original winmain _MESSAGE("calling winmain"); int result = g_hookedWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); _MESSAGE("returned from winmain (%d)", result); return result; } void DumpThreads(void) { HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId()); if(snapshot != INVALID_HANDLE_VALUE) { THREADENTRY32 info; info.dwSize = sizeof(info); if(Thread32First(snapshot, &info)) { do { if(info.th32OwnerProcessID == GetCurrentProcessId()) { UInt32 eip = 0xFFFFFFFF; HANDLE thread = OpenThread(THREAD_GET_CONTEXT, FALSE, info.th32ThreadID); if(thread) { CONTEXT ctx; ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(thread, &ctx); eip = ctx.Eip; CloseHandle(thread); } _MESSAGE("thread %d pri %d eip %08X%s", info.th32ThreadID, info.tpBasePri, eip, (info.th32ThreadID == GetCurrentThreadId()) ? " current thread" : ""); } info.dwSize = sizeof(info); } while(Thread32Next(snapshot, &info)); } CloseHandle(snapshot); } } bool hookInstalled = false; void InstallHook(void * retaddr, UInt32 hookSrc) { if(hookInstalled) return; else hookInstalled = true; _MESSAGE("InstallHook: thread = %d retaddr = %08X hookSrc = %d", GetCurrentThreadId(), retaddr, hookSrc); // DumpThreads(); std::string appPath = GetAppPath(); _MESSAGE("appPath = %s", appPath.c_str()); std::string dllSuffix; ProcHookInfo procHookInfo; bool isEditor = RunningEditor(); if(!IdentifyEXE(appPath.c_str(), isEditor, &dllSuffix, &procHookInfo)) { _ERROR("unknown exe"); return; } // build full path to our dll const char * dllPrefix = (isEditor == false) ? "\\skse_" : "\\skse_editor_"; g_dllPath = GetAppDir() + dllPrefix + dllSuffix + ".dll"; _MESSAGE("dll = %s", g_dllPath.c_str()); // hook winmain call UInt32 hookBaseAddr = procHookInfo.hookCallAddr; UInt32 hookBaseCallAddr = *((UInt32 *)(hookBaseAddr + 1)); hookBaseCallAddr += 5 + hookBaseAddr; // adjust for relcall _MESSAGE("old winmain = %08X", hookBaseCallAddr); g_hookedWinMain = (_HookedWinMain)hookBaseCallAddr; UInt32 newHookDst = ((UInt32)OnHook) - hookBaseAddr - 5; SafeWrite32(hookBaseAddr + 1, newHookDst); Hooks_Memory_PreloadCommit(); FlushInstructionCache(GetCurrentProcess(), NULL, 0); } typedef void (__stdcall * _GetSystemTimeAsFileTime)(LPFILETIME * fileTime); _GetSystemTimeAsFileTime GetSystemTimeAsFileTime_Original = NULL; _GetSystemTimeAsFileTime * _GetSystemTimeAsFileTime_IAT = NULL; typedef void (__stdcall * _GetStartupInfoA)(STARTUPINFOA * startupInfo); _GetStartupInfoA GetStartupInfoA_Original = NULL; _GetStartupInfoA * _GetStartupInfoA_IAT = NULL; void __stdcall GetSystemTimeAsFileTime_Hook(LPFILETIME * fileTime) { InstallHook(_ReturnAddress(), 0); GetSystemTimeAsFileTime_Original(fileTime); } void __stdcall GetStartupInfoA_Hook(STARTUPINFOA * startupInfo) { InstallHook(_ReturnAddress(), 1); GetStartupInfoA_Original(startupInfo); } static void OnAttach(void) { gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Skyrim\\SKSE\\skse_steam_loader.log"); gLog.SetPrintLevel(IDebugLog::kLevel_Error); gLog.SetLogLevel(IDebugLog::kLevel_DebugMessage); FILETIME now; GetSystemTimeAsFileTime(&now); _MESSAGE("skse loader %08X (steam) %08X%08X %s", PACKED_SKSE_VERSION, now.dwHighDateTime, now.dwLowDateTime, GetOSInfoStr().c_str()); _MESSAGE("base addr = %08X", g_dllHandle); UInt32 oldProtect; _GetSystemTimeAsFileTime_IAT = (_GetSystemTimeAsFileTime *)GetIATAddr((UInt8 *)GetModuleHandle(NULL), "kernel32.dll", "GetSystemTimeAsFileTime"); if(_GetSystemTimeAsFileTime_IAT) { _MESSAGE("GetSystemTimeAsFileTime IAT = %08X", _GetSystemTimeAsFileTime_IAT); VirtualProtect((void *)_GetSystemTimeAsFileTime_IAT, 4, PAGE_EXECUTE_READWRITE, &oldProtect); _MESSAGE("original GetSystemTimeAsFileTime = %08X", *_GetSystemTimeAsFileTime_IAT); GetSystemTimeAsFileTime_Original = *_GetSystemTimeAsFileTime_IAT; *_GetSystemTimeAsFileTime_IAT = GetSystemTimeAsFileTime_Hook; _MESSAGE("patched GetSystemTimeAsFileTime = %08X", *_GetSystemTimeAsFileTime_IAT); UInt32 junk; VirtualProtect((void *)_GetSystemTimeAsFileTime_IAT, 4, oldProtect, &junk); } else { _ERROR("couldn't read IAT"); } // win8 automatically initializes the stack cookie, so the previous hook doesn't get hit _GetStartupInfoA_IAT = (_GetStartupInfoA *)GetIATAddr((UInt8 *)GetModuleHandle(NULL), "kernel32.dll", "GetStartupInfoA"); if(_GetStartupInfoA_IAT) { _MESSAGE("GetStartupInfoA IAT = %08X", _GetStartupInfoA_IAT); VirtualProtect((void *)_GetStartupInfoA_IAT, 4, PAGE_EXECUTE_READWRITE, &oldProtect); _MESSAGE("original GetStartupInfoA = %08X", *_GetStartupInfoA_IAT); GetStartupInfoA_Original = *_GetStartupInfoA_IAT; *_GetStartupInfoA_IAT = GetStartupInfoA_Hook; _MESSAGE("patched GetStartupInfoA = %08X", *_GetStartupInfoA_IAT); UInt32 junk; VirtualProtect((void *)_GetStartupInfoA_IAT, 4, oldProtect, &junk); } }