diff --git a/mapping/DKII_EXE_v170.sgmap b/mapping/DKII_EXE_v170.sgmap index 46581c3..b728c25 100644 --- a/mapping/DKII_EXE_v170.sgmap +++ b/mapping/DKII_EXE_v170.sgmap @@ -1973,7 +1973,7 @@ struct: id=vtbl_0066C4A4,name=CDefaultPlayerInterface,size=20273,vtable=instance field: name=f4_profiler type: kind=ptr type: kind=struct,id=constructor_00526020 - field: name=f8__cpyToF10 + field: name=f8_playerTagId type: kind=int,size=2 field: name=fA__counter type: kind=int,size=4,signed=True @@ -2255,11 +2255,11 @@ struct: id=vtbl_0066C4A4,name=CDefaultPlayerInterface,size=20273,vtable=instance field: name=f1C27_chatHistory type: kind=array,count=3 type: kind=struct,id=fill_00409E9D - field: name=f1F39__showChat + field: name=f1F39_chatUpdated type: kind=int,size=4,signed=True - field: name=f1F3D + field: name=f1F3D_sendPlayerMask type: kind=int,size=4,signed=True - field: name=field_1F41 + field: name=f1F41_sendTarget type: kind=int,size=4,signed=True field: name=f1F45__chatMessageHistoryWindowText type: kind=struct,id=construct_00402752 @@ -2514,7 +2514,7 @@ struct: id=instance_0066C4A4,name=CDefaultPlayerInterface_vtbl,size=92 type: kind=function,declspec=thiscall ret: kind=int,size=4,signed=True arg: kind=ptr - type: kind=int,size=1,signed=True,winapi=char + type: kind=void arg: kind=int,size=2,signed=True field: name=CDefaultPlayerInterface::fun_4033F0 type: kind=ptr @@ -4947,9 +4947,9 @@ struct: id=vtbl_0066EF3C,name=CFrontEndComponent,size=201210,vtable=instance_006 type: kind=int,size=4,signed=True field: name=field_5944 type: kind=int,size=4 - field: name=gap_5948 - type: kind=array,count=356 - type: kind=int,size=1 + field: name=f5948_chatDataToSend + type: kind=array,count=178 + type: kind=int,size=2,signed=True field: name=f5AAC_playersCount type: kind=int,size=1 field: name=gap5AAD @@ -5471,7 +5471,8 @@ struct: id=vtbl_0066ED1C,name=CGuiManager,size=484,vtable=instance_0066ED1C type: kind=ptr type: kind=struct,id=vtbl_0066EE94 field: name=fA0_unkObj - type: kind=int,size=4 + type: kind=ptr + type: kind=struct,id=vtbl_0066ECA4 field: name=field_A4 type: kind=int,size=4 field: name=field_A8 @@ -5497,7 +5498,7 @@ struct: id=vtbl_0066ED1C,name=CGuiManager,size=484,vtable=instance_0066ED1C type: kind=int,size=4 field: name=f1E0_pBtn type: kind=ptr - type: kind=struct,id=vtbl_0066ED8C + type: kind=struct,id=vtbl_0066ECA4 vtable_value: va=0052B8C0 struct: id=instance_0066ED1C,name=CGuiManager_vtbl,size=4 field: name=CGuiManager::_scalar_deleting_destructor_uint @@ -12794,7 +12795,7 @@ struct: id=constructor_00521F40,name=GameAction,size=18 type: kind=int,size=4 field: name=fC_actionKind type: kind=int,size=4,signed=True - field: name=f10__cpyFrF8 + field: name=f10__playerTagId type: kind=int,size=2,signed=True struct: id=constructor_00525EB0,name=GameActionArray,size=588 field: name=f0_arr @@ -13132,6 +13133,16 @@ struct: id=construct_0057D91E,name=MeshVertEx,size=20 type: kind=float,size=4 field: name=f10_uv type: kind=int,size=4,signed=True +struct: id=construct_00409E13,name=MessageData,size=262 + field: name=f0_flags_playerMask + type: kind=int,size=4 + field: name=f4_sendTarget + type: kind=int,size=2,signed=True + field: name=f6_text + type: kind=array,count=127 + type: kind=int,size=2,winapi=wchar_t,fname=wchar_t + field: name=f104_eos + type: kind=int,size=2,signed=True struct: id=vtbl_006728F8,name=MouseRgbDxAction,size=36,vtable=instance_006728F8,super=get_005DA009 field: name=gap_C type: kind=array,count=4 @@ -14030,13 +14041,15 @@ struct: id=construct_0062D2BB,path=dk2/text/render,name=MyCharRenderCtx,size=40 type: kind=ptr type: kind=struct,id=pos_xy struct: id=fill_00409E9D,name=MyChatMessage,size=262 - field: name=field_0 + field: name=f0_expireTime type: kind=int,size=4,signed=True - field: name=f4_msg + field: name=f4_sendTarget + type: kind=int,size=2,signed=True + field: name=f6_msg type: kind=array,count=127 type: kind=int,size=2,signed=True - field: name=field_102 - type: kind=int,size=4,signed=True + field: name=f104_eos + type: kind=int,size=2,signed=True struct: id=call_new_00555576,name=MyCmdHandler,size=40 field: name=f0_str type: kind=ptr @@ -22524,8 +22537,8 @@ global: va=00402D00,name=fun_402D00,size=854,member_of=vtbl_0066C4A4 ret: kind=void arg: kind=ptr type: kind=struct,id=vtbl_0066C4A4 - arg: kind=int,size=2,signed=True -global: va=00403060,name=sub_403060,size=742 + arg: kind=int,size=2 +global: va=00403060,name=FSMAP_load_403060,size=742 type: kind=function,declspec=stdcall ret: kind=int,size=4,signed=True global: va=00403350,name=TbGraphicFileLoader_destructor,size=7 @@ -22789,21 +22802,21 @@ global: va=00409CF0,name=fun_409CF0,size=163,member_of=vtbl_0066C4A4 ret: kind=int,size=4,signed=True arg: kind=ptr type: kind=struct,id=vtbl_0066C4A4 -global: va=00409DA0,name=sub_409DA0,size=138 +global: va=00409DA0,name=sendChatMessage,size=138,member_of=vtbl_0066C4A4 type: kind=function,declspec=thiscall ret: kind=int,size=4,signed=True arg: kind=ptr - type: kind=int,size=2 + type: kind=struct,id=vtbl_0066C4A4 arg: kind=int,size=4,signed=True arg: kind=int,size=2,signed=True arg: kind=int,size=4,signed=True global: va=00409E30,name=CDefaultPlayerInterface_chatCallback,size=144 type: kind=function,declspec=cdecl - ret: kind=int,size=4,signed=True + ret: kind=void arg: kind=ptr type: kind=void arg: kind=ptr - type: kind=int,size=4 + type: kind=struct,id=construct_00409E13 arg: kind=ptr type: kind=struct,id=vtbl_0066C4A4 global: va=00409EC0,name=fun_409EC0,size=101,member_of=vtbl_0066C4A4 @@ -22895,10 +22908,11 @@ global: va=0040ABC0,name=sub_40ABC0,size=115 type: kind=int,size=4,signed=True arg: kind=int,size=4,signed=True arg: kind=int,size=4,signed=True -global: va=0040AC40,name=sub_40AC40,size=140 +global: va=0040AC40,name=sub_40AC40,size=140,member_of=vtbl_0066C4A4 type: kind=function,declspec=thiscall ret: kind=int,size=4,signed=True - arg: kind=int,size=4,signed=True + arg: kind=ptr + type: kind=struct,id=vtbl_0066C4A4 global: va=0040ACD0,name=sub_40ACD0,size=423,member_of=vtbl_0066C4A4 type: kind=function,declspec=thiscall ret: kind=ptr @@ -23933,10 +23947,11 @@ global: va=0041E620,name=sub_41E620,size=488,member_of=vtbl_0066C4A4 ret: kind=int,size=4,signed=True arg: kind=ptr type: kind=struct,id=vtbl_0066C4A4 -global: va=0041E810,name=sub_41E810,size=1224 +global: va=0041E810,name=sub_41E810,size=1224,member_of=vtbl_0066C4A4 type: kind=function,declspec=thiscall ret: kind=void - arg: kind=int,size=4,signed=True + arg: kind=ptr + type: kind=struct,id=vtbl_0066C4A4 global: va=0041ECE0,name=sub_41ECE0,size=216 type: kind=function,declspec=thiscall ret: kind=int,size=4,signed=True @@ -24324,7 +24339,7 @@ global: va=00426000,name=sub_426000,size=102 arg: kind=int,size=4,signed=True arg: kind=ptr type: kind=struct,id=vtbl_0066C4A4 -global: va=00426070,name=sub_426070,size=123 +global: va=00426070,name=InputTextField_reset,size=123 type: kind=function,declspec=cdecl ret: kind=int,size=4,signed=True arg: kind=int,size=4,signed=True @@ -36644,7 +36659,7 @@ global: va=004BF390,name=fun_4BF390,size=54,member_of=vtbl_0066D99C arg: kind=ptr type: kind=struct,id=vtbl_0066D99C arg: kind=int,size=2 -global: va=004BF3D0,name=fun_4BF3D0,size=50,member_of=vtbl_0066D99C +global: va=004BF3D0,name=isAlly,size=50,member_of=vtbl_0066D99C type: kind=function,declspec=thiscall ret: kind=int,size=4,signed=True arg: kind=ptr @@ -46673,7 +46688,7 @@ global: va=0052C464,name=jpt_52C3FB,size=36 global: va=0052C488,name=idt_52C3F5,size=91 type: kind=array,count=91 type: kind=int,size=1 -global: va=0052C4F0,name=fun_52C4F0,size=44,member_of=vtbl_0066ED1C +global: va=0052C4F0,name=setInputMessage,size=44,member_of=vtbl_0066ED1C type: kind=function,declspec=thiscall ret: kind=int,size=4,winapi=size_t,fname=size_t arg: kind=ptr @@ -51082,8 +51097,7 @@ global: va=00559DA0,name=filterService,size=293,member_of=init_00559BE0 arg: kind=int,size=4,signed=True global: va=00559ED0,name=MLDPlay_HandleMessage_callback,size=138 type: kind=function,declspec=stdcall - ret: kind=ptr - type: kind=int,size=4 + ret: kind=void arg: kind=int,size=4,signed=True arg: kind=int,size=4,signed=True arg: kind=int,size=4,signed=True @@ -63992,12 +64006,15 @@ global: va=005DBC30,name=destructor,size=86,member_of=vtbl_006723D0 global: va=005DBC90,name=registerCb,size=49,member_of=vtbl_006723D0 type: kind=function,declspec=thiscall ret: kind=ptr - type: kind=int,size=4 + type: kind=ptr + type: kind=struct,id=pos_xy arg: kind=ptr type: kind=struct,id=vtbl_006723D0 arg: kind=ptr - type: kind=int,size=4 - arg: kind=int,size=4,signed=True + type: kind=ptr + type: kind=struct,id=pos_xy + arg: kind=ptr + type: kind=struct,id=pos_xy global: va=005DBCD0,name=call,size=90,member_of=vtbl_006723D0 type: kind=function,declspec=thiscall ret: kind=void @@ -80146,7 +80163,7 @@ global: va=006ED540,name=sceneObjectsPresent,size=4096 global: va=006EE540,name=sceneObjects,size=16384 type: kind=array,count=4096 type: kind=ptr - type: kind=struct,id=vtbl_0066E3DC + type: kind=struct,id=vtbl_0066D99C global: va=006F2540,name=isSceneObjectForceIdx,size=4 type: kind=int,size=4,signed=True global: va=006F2548,name=g_neutralPlayerId,size=2 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13a8547..4195f35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -147,3 +147,7 @@ add_custom_target(dkii_flame ALL DEPENDS ${ABS_MERGED_EXE}) install(FILES ${ABS_MERGED_EXE} DESTINATION ".") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/DKII-${OUTPUT_NAME}.map" DESTINATION ".") + +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/dev") + include(dev/dev.cmake) +endif () diff --git a/src/dk2/CDefaultPlayerInterface.cpp b/src/dk2/CDefaultPlayerInterface.cpp index 0878708..c7b92e9 100644 --- a/src/dk2/CDefaultPlayerInterface.cpp +++ b/src/dk2/CDefaultPlayerInterface.cpp @@ -2,6 +2,8 @@ // Created by DiaLight on 10.09.2024. // #include "dk2/CDefaultPlayerInterface.h" +#include "dk2/MessageData.h" +#include "dk2/entities/CPlayer.h" #include "dk2_functions.h" #include "dk2_globals.h" #include "patches/micro_patches.h" @@ -38,13 +40,13 @@ int dk2::CDefaultPlayerInterface::tickKeyboard2() { int v8_isLShift = v13_isLShift; if ( isActionKeyPressed(18, controlKeyFlags, ignoreModifiers) ) { // DIK_LEFT if (MyResources_instance.playerCfg.isAlternativeScroll) { - __int16 v7 = this->_cpyToF10; + __int16 v7 = this->playerTagId; GameAction v17_action; v17_action.f0 = -64; v17_action.f4 = 0.0; v17_action.f8 = 0; v17_action.actionKind = 8; - v17_action._cpyFrF8 = v7; + v17_action._playerTagId = v7; v18_try_catch = 0; this->pushAction(&v17_action); v18_try_catch = -1; @@ -54,13 +56,13 @@ int dk2::CDefaultPlayerInterface::tickKeyboard2() { } if ( isActionKeyPressed(19, controlKeyFlags, ignoreModifiers) ) { // DIK_RIGHT if ( MyResources_instance.playerCfg.isAlternativeScroll ) { - __int16 f8__cpyToF10 = this->_cpyToF10; + __int16 f8__cpyToF10 = this->playerTagId; GameAction v17_action; v17_action.f0 = 64; v17_action.f4 = 0.0; v17_action.f8 = 0; v17_action.actionKind = 8; - v17_action._cpyFrF8 = f8__cpyToF10; + v17_action._playerTagId = f8__cpyToF10; v18_try_catch = 1; this->pushAction(&v17_action); v18_try_catch = -1; @@ -82,7 +84,7 @@ int dk2::CDefaultPlayerInterface::tickKeyboard2() { v17_action.f0 = ((result * MyResources_instance.playerCfg.scrollSpeed) << 6) / 10; int v11 = (MyResources_instance.playerCfg.scrollSpeed * this->f1098) << 6; *(DWORD *) &v17_action.f4 = (v11 / 10); - v17_action._cpyFrF8 = this->_cpyToF10; + v17_action._playerTagId = this->playerTagId; v18_try_catch = 2; return this->pushAction(&v17_action); } @@ -118,4 +120,29 @@ void dk2::CDefaultPlayerInterface::createSurfacesForView_42CDF0(RtGuiView *view) } } +void __cdecl dk2::CDefaultPlayerInterface_chatCallback( + void *a1, + MessageData *a2_message, + CDefaultPlayerInterface *a3_defPlayerIf) { + int playerFlag = 1 << ((CPlayer *) sceneObjects[a3_defPlayerIf->playerTagId])->playerNumber; + uint32_t sendPlayerFlags = ((a2_message->flags_playerMask & 0x7FFF0000) >> 1) | a2_message->flags_playerMask & 0x7FFF; + if ((sendPlayerFlags & playerFlag) == 0 ) return; // this message is not for you + MyChatMessage *hist = a3_defPlayerIf->chatHistory; + for (int i = 0; i < 2; ++i) { + memcpy(&hist[i], &hist[i + 1], 0x102u); + } + + if(fix_chat_buffer_invalid_memory_access::enabled) { + size_t strLen = wcslen((wchar_t *) &a2_message->sendTarget); // whole message concept is being wchar_t[] compatible zero terminated string + size_t strSize = 2 * strLen + 2; // precise message buffer size + memset(&a3_defPlayerIf->chatHistory[2].sendTarget, 0, 0x102u); + if(strSize > 0x102u) strSize = 0x102u; + memcpy(&a3_defPlayerIf->chatHistory[2].sendTarget, &a2_message->sendTarget, strSize); // don't read behind allocated buffer + } else { + memcpy(&a3_defPlayerIf->chatHistory[2].sendTarget, &a2_message->sendTarget, 0x102u); + } + int expireTime = getTimeMs() + 30000; + a3_defPlayerIf->chatHistory[2].expireTime = expireTime; + a3_defPlayerIf->chatUpdated = 1; +} diff --git a/src/dk2/CGameComponent.cpp b/src/dk2/CGameComponent.cpp index 7ed8853..1f01bde 100644 --- a/src/dk2/CGameComponent.cpp +++ b/src/dk2/CGameComponent.cpp @@ -102,7 +102,7 @@ dk2::CGameComponent *dk2::CGameComponent::mainGuiLoop() { int v8 = this->mt_profiler.cworld->v_getMEPlayerTagId(); if ( !this->mt_profiler.attachPlayerI(&CDefaultPlayerInterface_instance, v8) ) return 0; - this->mt_profiler.player_i->_cpyToF10 = playerTagId; + this->mt_profiler.player_i->playerTagId = playerTagId; } if(CPCEngineInterface_instance_start.pCBridge) { CBridge *cBridge = CPCEngineInterface_instance_start.pCBridge; diff --git a/src/patches/micro_patches.cpp b/src/patches/micro_patches.cpp index 55f65f4..11624f3 100644 --- a/src/patches/micro_patches.cpp +++ b/src/patches/micro_patches.cpp @@ -27,6 +27,7 @@ bool creatures_setup_lair_fix::enabled = true; bool wooden_bridge_burn_fix::enabled = true; bool max_host_port_number_fix::enabled = true; bool increase_zoom_level::enabled = true; +bool fix_chat_buffer_invalid_memory_access::enabled = true; bool override_max_room_count::enabled = true; uint8_t override_max_room_count::limit = 255; // default is 96 @@ -43,7 +44,10 @@ void fix_keyboard_state_on_alt_tab::window_proc(HWND hWnd, UINT Msg, WPARAM wPar case WM_ACTIVATEAPP: if (wParam) { // activated // clear buttons state - memset(dk2::MyInputManagerCb_instance.pdxInputState->keyboardState, 0, 256); + dk2::MyDxInputState *inputState = dk2::MyInputManagerCb_instance.pdxInputState; + if(inputState != nullptr) { + memset(inputState->keyboardState, 0, 256); + } } break; } @@ -72,7 +76,7 @@ void fix_close_window::window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lP dk2::GameAction action; ZeroMemory(&action, sizeof(action)); action.actionKind = dk2::GA_ExitToWindows; - action._cpyFrF8 = playetIf->_cpyToF10; + action._playerTagId = playetIf->playerTagId; playetIf->pushAction(&action); } else { dk2::setAppExitStatus(true); diff --git a/src/patches/micro_patches.h b/src/patches/micro_patches.h index 668005a..c00ffad 100644 --- a/src/patches/micro_patches.h +++ b/src/patches/micro_patches.h @@ -64,6 +64,10 @@ namespace increase_zoom_level { extern bool enabled; } +namespace fix_chat_buffer_invalid_memory_access { + extern bool enabled; +} + namespace override_max_room_count { extern bool enabled; extern uint8_t limit; diff --git a/src/replace_globals.map b/src/replace_globals.map index e14f0d0..471cfdb 100644 --- a/src/replace_globals.map +++ b/src/replace_globals.map @@ -21,6 +21,7 @@ # CDefaultPlayerInterface.h 00406DF0 int tickKeyboard2(); // -------------------- /* auto */ +00409E30 int __cdecl CDefaultPlayerInterface_chatCallback(void *, MessageData *, CDefaultPlayerInterface *); /* auto */ # MyDxMouse.h 005BC760 int *__cdecl MyDxMouse_create(int *, MyDxMouse **); /* auto */ diff --git a/src/tools/bug_hunter.cpp b/src/tools/bug_hunter.cpp index 80ea939..1527fca 100644 --- a/src/tools/bug_hunter.cpp +++ b/src/tools/bug_hunter.cpp @@ -24,14 +24,74 @@ #include #include #include +#include "sha1.hpp" namespace fs = std::filesystem; #define fmtHex32(val) std::hex << std::setw(8) << std::setfill('0') << std::uppercase << (val) << std::dec #define fmtHex16(val) std::hex << std::setw(4) << std::setfill('0') << std::uppercase << (val) << std::dec +#define fmtHex8(val) std::hex << std::setw(2) << std::setfill('0') << std::uppercase << ((DWORD) val) << std::dec #define fmtHex32W(val) std::hex << std::setw(8) << std::setfill(L'0') << std::uppercase << (val) << std::dec #define fmtHex(val) std::hex << std::uppercase << (val) << std::dec +struct MyCodeViewInfo { + + enum class CodeViewMagic : unsigned int { + pdb70 = 'SDSR', // RSDS + pdb20 = '01BN', // NB10 + }; + + struct DebugInfoPdb20 { + CodeViewMagic magic; + unsigned int offset; + unsigned int signature; + unsigned int age; + char pdbName[1]; + }; + + struct DebugInfoPdb70 { + CodeViewMagic magic; + GUID guid; + unsigned int age; + char pdbName[1]; + }; + + union DebugInfo { + CodeViewMagic magic; + DebugInfoPdb20 pdb20; + DebugInfoPdb70 pdb70; + }; + + HMODULE base; + DebugInfo *codeView = nullptr; + explicit MyCodeViewInfo(HMODULE hModule) : base(hModule) {} + + bool find() { + auto *pHeader = (PIMAGE_DOS_HEADER) base; + if (pHeader->e_magic != IMAGE_DOS_SIGNATURE) return false; + auto *header = (PIMAGE_NT_HEADERS) ((BYTE *) base + ((PIMAGE_DOS_HEADER) base)->e_lfanew); + if (header->Signature != IMAGE_NT_SIGNATURE) return false; + if (header->OptionalHeader.NumberOfRvaAndSizes == 0) return false; + DWORD debugRva = header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; + if (debugRva == 0) return false; + DWORD debugSize = header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; + PIMAGE_DEBUG_DIRECTORY debug = (PIMAGE_DEBUG_DIRECTORY) ((BYTE *) base + debugRva); + PIMAGE_DEBUG_DIRECTORY debugEnd = (PIMAGE_DEBUG_DIRECTORY) ((BYTE *) debug + debugSize); + DWORD codeViewRva = 0; + DWORD codeViewSize = 0; + for(; debug < debugEnd; debug++) { + if (debug->Type != IMAGE_DEBUG_TYPE_CODEVIEW) continue; + codeViewRva = debug->AddressOfRawData; + codeViewSize = debug->SizeOfData; + break; + } + if(codeViewRva == 0) return false; + DebugInfo *codeView = (DebugInfo *) ((BYTE *) base + codeViewRva); + if(codeView->magic != CodeViewMagic::pdb70) return false; + this->codeView = codeView; + return true; + } +}; struct MyVersionInfo { struct LANGANDCODEPAGE { WORD wLanguage; @@ -136,6 +196,9 @@ namespace bughunter { uintptr_t flame_text_start = 0; uintptr_t flame_text_end = 0; + uintptr_t weanetr_base = 0; + uintptr_t qmixer_base = 0; + std::vector fpomap; bool isDkiiCode(DWORD p) noexcept { @@ -162,6 +225,13 @@ namespace bughunter { return it - 1; } } +bool ichar_equals(char a, char b) { + return std::tolower(static_cast(a)) == + std::tolower(static_cast(b)); +} +bool iequals(const std::string& a, const std::string& b) { + return std::equal(a.begin(), a.end(), b.begin(), b.end(), ichar_equals); +} void resolveLocs() { uintptr_t base = (uintptr_t) GetModuleHandleA(NULL); @@ -179,6 +249,13 @@ void resolveLocs() { bughunter::dkii_text_end = base + (uint32_t) (uint8_t *) &_dkii_text_end; bughunter::flame_text_start = base + (uint32_t) (uint8_t *) &_flame_text_start; bughunter::flame_text_end = base + (uint32_t) (uint8_t *) &_flame_text_end; + + LoadedModules modules; + modules.update(); + for(auto &mod : modules) { + if(iequals(mod->name, "weanetr.dll")) bughunter::weanetr_base = mod->base; + if(iequals(mod->name, "QMIXER.dll")) bughunter::qmixer_base = mod->base; + } } void parseFpomap() { @@ -662,6 +739,7 @@ void formatModules(std::stringstream &ss, LoadedModules &modules) { for(auto &mod : modules) { ss << fmtHex32(mod->base) << "-" << fmtHex32(mod->end) << " "; ss << std::left << std::setw(16) << std::setfill(' ') << mod->name; + bool hasId = false; MyVersionInfo ver((HMODULE) mod->base); if(ver.open()) { std::string version = ver.queryValue("FileVersion"); @@ -675,7 +753,49 @@ void formatModules(std::stringstream &ss, LoadedModules &modules) { auto desc = ver.queryValue("FileDescription"); if(!desc.empty()) ss << " desc=\"" << desc << "\""; if(!prodictName.empty()) ss << " product_name=\"" << prodictName << "\""; - if(!version.empty()) ss << " ver=\"" << version << "\""; + if(!version.empty()) { + ss << " ver=\"" << version << "\""; + hasId = true; // here we can identify dll + } + } + if(!hasId) { + // try find pdb guid or calc sha1 hashsum + MyCodeViewInfo cvi((HMODULE) mod->base); + if(cvi.find()) { + GUID &guid = cvi.codeView->pdb70.guid; + DWORD age = cvi.codeView->pdb70.age; + ss << " codeview=\""; + ss << fmtHex32(guid.Data1); + ss << fmtHex16(guid.Data2); + ss << fmtHex16(guid.Data3); + for (int i = 0; i < 8; ++i) ss << fmtHex8(guid.Data4[i]); + ss << fmtHex(age); + ss << "\""; + hasId = true; // here we can identify dll + } + } + if(!hasId) { + // try calc sha1 hashsum + wchar_t dllPath[MAX_PATH]; + DWORD len = GetModuleFileNameW((HMODULE) mod->base, dllPath, MAX_PATH); + std::string buffer; + { + + std::ifstream file(dllPath, std::ios::binary | std::ios::ate); + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + buffer.resize(size); + if (!file.read(buffer.data(), size)) buffer.clear(); + } + if(!buffer.empty()) { + SHA1 checksum; + checksum.update(buffer); + std::string hash = checksum.final(); + ss << " sha1=\""; + ss << hash; + ss << "\""; + hasId = true; + } } ss << std::endl; } @@ -695,6 +815,13 @@ void buildFileName(FILETIME ×tamp, const char *namePart, char *reportFile, } } +bool isGameFrame(StackFrame &frame) { + if(frame.libBase == bughunter::base) return true; + if(bughunter::weanetr_base && frame.libBase == bughunter::weanetr_base) return true; + if(bughunter::qmixer_base && frame.libBase == bughunter::qmixer_base) return true; + return false; +} + LPTOP_LEVEL_EXCEPTION_FILTER g_prev = nullptr; LONG WINAPI TopLevelExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo) { std::vector states = collectAppThreads(); @@ -737,7 +864,7 @@ LONG WINAPI TopLevelExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionIn MessageBoxA(NULL, "err", "err", MB_OK); } } - std::vector ntdllWorkers; + std::vector gameThreads; for(auto &ts : states) { WalkerError err; StackLimits limits; @@ -762,13 +889,14 @@ LONG WINAPI TopLevelExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionIn StackWalker sw(modules, limits, ctx, err); for(auto &frame : sw) frames.push_back(frame); - // collect ntdll workers. dont know how to detect them properly - if(frames.size() >= 2) { - if(frames[frames.size() - 2].symName == "ZwWaitForWorkViaWorkerFactory") { - ntdllWorkers.push_back(ts.tid); - } - if(frames.size() == 2) continue; // dont log waiting ntdll workers + bool hasGameFrame = false; + for(auto &frame : frames) { + if(!isGameFrame(frame)) continue; + hasGameFrame = true; + break; } + if(!hasGameFrame) continue; + gameThreads.push_back(ts.tid); ss << std::endl; ss << "thread " << ts.tid << " stack=" << fmtHex32(limits.low) << "-" << fmtHex32(limits.high) << std::endl; @@ -780,10 +908,10 @@ LONG WINAPI TopLevelExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionIn } } - // dont block ntdll workers. they are important for ShellExecuteA at least + // resume non game workers + // they are important for ShellExecuteA at least for(auto &ts : states) { - // if(!ntdllWorkers.contains(ts.tid)) continue; - if (std::find(ntdllWorkers.begin(), ntdllWorkers.end(), ts.tid) == ntdllWorkers.end()) continue; + if (std::find(gameThreads.begin(), gameThreads.end(), ts.tid) != gameThreads.end()) continue; // if(gameThreads.contains(ts.tid)) continue; ts.resume(); } diff --git a/src/tools/sha1.hpp b/src/tools/sha1.hpp new file mode 100644 index 0000000..67a6b52 --- /dev/null +++ b/src/tools/sha1.hpp @@ -0,0 +1,334 @@ +/* + sha1.hpp - source code of + + ============ + SHA-1 in C++ + ============ + + 100% Public Domain. + + Original C Code + -- Steve Reid + Small changes to fit into bglibs + -- Bruce Guenter + Translation to simpler C++ Code + -- Volker Diels-Grabsch + Safety fixes + -- Eugene Hopkinson + Header-only library + -- Zlatko Michailov +*/ + +#ifndef SHA1_HPP +#define SHA1_HPP + + +#include +#include +#include +#include +#include +#include + + +class SHA1 +{ +public: + SHA1(); + void update(const std::string &s); + void update(std::istream &is); + std::string final(); + static std::string from_file(const std::string &filename); + +private: + uint32_t digest[5]; + std::string buffer; + uint64_t transforms; +}; + + +static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ +static const size_t BLOCK_BYTES = BLOCK_INTS * 4; + + +inline static void reset(uint32_t digest[], std::string &buffer, uint64_t &transforms) +{ + /* SHA1 initialization constants */ + digest[0] = 0x67452301; + digest[1] = 0xefcdab89; + digest[2] = 0x98badcfe; + digest[3] = 0x10325476; + digest[4] = 0xc3d2e1f0; + + /* Reset counters */ + buffer = ""; + transforms = 0; +} + + +inline static uint32_t rol(const uint32_t value, const size_t bits) +{ + return (value << bits) | (value >> (32 - bits)); +} + + +inline static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i) +{ + return rol(block[(i+13)&15] ^ block[(i+8)&15] ^ block[(i+2)&15] ^ block[i], 1); +} + + +/* + * (R0+R1), R2, R3, R4 are the different operations used in SHA1 + */ + +inline static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + + +inline static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + + +inline static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5); + w = rol(w, 30); +} + + +inline static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5); + w = rol(w, 30); +} + + +inline static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5); + w = rol(w, 30); +} + + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ + +inline static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS], uint64_t &transforms) +{ + /* Copy digest[] to working vars */ + uint32_t a = digest[0]; + uint32_t b = digest[1]; + uint32_t c = digest[2]; + uint32_t d = digest[3]; + uint32_t e = digest[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(block, a, b, c, d, e, 0); + R0(block, e, a, b, c, d, 1); + R0(block, d, e, a, b, c, 2); + R0(block, c, d, e, a, b, 3); + R0(block, b, c, d, e, a, 4); + R0(block, a, b, c, d, e, 5); + R0(block, e, a, b, c, d, 6); + R0(block, d, e, a, b, c, 7); + R0(block, c, d, e, a, b, 8); + R0(block, b, c, d, e, a, 9); + R0(block, a, b, c, d, e, 10); + R0(block, e, a, b, c, d, 11); + R0(block, d, e, a, b, c, 12); + R0(block, c, d, e, a, b, 13); + R0(block, b, c, d, e, a, 14); + R0(block, a, b, c, d, e, 15); + R1(block, e, a, b, c, d, 0); + R1(block, d, e, a, b, c, 1); + R1(block, c, d, e, a, b, 2); + R1(block, b, c, d, e, a, 3); + R2(block, a, b, c, d, e, 4); + R2(block, e, a, b, c, d, 5); + R2(block, d, e, a, b, c, 6); + R2(block, c, d, e, a, b, 7); + R2(block, b, c, d, e, a, 8); + R2(block, a, b, c, d, e, 9); + R2(block, e, a, b, c, d, 10); + R2(block, d, e, a, b, c, 11); + R2(block, c, d, e, a, b, 12); + R2(block, b, c, d, e, a, 13); + R2(block, a, b, c, d, e, 14); + R2(block, e, a, b, c, d, 15); + R2(block, d, e, a, b, c, 0); + R2(block, c, d, e, a, b, 1); + R2(block, b, c, d, e, a, 2); + R2(block, a, b, c, d, e, 3); + R2(block, e, a, b, c, d, 4); + R2(block, d, e, a, b, c, 5); + R2(block, c, d, e, a, b, 6); + R2(block, b, c, d, e, a, 7); + R3(block, a, b, c, d, e, 8); + R3(block, e, a, b, c, d, 9); + R3(block, d, e, a, b, c, 10); + R3(block, c, d, e, a, b, 11); + R3(block, b, c, d, e, a, 12); + R3(block, a, b, c, d, e, 13); + R3(block, e, a, b, c, d, 14); + R3(block, d, e, a, b, c, 15); + R3(block, c, d, e, a, b, 0); + R3(block, b, c, d, e, a, 1); + R3(block, a, b, c, d, e, 2); + R3(block, e, a, b, c, d, 3); + R3(block, d, e, a, b, c, 4); + R3(block, c, d, e, a, b, 5); + R3(block, b, c, d, e, a, 6); + R3(block, a, b, c, d, e, 7); + R3(block, e, a, b, c, d, 8); + R3(block, d, e, a, b, c, 9); + R3(block, c, d, e, a, b, 10); + R3(block, b, c, d, e, a, 11); + R4(block, a, b, c, d, e, 12); + R4(block, e, a, b, c, d, 13); + R4(block, d, e, a, b, c, 14); + R4(block, c, d, e, a, b, 15); + R4(block, b, c, d, e, a, 0); + R4(block, a, b, c, d, e, 1); + R4(block, e, a, b, c, d, 2); + R4(block, d, e, a, b, c, 3); + R4(block, c, d, e, a, b, 4); + R4(block, b, c, d, e, a, 5); + R4(block, a, b, c, d, e, 6); + R4(block, e, a, b, c, d, 7); + R4(block, d, e, a, b, c, 8); + R4(block, c, d, e, a, b, 9); + R4(block, b, c, d, e, a, 10); + R4(block, a, b, c, d, e, 11); + R4(block, e, a, b, c, d, 12); + R4(block, d, e, a, b, c, 13); + R4(block, c, d, e, a, b, 14); + R4(block, b, c, d, e, a, 15); + + /* Add the working vars back into digest[] */ + digest[0] += a; + digest[1] += b; + digest[2] += c; + digest[3] += d; + digest[4] += e; + + /* Count the number of transformations */ + transforms++; +} + + +inline static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS]) +{ + /* Convert the std::string (byte buffer) to a uint32_t array (MSB) */ + for (size_t i = 0; i < BLOCK_INTS; i++) + { + block[i] = (buffer[4*i+3] & 0xff) + | (buffer[4*i+2] & 0xff)<<8 + | (buffer[4*i+1] & 0xff)<<16 + | (buffer[4*i+0] & 0xff)<<24; + } +} + + +inline SHA1::SHA1() +{ + reset(digest, buffer, transforms); +} + + +inline void SHA1::update(const std::string &s) +{ + std::istringstream is(s); + update(is); +} + + +inline void SHA1::update(std::istream &is) +{ + while (true) + { + char sbuf[BLOCK_BYTES]; + is.read(sbuf, BLOCK_BYTES - buffer.size()); + buffer.append(sbuf, (std::size_t)is.gcount()); + if (buffer.size() != BLOCK_BYTES) + { + return; + } + uint32_t block[BLOCK_INTS]; + buffer_to_block(buffer, block); + transform(digest, block, transforms); + buffer.clear(); + } +} + + +/* + * Add padding and return the message digest. + */ + +inline std::string SHA1::final() +{ + /* Total number of hashed bits */ + uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8; + + /* Padding */ + buffer += (char)0x80; + size_t orig_size = buffer.size(); + while (buffer.size() < BLOCK_BYTES) + { + buffer += (char)0x00; + } + + uint32_t block[BLOCK_INTS]; + buffer_to_block(buffer, block); + + if (orig_size > BLOCK_BYTES - 8) + { + transform(digest, block, transforms); + for (size_t i = 0; i < BLOCK_INTS - 2; i++) + { + block[i] = 0; + } + } + + /* Append total_bits, split this uint64_t into two uint32_t */ + block[BLOCK_INTS - 1] = (uint32_t)total_bits; + block[BLOCK_INTS - 2] = (uint32_t)(total_bits >> 32); + transform(digest, block, transforms); + + /* Hex std::string */ + std::ostringstream result; + for (size_t i = 0; i < sizeof(digest) / sizeof(digest[0]); i++) + { + result << std::hex << std::setfill('0') << std::setw(8); + result << digest[i]; + } + + /* Reset for next run */ + reset(digest, buffer, transforms); + + return result.str(); +} + + +inline std::string SHA1::from_file(const std::string &filename) +{ + std::ifstream stream(filename.c_str(), std::ios::binary); + SHA1 checksum; + checksum.update(stream); + return checksum.final(); +} + + +#endif /* SHA1_HPP */