diff --git a/InjectLib/InjectDll.cpp b/InjectLib/InjectDll.cpp index cfd2ed7..bca83f9 100644 --- a/InjectLib/InjectDll.cpp +++ b/InjectLib/InjectDll.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2018 Victor Sheinmann, Vicshann@gmail.com + Copyright (c) 2020 Victor Sheinmann, Vicshann@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, @@ -14,6 +14,13 @@ #include "InjectDll.h" +//#pragma comment(linker,"/MERGE:.rdata=.text") // .idata can be only there +//#pragma comment(linker,"/MERGE:.data=.text") +//#pragma comment(linker,"/MERGE:.idata=.text") +//#pragma comment(linker, "/SECTION:.text,EWR") // .text; .bss; .pdata; .idata; .reloc // GhostDbg can cut off after BSS (It does imports and relocs and PDATA is unused) +#pragma comment(linker,"/MERGE:.data=.rdata") // Now: .text; .bss; .rdata; .pdata; .reloc // rdata will not be writable - fixed manually for a separate DLL release +//#pragma comment(linker,"/MERGE:.text=.rdata") // Creates .xdata section +//#pragma comment(linker, "/SECTION:.rdata,WR") // Any attampt to make it writable will create .xdata section #pragma comment(linker,"/ENTRY:DLLMain") #pragma comment(linker,"/NODEFAULTLIB") @@ -23,6 +30,7 @@ UINT IPCSize = 0x100000; // 1Mb (Enough when Req-Rsp removed) //bool HideDllProxy = true; //bool HideDllProxyDsk = true; +bool SuspMainThAtLd = false; // Suspend main thread when loaded by GInjer bool RstDskHiddenProxy = true; bool AllowEjectOnDetach = false; //--------------------------------------- @@ -34,83 +42,119 @@ PHOOK(ProcNtUnmapViewOfSection) HookNtUnmapViewOfSection; PHOOK(ProcNtMapViewOfSection) HookNtMapViewOfSection; PHOOK(ProcNtGetContextThread) HookNtGetContextThread; PHOOK(ProcNtSetContextThread) HookNtSetContextThread; -PHOOK(ProcNtTerminateThread) HookNtTerminateThread; -//PHOOK(ProcNtTerminateProcess) HookNtTerminateProcess; +PHOOK(ProcNtTerminateThread) HookNtTerminateThread; +PHOOK(ProcNtTerminateProcess) HookNtTerminateProcess; PHOOK(ProcNtContinue) HookNtContinue; +SHookLdrpInitialize LdrpInitHook; SHookRtlDispatchException ExpDispHook; -bool ModInjected = false; +DWORD ModInjFlags = 0; // If 0 then the module is loaded normally(With loader) // -1 if the module is hidden or a proxy //BYTE ProxyEncKey = 0; //PBYTE ProxyDllCopy = NULL; //DWORD ProxyDllSize = 0; GhDbg::CDbgClient* Dbg = NULL; -HANDLE hIpcTh = NULL; -DWORD LastExcThID; // Helps to reduce overhead of NtContinue hook +HANDLE hIpcTh = NULL; +DWORD MainThId = 0; +DWORD LastThID = -1; +DWORD LastExcThID = 0;; // Helps to reduce overhead of NtContinue hook //LPSTR LibPathName = NULL; // TODO: Add an option to not use a detectable hooks ("No detectable hooks") PBYTE ThisLibBase = NULL; -SIZE_T ThisLibSize = 0; +//SIZE_T ThisLibSize = 0; PBYTE MainExeBase = NULL; -SIZE_T MainExeSize = 0; +//SIZE_T MainExeSize = 0; + +alignas(16) BYTE ArrDbgClient[sizeof(GhDbg::CDbgClient)]; wchar_t SysDirPath[MAX_PATH]; wchar_t StartUpDir[MAX_PATH]; wchar_t CfgFilePath[MAX_PATH]; wchar_t WorkFolder[MAX_PATH]; //=========================================================================== -BOOL APIENTRY DLLMain(HMODULE hModule, DWORD ReasonCall, LPVOID lpReserved) +/* + NOTE: Only a SystemService(sysenter/int2E) type of functions is allowed to be used + + Can be loaded by: XDbgPlugin, GInjer, A target process somehow + + Injector can load a DLL with LdrLoadDll or as '(HMODULE)Mod->ModuleBase, DLL_REFLECTIVE_LOAD, Mod' + Ginjer injects from a main thread +*/ +// Reflective load: +// hModule = Module Base +// ReasonCall = 15 +// lpReserved = SInjModDesc* (Can be used to find SBlkDescr*) +//=========================================================================== +BOOL APIENTRY DLLMain(HMODULE hModule, DWORD ReasonCall, LPVOID lpReserved) // ReasonCall and lpReserved is invalid for mfRunUAPC,mfRunRMTH,mfRunThHij { - wchar_t DllDirPath[MAX_PATH]; - bool RemTh = (DWORD)hModule & 0x0FFF; // Normal HMODULE would be aligned at 0x1000 - if(RemTh || (ReasonCall > 3)) - { - // - // TODO: Prevent multi-entering from different threads when APC injection method used - // - hModule = (HMODULE)InjLdr::PEImageInitialize(hModule); - ReasonCall = DLL_PROCESS_ATTACH; - ModInjected = true; - } + SModDesc* ModDesc = ModDescFromCurTh(); // NULL if loaded not by GInjer // NOTE: GInjer uses main thread + SBlkDesc* BlkDesc = AddrToBlkDesc(ModDesc); + SLdrDesc* LdrDesc = GetCurLdrDesc(BlkDesc); + UINT RemThFlags = (DWORD)hModule & InjLdr::RemThModMarker; // DLLMain has been passed to CreateRemoteThread/APC/ExistingThread (Where is only one argument available) // Normal HMODULE would be aligned at 0x1000 +#ifdef _DEBUG + if(ModDesc){LdrLogInit(ModDesc); LDRLOG("Hello from %08X: %ls", ModDesc->Flags, &ModDesc->ModulePath);} +#endif + if(RemThFlags || (ReasonCall >= DLL_REFLECTIVE_LOAD)) // NOTE: Variables get read in conditions even if these conditions is skipped // Either an own thread or APC callback of an existing thread // ReasonCall may be already outside of stack(Remote Thread)! + { + DWORD InjFlg = (RemThFlags?RemThFlags:(ReasonCall & InjLdr::RemThModMarker)) << 24; // ReasonCall = (Flags >> 24)|0x100 // mfRunUAPC,mfRunRMTH,mfRunThHij,mfRawRMTH + bool NotOwnThread = (InjFlg & (InjLdr::mfRunUAPC|InjLdr::mfRunThHij)); + bool NotReusableTh = ModDesc || NotOwnThread; + if(InjFlg & InjLdr::mfRunUAPC) // Cannot reuse these threads + { + // + // TODO: Prevent multi-entering from different threads when APC or Hijack injection method used + // + } + if(ModDesc)hModule = (HMODULE)InjLdr::ReflectiveRelocateSelf(hModule, (LdrDesc)?((PVOID)LdrDesc->NtDllBase):(NULL)); // Allocate to a new buffer // Loaded by GInjer + else hModule = (HMODULE)InjLdr::PEImageInitialize(hModule); // Relocate in current buffer (Must be large enough) // Loaded by a Debugger plugin + if(!NotReusableTh)hIpcTh = NtCurrentThread; // Can be changed later, before Start, if needed // A reusable injected remote thread // Assign globals after relocation + if(ModDesc && !NotOwnThread)MainThId = NtCurrentThreadId(); // Main thread by GInjer + ReasonCall = DLL_PROCESS_ATTACH; + ModInjFlags = InjFlg; // Injected with + } + else MainThId = NtCurrentThreadId(); // Injected from a Main Thread or loaded normally switch(ReasonCall) { case DLL_PROCESS_ATTACH: - { + { + wchar_t DllDirPath[MAX_PATH]; ThisLibBase = (PBYTE)hModule; - MainExeBase = (PBYTE)GetModuleHandleA(NULL); + MainExeBase = (PBYTE)NNTDLL::GetModuleBaseLdr(NULL); // GetModuleHandleA(NULL); // LibPathName = (LPSTR)&SysDirPath; - ThisLibSize = GetRealModuleSize(ThisLibBase); - MainExeSize = GetRealModuleSize(MainExeBase); - - GetModuleFileNameW((HMODULE)hModule,DllDirPath,countof(DllDirPath)); - GetSystemDirectoryW(SysDirPath,countof(SysDirPath)); +// ThisLibSize = GetRealModuleSize(ThisLibBase); +// MainExeSize = GetRealModuleSize(MainExeBase); + + NNTDLL::GetModuleNameLdr(hModule,DllDirPath,countof(DllDirPath)); // GetModuleFileNameW((HMODULE)hModule,DllDirPath,countof(DllDirPath)); +// GetSystemDirectoryW(SysDirPath,countof(SysDirPath)); // Get it from PEB? NSTR::StrCnat(SysDirPath, L"\\"); NSTR::StrCnat(SysDirPath,GetFileName(DllDirPath)); - GetModuleFileNameW((HMODULE)MainExeBase,StartUpDir,countof(StartUpDir)); + NNTDLL::GetModuleNameLdr(MainExeBase,StartUpDir,countof(StartUpDir)); // GetModuleFileNameW((HMODULE)MainExeBase,StartUpDir,countof(StartUpDir)); NSTR::StrCopy(WorkFolder, StartUpDir); TrimFilePath(WorkFolder); // NSTR::StrCnat((LPSTR)&WorkFolder,".LOGS\\"); - #ifndef NOLOG NSTR::StrCopy(LogFilePath, WorkFolder); NSTR::StrCnat(LogFilePath, GetFileName(StartUpDir)); - NSTR::StrCnat(LogFilePath, LOGFILE); + NSTR::StrCnat(LogFilePath, ctENCSA(LOGFILE)); #endif NSTR::StrCopy(CfgFilePath, WorkFolder); NSTR::StrCnat(CfgFilePath, GetFileName(StartUpDir)); - NSTR::StrCnat(CfgFilePath, CFGFILE); + NSTR::StrCnat(CfgFilePath, ctENCSA(CFGFILE)); - CreateDirectoryPath(WorkFolder); +// CreateDirectoryPath(WorkFolder); LoadConfiguration(); - if(LogMode & lmCons){AllocConsole();/*SetWinConsoleSizes(1000, 500, 1000, 500);*/} - LOGMSG("Time=%08X, ExeBase=%p, Owner='%ls'", (DWORD)GetTime64(),MainExeBase,&StartUpDir); -// LOGMSG("HookMod=%p, RealMod=%p", hModule, hRealMod); + DBGMSG("Starting up... (Time=%016X), Owner='%ls'", SysTimeToTime64(NNTDLL::GetSystemTime()), &StartUpDir); + DBGMSG("RemThFlags=%08X, hIpcTh=%p, ModDesc=%p, hModule=%p, lpReserved=%p, ModInjFlags=%08X, MainThId=%u", RemThFlags, hIpcTh, ModDesc, hModule, lpReserved, ModInjFlags, MainThId); TrimFilePath(StartUpDir); - LOGMSG("WorkFolder: %ls", &WorkFolder); - LOGMSG("StartUpDir: %ls", &StartUpDir); - LOGMSG("SysDirPath: %ls", &SysDirPath); + DBGMSG("WorkFolder: %ls", &WorkFolder); + DBGMSG("StartUpDir: %ls", &StartUpDir); + DBGMSG("SysDirPath: %ls", &SysDirPath); + +/* { + NTSTATUS stat = GhDbg::CDbgClient::CreateIpcThread(&hIpcTh, NULL, TRUE); // <<<<<<<<< TEST !!!!!!!!!!!! + } */ /* BOOL dres = true; if(!ModInjected && HideDllProxy) // NOTE: NO MORE DLL PROXY NEEDED { @@ -119,12 +163,12 @@ BOOL APIENTRY DLLMain(HMODULE hModule, DWORD ReasonCall, LPVOID lpReserved) hIpcTh = CreateThread(NULL,0,&GhDbg::CDbgClient::IPCQueueThread,NULL,CREATE_SUSPENDED,NULL); // Some anticheats prevent creation of threads outside of any module if(InjLdr::HideSelfProxyDll(hModule, GetModuleHandleA(ctENCSA("ntdll.dll")), (LPSTR)&SysDirPath, &NewBase, &EntryPT) > 0) // Are imports from our proxy DLL is already resolved by loader at this point? { - LOGMSG("Calling EP of a real DLL: Base=%p, EP=%p",hModule,EntryPT); + DBGMSG("Calling EP of a real DLL: Base=%p, EP=%p",hModule,EntryPT); dres = ((decltype(DLLMain)*)EntryPT)(hModule, ReasonCall, lpReserved); // Pass DLL_PROCESS_ATTACH notification hModule = (HMODULE)NewBase; if(HideDllProxyDsk && DllDirPath[0]) { - LOGMSG("Hiding from disk..."); + DBGMSG("Hiding from disk..."); if(RstDskHiddenProxy) { ProxyEncKey = (GetTickCount() >> 3) | 0x80; @@ -146,25 +190,24 @@ BOOL APIENTRY DLLMain(HMODULE hModule, DWORD ReasonCall, LPVOID lpReserved) } } DeleteFileA((LPSTR)&DllDirPath); // It is no longer mapped - LOGMSG("Done hiding from disk!"); + DBGMSG("Done hiding from disk!"); } } - LOGMSG("Done hiding!"); + DBGMSG("Done hiding!"); } */ if(!InitApplication())return false; - if(RemTh){LOGMSG("Terminating injected thread: %u", NtCurrentThreadId()); TerminateThread(GetCurrentThread(),0);} //Stack frame may be incorrect + if(ModInjFlags & InjLdr::mfRunRMTH){DBGMSG("Terminating injected thread(this): %u", NtCurrentThreadId()); NtTerminateThread(NtCurrentThread,0);} // Stack frame may be incorrect return true; //dres; } break; case DLL_THREAD_ATTACH: - LOGMSG("Hello DLL_THREAD_ATTACH"); if(Dbg && Dbg->IsActive())Dbg->TryAddCurrThread(); //Dbg->Report_CREATE_THREAD_DEBUG_EVENT(NtCurrentThreadId()); // For a simple testing break; case DLL_THREAD_DETACH: - if(Dbg && Dbg->IsActive())Dbg->Report_EXIT_THREAD_DEBUG_EVENT(NtCurrentThreadId(),0); // For a simple testing + if(Dbg && Dbg->IsActive())Dbg->Report_EXIT_THREAD_DEBUG_EVENT(NtCurrentTeb(),0); // For a simple testing break; case DLL_PROCESS_DETACH: - if(Dbg && Dbg->IsActive())Dbg->Report_EXIT_PROCESS_DEBUG_EVENT(0); // For a simple testing + if(Dbg && Dbg->IsActive())Dbg->Report_EXIT_PROCESS_DEBUG_EVENT(NtCurrentTeb(),0); // For a simple testing UnInitApplication(); break; @@ -173,21 +216,28 @@ BOOL APIENTRY DLLMain(HMODULE hModule, DWORD ReasonCall, LPVOID lpReserved) return true; } //==================================================================================== -void _stdcall LoadConfiguration(void) -{ - CJSonItem Root; +void _stdcall LoadConfiguration(void) // Avoid any specific file access here for now +{ +#ifdef _DEBUG + LogMode = lmFile; + // NSTR::StrCopy(LogFilePath, "C:\\TEST\\_LogMy.txt"); +#endif + +/* CJSonItem Root; CMiniStr str; str.FromFile(CfgFilePath); bool BinFmt = (str.Length())?(CJSonItem::IsBinaryEncrypted(str.c_data()) >= 0):(0); - LOGMSG("Loading config(Bin=%u): %ls", BinFmt, &CfgFilePath); + DBGMSG("Loading config(Bin=%u): %ls", BinFmt, &CfgFilePath); if(str.Length())Root.FromString(str); CJSonItem* Params = EnsureJsnParam(jsObject, "Parameters", &Root); LogMode = EnsureJsnParam((int)LogMode, "LogMode", Params)->GetValInt(); // lmCons;// - IPCSize = EnsureJsnParam(IPCSize, "IPCSize", Params)->GetValInt(); + IPCSize = EnsureJsnParam(IPCSize, "IPCSize", Params)->GetValInt(); + SuspMainThAtLd = EnsureJsnParam(SuspMainThAtLd, "SuspMainThAtLd", Params)->GetValBol(); //HideDllProxy = EnsureJsnParam(HideDllProxy, "HideDllProxy", Params)->GetValBol(); //HideDllProxyDsk = EnsureJsnParam(HideDllProxyDsk, "HideDllProxyDsk", Params)->GetValBol(); AllowEjectOnDetach = EnsureJsnParam(AllowEjectOnDetach, "AllowEjectOnDetach", Params)->GetValBol(); - // if(LogMode == lmCons)AllocConsole(); +// if(LogMode & lmCons){AllocConsole();} // SetWinConsoleSizes(1000, 500, 1000, 500); + CJSonItem* DbgParams = EnsureJsnParam(jsObject, "DbgClient", &Root); if(Dbg) { @@ -199,24 +249,25 @@ void _stdcall LoadConfiguration(void) Dbg->OnlyOwnTF = EnsureJsnParam(Dbg->OnlyOwnTF, "OnlyOwnTF", DbgParams)->GetValBol(); Dbg->SwBpVal = EnsureJsnParam((UINT)Dbg->SwBpVal, "SwBpVal", DbgParams)->GetValBol(); } - LOGMSG("Saving config(Bin=%u): %ls", BinFmt, &CfgFilePath); + DBGMSG("Saving config(Bin=%u): %ls", BinFmt, &CfgFilePath); str.Clear(); if(BinFmt)Root.ToBinary(str,true); Root.ToString(str,true); - str.ToFile(CfgFilePath); + str.ToFile(CfgFilePath); */ } //------------------------------------------------------------------------------------ void _stdcall SaveConfiguration(int BinFmt) { - CJSonItem Root; +/* CJSonItem Root; CMiniStr str; str.FromFile(CfgFilePath); bool VBinFmt = (str.Length() || (BinFmt < 0))?(CJSonItem::IsBinaryEncrypted(str.c_data()) >= 0):(BinFmt > 0); - LOGMSG("Loading config(Bin=%u): %ls", VBinFmt, &CfgFilePath); + DBGMSG("Loading config(Bin=%u): %ls", VBinFmt, &CfgFilePath); if(str.Length())Root.FromString(str); CJSonItem* Params = EnsureJsnParam(jsObject, "Parameters", &Root); LogMode = SetJsnParamValue((int)LogMode, "LogMode", Params)->GetValInt(); IPCSize = SetJsnParamValue(IPCSize, "IPCSize", Params)->GetValInt(); + SuspMainThAtLd = SetJsnParamValue(SuspMainThAtLd, "SuspMainThAtLd", Params)->GetValBol(); //HideDllProxy = SetJsnParamValue(HideDllProxy, "HideDllProxy", Params)->GetValBol(); //HideDllProxyDsk = SetJsnParamValue(HideDllProxyDsk, "HideDllProxyDsk", Params)->GetValBol(); AllowEjectOnDetach = SetJsnParamValue(AllowEjectOnDetach, "AllowEjectOnDetach", Params)->GetValBol(); @@ -232,66 +283,71 @@ void _stdcall SaveConfiguration(int BinFmt) SetJsnParamValue(Dbg->OnlyOwnTF, "OnlyOwnTF", DbgParams)->GetValBol(); SetJsnParamValue((UINT)Dbg->SwBpVal, "SwBpVal", DbgParams)->GetValBol(); } - LOGMSG("Saving config(Bin=%u): %ls", BinFmt, &CfgFilePath); + DBGMSG("Saving config(Bin=%u): %ls", BinFmt, &CfgFilePath); str.Clear(); if(VBinFmt)Root.ToBinary(str,true); Root.ToString(str,true); - str.ToFile(CfgFilePath); + str.ToFile(CfgFilePath); */ } //------------------------------------------------------------------------------------ bool _stdcall InitApplication(void) { - LOGMSG("Enter"); - if(GhDbg::CDbgClient::IsExistForID(GetCurrentProcessId())){LOGMSG("Already injected!"); return false;} + DBGMSG("Enter"); + if(GhDbg::CDbgClient::IsExistForID(NtCurrentProcessId())){DBGMSG("Already injected!"); return false;} /*#ifdef _AMD64_ - HookRtlRestoreContext.SetHook("RtlRestoreContext","ntdll.dll"); + HookRtlRestoreContext.SetHook("RtlRestoreContext","ntdll.dll"); // NtContinue #endif HookKiUserExceptionDispatcher.SetHook("KiUserExceptionDispatcher","ntdll.dll"); HookLdrInitializeThunk.SetHook("LdrInitializeThunk","ntdll.dll"); */ - Dbg = new GhDbg::CDbgClient; + Dbg = new ((void*)&ArrDbgClient) GhDbg::CDbgClient(ThisLibBase); Dbg->UsrReqCallback = &DbgUsrReqCallback; - LoadConfiguration(); - LOGMSG("IPC created: IPCSize=%u",IPCSize); - HookNtMapViewOfSection.SetHook("NtMapViewOfSection","ntdll.dll"); - HookNtUnmapViewOfSection.SetHook("NtUnmapViewOfSection","ntdll.dll"); - HookNtGetContextThread.SetHook("NtGetContextThread","ntdll.dll"); - HookNtSetContextThread.SetHook("NtSetContextThread","ntdll.dll"); - HookNtTerminateThread.SetHook("NtTerminateThread","ntdll.dll"); -// HookNtTerminateProcess.SetHook("NtTerminateProcess","ntdll.dll"); // Only for ProxyRestore to disk - HookNtContinue.SetHook("NtContinue","ntdll.dll"); - ExpDispHook.SetHook(ProcExpDispBefore, ProcExpDispAfter); - LOGMSG("Hooks set"); - Dbg->Start(IPCSize, hIpcTh); // Start it from DLL Main to avoid of similair DLL being loaded again - LOGMSG("IPC started"); +////////// LoadConfiguration(); + DBGMSG("IPC created: IPCSize=%u",IPCSize); + PVOID pNtDll = GetNtDllBaseFast(); + DBGMSG("NtDllBase: %p",pNtDll); + HookNtMapViewOfSection.SetHook(GetProcAddr(pNtDll, ctENCSA("NtMapViewOfSection"))); // For DLLs list + HookNtUnmapViewOfSection.SetHook(GetProcAddr(pNtDll, ctENCSA("NtUnmapViewOfSection"))); // For DLLs list + HookNtGetContextThread.SetHook(GetProcAddr(pNtDll, ctENCSA("NtGetContextThread"))); // For DRx hiding + HookNtSetContextThread.SetHook(GetProcAddr(pNtDll, ctENCSA("NtSetContextThread"))); // For DRx hiding + HookNtTerminateThread.SetHook(GetProcAddr(pNtDll, ctENCSA("NtTerminateThread"))); // For Thread list update + HookNtTerminateProcess.SetHook(GetProcAddr(pNtDll, ctENCSA("NtTerminateProcess"))); // Importand for latest Windows 10 bugs + HookNtContinue.SetHook(GetProcAddr(pNtDll, ctENCSA("NtContinue"))); // For Thread list update // TODO: Replace with LdrInitializeThunk hook + ExpDispHook.SetHook(ProcExpDispBefore, ProcExpDispAfter); // Debugger core function + LdrpInitHook.SetHook(ProcLdrpInitialize); // Optional: HookNtContinue can do the job but threads will be reported after initialization + DBGMSG("Hooks set: hIpcTh=%p, MainThId=%u",hIpcTh, MainThId); + Dbg->Start(IPCSize, hIpcTh, NULL, MainThId, (hIpcTh != NtCurrentThread)?NtCurrentThreadId():0); // Start it from DLL Main to avoid of similair DLL being loaded again // Exclude current temporary thread + DBGMSG("IPC started"); return true; } //------------------------------------------------------------------------------------ void _stdcall UnInitApplication(void) -{ +{ + DBGMSG("Enter"); + if(Dbg)Dbg->~CDbgClient(); // delete(Dbg); // Compiler will ALWAYS put an unused call to operator DELETE here if called 'Dbg->~CDbgClient()' in Release build if not specified '/MTd'(Multi-Threaded Debug) but it will define '_DEBUG to 1'! + DBGMSG("IPC destroyed"); HookNtContinue.Remove(); HookNtTerminateThread.Remove(); - //HookNtTerminateProcess.Remove(); + HookNtTerminateProcess.Remove(); HookNtUnmapViewOfSection.Remove(); HookNtMapViewOfSection.Remove(); HookNtSetContextThread.Remove(); HookNtGetContextThread.Remove(); + LdrpInitHook.Remove(); ExpDispHook.Remove(); - LOGMSG("Hooks removed"); - if(Dbg)delete(Dbg); - LOGMSG("IPC destroyed"); + DBGMSG("Hooks removed"); } //------------------------------------------------------------------------------------ -int _stdcall DbgUsrReqCallback(ShMem::CMessageIPC::SMsgHdr* Req, PVOID ArgA, UINT ArgB) +int _fastcall DbgUsrReqCallback(ShMem::CMessageIPC::SMsgHdr* Req, PVOID ArgA, UINT ArgB) { if(Req->MsgID == GhDbg::miDbgGetConfigs) { ShMem::CArgPack<>* apo = (ShMem::CArgPack<>*)ArgA; // apo->PushArgEx(HideDllProxy, "Hide Proxy DLL (After Restart)", GhDbg::CDbgClient::MakeCfgItemID(++ArgB,GhDbg::dtBool)); // apo->PushArgEx(HideDllProxyDsk, "Hide Proxy DLL on Disk (After Restart)", GhDbg::CDbgClient::MakeCfgItemID(++ArgB,GhDbg::dtBool)); - if(ModInjected)apo->PushArgEx(AllowEjectOnDetach, "Allow Eject On Detach", GhDbg::CDbgClient::MakeCfgItemID(++ArgB,GhDbg::dtBool)); - if(ModInjected){bool Nons = false; apo->PushArgEx(Nons, "Eject", GhDbg::CDbgClient::MakeCfgItemID(++ArgB,GhDbg::dtBool));} + if(!ModInjFlags)apo->PushArgEx(AllowEjectOnDetach, ctENCSA("Allow Eject On Detach"), GhDbg::CDbgClient::MakeCfgItemID(++ArgB,GhDbg::dtBool)); + if(!ModInjFlags){bool Nons = false; apo->PushArgEx(Nons, ctENCSA("Eject"), GhDbg::CDbgClient::MakeCfgItemID(++ArgB,GhDbg::dtBool));} return ArgB; } if(Req->MsgID == GhDbg::miDbgSetConfigs) @@ -312,12 +368,12 @@ int _stdcall DbgUsrReqCallback(ShMem::CMessageIPC::SMsgHdr* Req, PVOID ArgA, UIN AllowEjectOnDetach = *(bool*)ArgA; break; case 2: - if(ModInjected) + if(!ModInjFlags) { - LOGMSG("Ejecting by user!"); + DBGMSG("Ejecting by user!"); UnInitApplication(); - LOGMSG("Uninit done. Unmapping..."); - InjLdr::UnmapAndTerminateSelf(ThisLibBase); + DBGMSG("Uninit done. Unmapping..."); + NtTerminateThread(NtCurrentThread, 0); //// InjLdr::UnmapAndTerminateSelf(ThisLibBase); // TODO: Self unmap or deallocate } break; } @@ -327,12 +383,12 @@ int _stdcall DbgUsrReqCallback(ShMem::CMessageIPC::SMsgHdr* Req, PVOID ArgA, UIN } if(Req->MsgID == GhDbg::miDbgDetachNtfy) { - if(AllowEjectOnDetach && ModInjected) + if(AllowEjectOnDetach && !ModInjFlags) { - LOGMSG("Ejecting on Detach!"); + DBGMSG("Ejecting on Detach!"); UnInitApplication(); - LOGMSG("Uninit done. Unmapping..."); - InjLdr::UnmapAndTerminateSelf(ThisLibBase); + DBGMSG("Uninit done. Unmapping..."); + NtTerminateThread(NtCurrentThread, 0); //// InjLdr::UnmapAndTerminateSelf(ThisLibBase); // TODO: Self unmap or deallocate } } return 0; @@ -372,6 +428,12 @@ void NTAPI ProcLdrInitializeThunk(PVOID ArgA, PVOID ArgB, PVOID ArgC, PVOID ArgD HookLdrInitializeThunk.OrigProc(ArgA, ArgB, ArgC, ArgD); // Must be tail optimized - Requires optimization to be enabled (O1,O2,Ox) } */ //------------------------------------------------------------------------------------ +void _stdcall ProcLdrpInitialize(volatile PCONTEXT Ctx, volatile PVOID NtDllBase) +{ + DBGMSG("RetAddr=%p, Ctx=%p, NtDllBase=%p",_ReturnAddress(),Ctx,NtDllBase); +// if(Dbg && Dbg->IsActive()){LastThID = NtCurrentThreadId(); Dbg->TryAddCurrThread();} // LastThID prevents TryAddCurrThread from ProcNtContinue +} +//------------------------------------------------------------------------------------ /* x64 RSP+00 = RetAddr RSP+08 = RCX @@ -388,10 +450,10 @@ RSP+20 = R9 */ bool _cdecl ProcExpDispBefore(volatile PVOID ArgA, volatile PVOID ArgB, volatile PVOID ArgC, volatile PVOID ArgD, volatile PVOID RetVal) { - DBGMSG("Code=%08X, Addr=%p, FCtx=%08X",((PEXCEPTION_RECORD)ArgA)->ExceptionCode, ((PEXCEPTION_RECORD)ArgA)->ExceptionAddress, ((PCONTEXT)ArgB)->ContextFlags); +// DBGMSG("Code=%08X, Addr=%p, FCtx=%08X",((PEXCEPTION_RECORD)ArgA)->ExceptionCode, ((PEXCEPTION_RECORD)ArgA)->ExceptionAddress, ((PCONTEXT)ArgB)->ContextFlags); DWORD ThID = LastExcThID = NtCurrentThreadId(); if(!Dbg || !Dbg->IsActive() || Dbg->IsDbgThreadID(ThID))return true; - if(Dbg->HandleException(ThID, (PEXCEPTION_RECORD)ArgA, (PCONTEXT)ArgB)){RetVal = (PVOID)TRUE; DBGMSG("Handled!"); return false;} // Handled by a debugger + if(Dbg->HandleException(ThID, (PEXCEPTION_RECORD)ArgA, (PCONTEXT)ArgB)){RetVal = (PVOID)TRUE; /*DBGMSG("Handled!");*/ return false;} // Handled by a debugger if(!Dbg->HideDbgState)return true; // CONTEXT ForgedCtx; // No debugger context hiding for now :( // Can it be detected that this is a copy of original CONTEXT and have a different address on stack? @@ -403,8 +465,8 @@ bool _cdecl ProcExpDispBefore(volatile PVOID ArgA, volatile PVOID ArgB, volatile //------------------------------------------------------------------------------------ bool _cdecl ProcExpDispAfter(volatile PVOID ArgA, volatile PVOID ArgB, volatile PVOID ArgC, volatile PVOID ArgD, volatile PVOID RetVal) { - DBGMSG("Exiting!"); -// Dbg->DebugThreadSave(ThID, &ForgedCtx); // Save any modifications to DRx in a separate struct +// DBGMSG("Exiting!"); +// Dbg->DebugThreadSave(ThID, &ForgedCtx); // Save any modifications to DRx in a separate struct // NOTE: It is rare that a AntiDebug check will use an exception handlers to check DRx? // Dbg->DebugRstExcContext(ContextRecord, &ForgedCtx); return true; } @@ -416,50 +478,57 @@ bool _cdecl ProcExpDispAfter(volatile PVOID ArgA, volatile PVOID ArgB, volatile // Normal Dll map: AllocationType=00800000[MEM_ROTATE], Win32Protect=00000004[PAGE_READWRITE] // Win10 // // WinXPx32: ZwMapViewOfSection(v88, -1, (int)&v89, 0, 0, 0, (int)&v86, 1, 0, 4); -// Win7x64: ZwMapViewOfSection(v9, -1i64, v13, 0i64, 0i64, 0i64, v12, 1, v10, 4); +// Win7x64: ZwMapViewOfSection(v9, -1i64, v13, 0i64, 0i64, 0i64, v12, 1, v10, 4); // NTSTATUS NTAPI ProcNtMapViewOfSection(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset, PSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Win32Protect) // NOTE: A detectable hook! { - NTSTATUS res = HookNtMapViewOfSection.OrigProc(SectionHandle,ProcessHandle,BaseAddress,ZeroBits,CommitSize,SectionOffset,ViewSize,InheritDisposition,AllocationType,Win32Protect); - if(!res && BaseAddress && *BaseAddress && ViewSize && *ViewSize && Dbg && Dbg->IsActive() && GhDbg::IsCurrentProcess(ProcessHandle) && IsValidPEHeaderBlk(*BaseAddress, Dbg->IsMemAvailable(*BaseAddress))) // Try to get the module`s name? + NTSTATUS res = HookNtMapViewOfSection.OrigProc(SectionHandle,ProcessHandle,BaseAddress,ZeroBits,CommitSize,SectionOffset,ViewSize,InheritDisposition,AllocationType,Win32Protect); // May return STATUS_IMAGE_NOT_AT_BASE + if((res >= 0) && BaseAddress && *BaseAddress && ViewSize && *ViewSize) // && Dbg && Dbg->IsActive() && GhDbg::IsCurrentProcess(ProcessHandle) && IsValidPEHeaderBlk(*BaseAddress, Dbg->IsMemAvailable(*BaseAddress))) // Try to get the module`s name? { - DBGMSG("Module: Status=%08X, SectionHandle=%p, BaseAddress=%p, ViewSize=%08X, AllocationType=%08X, Win32Protect=%08X",res,SectionHandle,*BaseAddress,*ViewSize,AllocationType,Win32Protect); - if(Dbg && Dbg->IsActive() && (Win32Protect == PAGE_READWRITE)) // <<< Duplicate mapping causes BPs to be set again and never removed if this module is already loaded(If this is not caused by LdrLoadDll)! + DBGMSG("Module: Status=%08X, SectionHandle=%p, BaseAddress=%p, ViewSize=%08X, AllocationType=%08X, Win32Protect=%08X",res,SectionHandle,*BaseAddress,*ViewSize,AllocationType,Win32Protect); + if(Dbg && Dbg->IsActive() && (Win32Protect == PAGE_READWRITE) && NNTDLL::IsCurrentProcess(ProcessHandle) && IsValidPEHeaderBlk(*BaseAddress, Dbg->IsMemAvailable(*BaseAddress))) // <<< Duplicate mapping causes BPs to be set again and never removed if this module is already loaded(If this is not caused by LdrLoadDll)! { - Dbg->Report_LOAD_DLL_DEBUG_INFO(*BaseAddress); // NOTE: This is done before PE configuration by LdrLoadDll // Events:TLS Callbacks must be disabled or xg4dbg will crash in 'cbLoadDll{ auto modInfo = ModInfoFromAddr(duint(base));}' (because it won`t check for NULL) if this mapping will be unmapped too soon + Dbg->Report_LOAD_DLL_DEBUG_INFO(NtCurrentTeb(), *BaseAddress); // NOTE: This is done before PE configuration by LdrLoadDll // Events:TLS Callbacks must be disabled or xg4dbg will crash in 'cbLoadDll{ auto modInfo = ModInfoFromAddr(duint(base));}' (because it won`t check for NULL) if this mapping will be unmapped too soon } } -// else {LOGMSG("Status=%08X, SectionHandle=%p, ViewSize=%08X, AllocationType=%08X, Win32Protect=%08X",res,SectionHandle,ViewSize,AllocationType,Win32Protect);} +// else {DBGMSG("Status=%08X, SectionHandle=%p, ViewSize=%08X, AllocationType=%08X, Win32Protect=%08X",res,SectionHandle,ViewSize,AllocationType,Win32Protect);} return res; } //------------------------------------------------------------------------------------ NTSTATUS NTAPI ProcNtUnmapViewOfSection(HANDLE ProcessHandle, PVOID BaseAddress) // NOTE: A detectable hook! { - if(GhDbg::IsCurrentProcess(ProcessHandle) && IsValidPEHeaderBlk(BaseAddress, Dbg->IsMemAvailable(BaseAddress))) + if(NNTDLL::IsCurrentProcess(ProcessHandle) && IsValidPEHeaderBlk(BaseAddress, Dbg->IsMemAvailable(BaseAddress))) { - DBGMSG("BaseAddress=%p",BaseAddress); - if(Dbg && Dbg->IsActive() && Dbg->IsOtherConnections())Dbg->Report_UNLOAD_DLL_DEBUG_EVENT(BaseAddress); + DBGMSG("BaseAddress=%p",BaseAddress); + if(Dbg && Dbg->IsActive() && Dbg->IsOtherConnections())Dbg->Report_UNLOAD_DLL_DEBUG_EVENT(NtCurrentTeb(), BaseAddress); } return HookNtUnmapViewOfSection.OrigProc(ProcessHandle,BaseAddress); } //------------------------------------------------------------------------------------ -// Called at start of a thread and at return from APC/Exception -// Protect CONTEXT here? Can be used separately from any kernel callback? +// Called at start of a thread(After initialization) and at return from APC/Exception +// Protect CONTEXT here? Can be used separately from any kernel callback? // Is someone using it for current thread`s CONTEXT modification(DRx corruption)? // On Win10 x64 it is called by RtlRestoreContext for normal exceptions but it is not used on Win7 x64 // On x32 it is called from LdrInitializeThunk when a new thread created (User mode thread`s entry point) +// LdrInitializeThunk exits with NtContinue // Hooking LdrInitializeThunk is hard // NTSTATUS NTAPI ProcNtContinue(PCONTEXT ContextRecord, BOOLEAN TestAlert) // NOTE: A detectable hook! // NOTE: Too much overhead of exception processing with this(Twice 'GetThread' on breakpoints) { - static DWORD LastThID = -1; DWORD CurrThID = NtCurrentThreadId(); - if(Dbg && (CurrThID != LastThID) && (CurrThID != LastExcThID) && Dbg->IsActive())Dbg->TryAddCurrThread(); // Report this thread if it is not in list yet + if(Dbg && (CurrThID != LastThID) && (CurrThID != LastExcThID) && Dbg->IsActive()){LastThID=CurrThID; Dbg->TryAddCurrThread();} // Report this thread if it is not in list yet return HookNtContinue.OrigProc(ContextRecord, TestAlert); // Will not return } //------------------------------------------------------------------------------------ -/*NTSTATUS NTAPI ProcNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus) // DISABLED: Just let IPC to timeout +// ProcessHandle is NULL then terminates all threads, except a current one +// RtlExitUserProcess: NtTerminateProcess(NULL), NtTerminateProcess(NtCurrentProcess) +// On Win 10 (2019) some suspended threads won`t be terminated! +// Some race condition with NtSuspendThread and NtTerminateProcess when called from different threads at almost same time? +// And they will be suspended when reporting i.e. DLL_UNLOAD, but GhostDbg thread already dead and can`t unfreeze these threads +// Could be a useful feature of NtSuspendProcess ;) +NTSTATUS NTAPI ProcNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus) { - if(RstDskHiddenProxy && ProxyDllCopy) // Restore the Proxy Dll on disk + DBGMSG("ProcessHandle=%p, ExitStatus=%08X",ProcessHandle,ExitStatus); +/* if(RstDskHiddenProxy && ProxyDllCopy) // Restore the Proxy Dll on disk { BYTE DllPath[MAX_PATH]; for(UINT ctr=0,total=ProxyDllSize+MAX_PATH;ctr < total;ctr++)ProxyDllCopy[ctr] = DecryptByteWithCtr(ProxyDllCopy[ctr], ProxyEncKey, ctr); @@ -472,38 +541,45 @@ NTSTATUS NTAPI ProcNtContinue(PCONTEXT ContextRecord, BOOLEAN TestAlert) // NO CloseHandle(hFile); VirtualFree(ProxyDllCopy,0,MEM_RELEASE); ProxyDllCopy = NULL; - LOGMSG("ProxyDll Restored: %s", (LPSTR)&DllPath); + DBGMSG("ProxyDll Restored: %s", (LPSTR)&DllPath); } + } */ + if(Dbg && Dbg->IsActive() && (!ProcessHandle || (ProcessHandle == NtCurrentProcess))) + { + if(!ProcessHandle)Dbg->Report_EXIT_PROCESS_DEBUG_EVENT(NtCurrentTeb(),0); // (ProcessHandle==NULL) will terminate all other threads, including GhostDbg + else UnInitApplication(); // Do not report EXIT_PROCESS_DEBUG_EVENT second time! } return HookNtTerminateProcess.OrigProc(ProcessHandle, ExitStatus); -} */ +} //------------------------------------------------------------------------------------ // Exit from a thread`s proc will also end up here // ThreadHandle is NULL for a current thread; NtCurrentThread is also works NTSTATUS NTAPI ProcNtTerminateThread(HANDLE ThreadHandle, NTSTATUS ExitStatus) // NOTE: A detectable hook! { - ULONG ThID; - if(Dbg && Dbg->IsActive() && (ThID=GhDbg::GetCurrProcessThreadID(ThreadHandle)))Dbg->Report_EXIT_THREAD_DEBUG_EVENT(ThID,ExitStatus); + PTEB teb = NULL; + DBGMSG("ThreadHandle=%p",ThreadHandle); + if(Dbg && Dbg->IsActive() && (teb=NNTDLL::GetCurrProcessTEB(ThreadHandle))){ Dbg->Report_EXIT_THREAD_DEBUG_EVENT(teb,ExitStatus); } + DBGMSG("Terminating: %p, %u",teb,(teb?(UINT)teb->ClientId.UniqueThread:0)); return HookNtTerminateThread.OrigProc(ThreadHandle, ExitStatus); // TODO: Just call our own NtTerminateThread } //------------------------------------------------------------------------------------ NTSTATUS NTAPI ProcNtGetContextThread(HANDLE ThreadHandle, PCONTEXT Context) // NOTE: A detectable hook! { ULONG ThID; - NTSTATUS res = HookNtGetContextThread.OrigProc(ThreadHandle, Context); // TODO: Just call our own NtGetContextThread - if(!res && Dbg && Dbg->IsActive() && Dbg->HideDbgState && (ThID=GhDbg::GetCurrProcessThreadID(ThreadHandle)))Dbg->DebugThreadLoad(ThID, Context); // Load into CONTEXT a previously saved DRx instead of currently read ones + NTSTATUS res = HookNtGetContextThread.OrigProc(ThreadHandle, Context); // TODO: Just call our own NtGetContextThread(What if it is hooked by someone)? + if(!res && Dbg && Dbg->IsActive() && Dbg->HideDbgState && (ThID=NNTDLL::GetCurrProcessThreadID(ThreadHandle)))Dbg->DebugThreadLoad(ThID, Context); // Load into CONTEXT a previously saved DRx instead of currently read ones return res; } //------------------------------------------------------------------------------------ NTSTATUS NTAPI ProcNtSetContextThread(HANDLE ThreadHandle, PCONTEXT Context) // NOTE: A detectable hook! // Do not let DRx to be changed by this { ULONG ThID; - if(!Dbg || !Dbg->IsActive() || !Dbg->HideDbgState || !(ThID=GhDbg::GetCurrProcessThreadID(ThreadHandle)))return HookNtSetContextThread.OrigProc(ThreadHandle, Context); // TODO: Just call our own NtSetContextThread + if(!Dbg || !Dbg->IsActive() || !Dbg->HideDbgState || !(ThID=NNTDLL::GetCurrProcessThreadID(ThreadHandle)))return HookNtSetContextThread.OrigProc(ThreadHandle, Context); // TODO: Just call our own NtSetContextThread CONTEXT FCtx; // Copy of CONTEXT where CONTEXT_DEBUG_REGISTERS is removed (CONTEXT_DEBUG_REGISTERS must be preserved in original Context in case it may be checked) Dbg->DebugThreadSave(ThID, Context); // Save any magic numbers which may be stored in debug registers to detect a debugger memcpy(&FCtx,Context,sizeof(CONTEXT)); FCtx.ContextFlags &= ~0x00000010; // CONTEXT_DEBUG_REGISTERS // TF is allowed to change? - return HookNtSetContextThread.OrigProc(ThreadHandle, &FCtx); // TODO: Just call our own NtSetContextThread + return HookNtSetContextThread.OrigProc(ThreadHandle, &FCtx); // TODO: Just call our own NtSetContextThread(What if it is hooked by someone)? } //------------------------------------------------------------------------------------ diff --git a/InjectLib/InjectDll.h b/InjectLib/InjectDll.h index e5721d1..8fcc6cc 100644 --- a/InjectLib/InjectDll.h +++ b/InjectLib/InjectDll.h @@ -1,15 +1,29 @@ #pragma once - +/* + Copyright (c) 2020 Victor Sheinmann, Vicshann@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + #include -#include +//#include #include "Utils.h" #include "UniHook.hpp" #include "json.h" -#include "FormatPE.h" +#include "NtDllEx.hpp" #include "CompileTime.hpp" #include "InjDllLdr.hpp" #include "GhostDbg.hpp" +#include "..\..\GlobalInjector\GInjer\LoaderCode.h" //==================================================================================== #define CFGFILE ".InjLib.jsn" @@ -20,15 +34,16 @@ void _stdcall LoadConfiguration(void); void _stdcall SaveConfiguration(int BinFmt=-1); void _stdcall UnInitApplication(void); bool _stdcall InitApplication(void); -int _stdcall DbgUsrReqCallback(ShMem::CMessageIPC::SMsgHdr* Req, PVOID ArgA, UINT ArgB); +int _fastcall DbgUsrReqCallback(ShMem::CMessageIPC::SMsgHdr* Req, PVOID ArgA, UINT ArgB); //------------------------------------------------------------------------------------ bool _cdecl ProcExpDispBefore(volatile PVOID ArgA, volatile PVOID ArgB, volatile PVOID ArgC, volatile PVOID ArgD, volatile PVOID RetVal); bool _cdecl ProcExpDispAfter(volatile PVOID ArgA, volatile PVOID ArgB, volatile PVOID ArgC, volatile PVOID ArgD, volatile PVOID RetVal); //__declspec(noreturn) VOID NTAPI ProcRtlRestoreContext(PCONTEXT ContextRecord, PEXCEPTION_RECORD ExceptionRecord); //__declspec(noreturn) void _fastcall ProcKiUserExceptionDispatcher(void); //__declspec(noreturn) void NTAPI ProcLdrInitializeThunk(PVOID ArgA, PVOID ArgB, PVOID ArgC, PVOID ArgD); +void _stdcall ProcLdrpInitialize(volatile PCONTEXT Ctx, volatile PVOID NtDllBase); NTSTATUS NTAPI ProcNtContinue(PCONTEXT ContextRecord, BOOLEAN TestAlert); -//NTSTATUS NTAPI ProcNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus); +NTSTATUS NTAPI ProcNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus); NTSTATUS NTAPI ProcNtTerminateThread(HANDLE ThreadHandle, NTSTATUS ExitStatus); NTSTATUS NTAPI ProcNtGetContextThread(HANDLE ThreadHandle, PCONTEXT Context); NTSTATUS NTAPI ProcNtSetContextThread(HANDLE ThreadHandle, PCONTEXT Context); diff --git a/InjectLib/InjectDll.vcxproj b/InjectLib/InjectDll.vcxproj index 5d7d67d..d3c91f4 100644 --- a/InjectLib/InjectDll.vcxproj +++ b/InjectLib/InjectDll.vcxproj @@ -112,30 +112,31 @@ - Disabled + Full Speed $(SolutionDir)COMMON\;$(SolutionDir)COMMON\ThirdParty\;$(SolutionDir)COMMON\ModuleTools\;$(SolutionDir)COMMON\ThirdParty\ntdll\;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + WIN32;_NODESTR;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) false false Default MultiThreadedDebug - - + Default false - StreamingSIMDExtensions + StreamingSIMDExtensions2 false NotUsing Level3 false ProgramDatabase - StdCall + FastCall false true true true OnlyExplicitInline stdcpplatest + /Gs100000000 %(AdditionalOptions) + false LinkVerboseLib @@ -172,24 +173,23 @@ - Disabled + Full Speed $(SolutionDir)COMMON\;$(SolutionDir)COMMON\ThirdParty\;$(SolutionDir)COMMON\ModuleTools\;$(SolutionDir)COMMON\ThirdParty\ntdll\;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + WIN32;_NODESTR;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) false false Default MultiThreadedDebug - - + Default false - StreamingSIMDExtensions + NotSet false NotUsing Level3 false ProgramDatabase - StdCall + FastCall false true true @@ -197,6 +197,8 @@ false OnlyExplicitInline stdcpplatest + /Gs100000000 %(AdditionalOptions) + false LinkVerboseLib @@ -235,24 +237,25 @@ true false $(SolutionDir)COMMON\;$(SolutionDir)COMMON\ThirdParty\;$(SolutionDir)COMMON\ModuleTools\;$(SolutionDir)COMMON\ThirdParty\ntdll\;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + WIN32;_NODESTR;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true false MultiThreaded - - + Default false - StreamingSIMDExtensions + StreamingSIMDExtensions2 false NotUsing Level3 false - StdCall - CompileAsCpp + FastCall + Default true stdcpplatest + /Gs100000000 %(AdditionalOptions) + false LinkVerboseLib @@ -297,24 +300,25 @@ copy /Y "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)BUILD\RELEASE\$(Solu true false $(SolutionDir)COMMON\;$(SolutionDir)COMMON\ThirdParty\;$(SolutionDir)COMMON\ModuleTools\;$(SolutionDir)COMMON\ThirdParty\ntdll\;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + WIN32;_NODESTR;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true false MultiThreaded - - + Default false - StreamingSIMDExtensions + NotSet false NotUsing Level3 false - StdCall - CompileAsCpp + FastCall + Default true stdcpplatest + /Gs100000000 %(AdditionalOptions) + false LinkVerboseLib @@ -362,6 +366,7 @@ copy /Y "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)BUILD\RELEASE\$(Solu + diff --git a/InjectLib/InjectDll.vcxproj.filters b/InjectLib/InjectDll.vcxproj.filters index 7f6358c..49aacea 100644 --- a/InjectLib/InjectDll.vcxproj.filters +++ b/InjectLib/InjectDll.vcxproj.filters @@ -65,5 +65,8 @@ Common + + Common + \ No newline at end of file diff --git a/NOTES.txt b/NOTES.txt index 7290ed1..3134a17 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -15,24 +15,98 @@ ETHREAD -> StartAddress ? https://www.codeproject.com/Articles/543542/Windows-x-system-service-hooks-and-advanced-debu + https://www.codeproject.com/Articles/543542/Windows-x64-system-service-hooks-and-advanced-debu TODO: - Unload inject DLL if it was loaded by a debugger and it is going to detach + Unload inject DLL if it was loaded by a debugger and it is going to detach? Intercept Any exception (Optional) - Start target as Explorer.exe + Start a target as Explorer.exe Hardware BP only for an active thread (Last for GetThreadContext?) Inject into child processes - Block constant Thread Suspend/Resume calls except for BP setting - Kernel exception dispatcer hook(No hook of KiUserExceptionDispatcher and NtContinue) - Use unly a low level WinAPI in GhostDbg.hpp + Block constant Thread Suspend/Resume calls by x64Dbg if it is already in DbgEvent handler? + Kernel exception dispatcher hook(No hook of KiUserExceptionDispatcher and NtContinue) (GhostDrv) + Make DebugBreak less intrusive (No CreateThread?) (APC?) + Alternate software BP support? + Single IPC buffer for all GhostDbg clients (Required for GhostDrv) + IPC SRW locks + Enable Config files + IDA compatibility + Fix NativeCreateThread on latest Windows 10 + Fix extreme slowness on Windows XP (Sync problem? Fix for GhostDrv) // https://communities.vmware.com/thread/466749 + GInjer compatibility (Especially WOW64 debugging) ISSUES: - Software breakpoints is very dangerous to set + Software breakpoints is very dangerous to set if not all threads are suspended on a Debug Event Because of SEH recursion a DBG context protection is not used(Find a way to mark a thread with RefCtr) + +//----------------------------------------------------------------------------------------------------------------- + MSVC compiler will always generate ExceptionDirectory for x64 builds. Need to use CLANG to make InjLib smaller: '/clang:-fno-unwind-tables' +//--------------------------- + +BOOL IsWow64Process2(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine) --> NTSTATUS RtlWow64GetProcessMachines(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine); + //----------------------------------------------------------------------------------------------------------------- How DebugActiveProcess works: 1) Suspends all process` threads 2) Creates a remote thread at ntdll.dll:DbgUiRemoteBreakin - 3) Suspends all other process` threads again (Including at DbgUiRemoteBreakin) // Suspend count 1,2,2,2,... + 3) Suspends all other process` threads again (Including at DbgUiRemoteBreakin) // Suspend count is 1,2,2,2,... + + + ------------------------------------------ + https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-resumethread + + Note that while reporting debug events, all threads within the reporting process are frozen. + Debuggers are expected to use the SuspendThread and ResumeThread functions to limit the set of threads that can execute within a process. + By suspending all threads in a process except for the one reporting a debug event, it is possible to "single step" a single thread. + The other threads are not released by a continue operation if they are suspended. + + ----------------------------------------------------------------------- + _chkstk Routine is a helper routine for the C compiler. For x86 compilers, _chkstk Routine is called when the local variables exceed 4K bytes; for x64 compilers it is 8K. + + ------------------------------------- + RtlDispatchException hooking: + 1) Find a call to RtlDispatchException in KiUserExceptionDispatcher + 2) Put a jump to a stub at beginning of RtlDispatchException (Save original instructions) + + ---------------------------------------------------------------------- + __int64 __fastcall Wow64NtCreateThread(_QWORD *a1, unsigned int a2, __int64 a3, __int64 aProcessHandle, __int64 a5, CONTEXT *a6, __int64 a7, char aSuspended) +{ + void *vProcessHandle; // rbp + struct _OBJECT_ATTRIBUTES *v9; // r15 + unsigned int v10; // er14 + void **v11; // rbx + __int64 result; // rax + NTSTATUS vStatus; // edi + __int16 vMachine; // [rsp+40h] [rbp-538h] + CONTEXT Dst; // [rsp+50h] [rbp-528h] + vProcessHandle = (void *)aProcessHandle; + v9 = (struct _OBJECT_ATTRIBUTES *)a3; + v10 = a2; + v11 = (void **)a1; + if ( !a1 || !a7 || !a6 )return 0xC000000Di64; + result = RtlWow64GetProcessMachines(aProcessHandle, &vMachine, 0i64); + if ( (int)result < 0 )return result; + if ( vMachine )return 0xC0000022i64; // ??????????????????????????????? // Not IMAGE_FILE_MACHINE_UNKNOWN // IsWow64Process is TRUE + memset_0(&Dst, 0, 1232u); + Dst.Rip = LODWORD(a6->R8); + Dst.Rcx = LODWORD(a6->Rdi); + Dst.Rdx = HIDWORD(a6->Rbp); + Dst.R8 = HIDWORD(a6->R9); + Dst.ContextFlags = 0x100003; + vStatus = NtCreateThread(v11, v10, v9, vProcessHandle, (PCLIENT_ID)a5, &Dst, (PINITIAL_TEB)a7, 1u); + if ( vStatus < 0 )return (unsigned int)vStatus; + if ( vMachine == 0x14C && (unsigned __int16)RtlWow64GetCurrentMachine() == 0x14C )Wow64pCpuInitializeStartupContext((__int64)vProcessHandle, (__int64)*v11, (__int64)a6); + if ( !aSuspended ) + { + vStatus = NtResumeThread(*v11, 0i64); + if ( vStatus < 0 ) + { + NtTerminateThread(*v11, 0); + return (unsigned int)vStatus; + } + } + return 0i64; +} + ---------------------------------------------------------------------- diff --git a/XDbgPlugin/LogoIcon.ico b/XDbgPlugin/LogoIcon.ico new file mode 100644 index 0000000..398b368 Binary files /dev/null and b/XDbgPlugin/LogoIcon.ico differ diff --git a/XDbgPlugin/Resources.rc b/XDbgPlugin/Resources.rc index 6198aa8..ef3c3c0 100644 --- a/XDbgPlugin/Resources.rc +++ b/XDbgPlugin/Resources.rc @@ -7,7 +7,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 - +5 ICON "LogoIcon.ico" MAINICON RCDATA "MainIcon.png" // ICON InjLib RCDATA INJLIBPATH @@ -16,8 +16,8 @@ InjLib RCDATA INJLIBPATH // Version // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,1,0,0 - PRODUCTVERSION 1,1,0,0 + FILEVERSION 2,1,0,0 + PRODUCTVERSION 2,1,0,0 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -35,12 +35,12 @@ BEGIN VALUE "Comments", "https://github.com/Vicshann" // Github page VALUE "CompanyName", "Vicshann" VALUE "FileDescription", "x64Dbg plugin" - VALUE "FileVersion", "0, 8, 0, 0" + VALUE "FileVersion", "2, 0, 0, 0" VALUE "InternalName", "GhostDbg" - VALUE "LegalCopyright", "Copyright (c) 2018 Vicshann" + VALUE "LegalCopyright", "Copyright (c) 2020 Vicshann" VALUE "OriginalFilename", "GhostDbg.dll" VALUE "ProductName", "GhostDbg" - VALUE "ProductVersion", "0, 8, 0, 0" + VALUE "ProductVersion", "2, 0, 0, 0" END END BLOCK "VarFileInfo" diff --git a/XDbgPlugin/XDbgPlugin.cpp b/XDbgPlugin/XDbgPlugin.cpp index cc77d39..ce90046 100644 --- a/XDbgPlugin/XDbgPlugin.cpp +++ b/XDbgPlugin/XDbgPlugin.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2018 Victor Sheinmann, Vicshann@gmail.com + Copyright (c) 2020 Victor Sheinmann, Vicshann@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, @@ -15,7 +15,6 @@ #include "XDbgPlugin.h" #include "_plugins.h" - #pragma comment(linker,"/ENTRY:DLLMain") #pragma comment(linker,"/NODEFAULTLIB") @@ -24,11 +23,12 @@ typedef ShMem SHM; typedef GhDbg XNI; // ---------- SETTINGS ------------------ -bool PLogOnly = false; // Just log usage of Debug API -bool PEnabled = false; -bool AllowInject = false; // Allow to load inject DLL into target process. Else only processes with injected DLLs will be visible -bool AllowInjNew = false; -UINT InjFlags = InjLdr::mfInjMap|InjLdr::mfRunRMTH; +bool PLogOnly = false; // Just log usage of Debug API +bool PEnabled = false; // Enable the GhostDbg plugin +bool AllowInject = true; // Allow to load inject DLL into a target process(Attach). Else only processes with already injected GhostDbg DLLs will be visible +bool AllowInjNew = true; // Allow to load inject DLL into a target process(Create). +bool SuspendProc = true; // It is safer to keep a target process suspended while IPC and GhostDbg Client initializing but some timeouts may be detected +UINT InjFlags = InjLdr::mfInjMap|InjLdr::mfRunRMTH|InjLdr::mfRawRMTH; UINT WaitForInj = 3000; //--------------------------------------- @@ -44,6 +44,7 @@ PHOOK(ProcNtGetContextThread) HookNtGetContextThread; PHOOK(ProcNtSetContextThread) HookNtSetContextThread; PHOOK(ProcNtReadVirtualMemory) HookNtReadVirtualMemory; PHOOK(ProcNtWriteVirtualMemory) HookNtWriteVirtualMemory; +PHOOK(ProcNtTerminateProcess) HookNtTerminateProcess; PHOOK(ProcNtTerminateThread) HookNtTerminateThread; PHOOK(ProcNtSuspendThread) HookNtSuspendThread; PHOOK(ProcNtResumeThread) HookNtResumeThread; @@ -54,20 +55,26 @@ PHOOK(ProcDebugActiveProcessStop) HookDebugActiveProcessStop; PHOOK(ProcWaitForDebugEvent) HookWaitForDebugEvent; PHOOK(ProcContinueDebugEvent) HookContinueDebugEvent; PHOOK(ProcDebugBreakProcess) HookDebugBreakProcess; -PHOOK(ProcTerminateProcess) HookTerminateProcess; PHOOK(ProcCreateProcessA) HookCreateProcessA; PHOOK(ProcCreateProcessW) HookCreateProcessW; +PHOOK(ProcIsWow64Process) HookIsWow64Process; volatile bool BreakWrk = false; SHM::CMessageIPC* DbgIPC = NULL; XNI::CThreadList* ThList = NULL; +PVOID NtDllBase = NULL; +ULONG NtDllSize = 0; HMODULE hInst = NULL; -DWORD DbgProcID = 0; +HANDLE hSuspTh = NULL; +HANDLE hLstProc = NULL; +DWORD DbgProcID = 0; +DWORD LstProcID = 0; // After termination, to prevent reopen try and wait on unavailable IPC HWND hXDbgWnd = NULL; int PluginHandle = -1; int DbgCliMenu = -1; -bool HooksInstalled = false; +bool DoDisconnAtTerm = false; +bool HooksInstalled = false; bool DbgCliFlags[32]; // Because there is no function to request a menu item`s checked state in X64DBG :( @@ -105,7 +112,7 @@ BOOL APIENTRY DLLMain(HMODULE hModule, DWORD ReasonCall, LPVOID lpReserved) LoadConfiguration(); if(LogMode & lmCons){AllocConsole();/*SetWinConsoleSizes(1000, 500, 1000, 500);*/} - LOGMSG("Starting up... (Time=%08X), Owner='%ls'", (DWORD)GetTime64(),&StartUpDir); + LOGMSG("Starting up... (Time=%016llX), Owner='%ls'", SysTimeToTime64(NNTDLL::GetSystemTime()), &StartUpDir); TrimFilePath(StartUpDir); LOGMSG("WorkFolder: %ls", &WorkFolder); LOGMSG("StartUpDir: %ls", &StartUpDir); @@ -129,9 +136,16 @@ void _stdcall LoadConfiguration(void) PLogOnly = INIRefreshValueInt(CFGSECNAME, L"PLogOnly", PLogOnly, CfgFilePath); PEnabled = INIRefreshValueInt(CFGSECNAME, L"PEnabled", PEnabled, CfgFilePath); AllowInject = INIRefreshValueInt(CFGSECNAME, L"AllowInject", AllowInject, CfgFilePath); - AllowInjNew = INIRefreshValueInt(CFGSECNAME, L"AllowInjNew", AllowInjNew, CfgFilePath); + AllowInjNew = INIRefreshValueInt(CFGSECNAME, L"AllowInjNew", AllowInjNew, CfgFilePath); + SuspendProc = INIRefreshValueInt(CFGSECNAME, L"SuspendProc", SuspendProc, CfgFilePath); InjFlags = INIRefreshValueInt(CFGSECNAME, L"InjectFlags", InjFlags, CfgFilePath); WaitForInj = INIRefreshValueInt(CFGSECNAME, L"WaitForInj", WaitForInj, CfgFilePath); + + PVOID pNtDll = GetNtDllBaseFast(); + NtDllSize = GetRealModuleSize(pNtDll); + NtDllBase = VirtualAlloc(NULL,NtDllSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE); + memcpy(NtDllBase,pNtDll,NtDllSize); + LOGMSG("Clean copy of NtDll: %p", NtDllBase); } //------------------------------------------------------------------------------------ void _stdcall SaveConfiguration(void) @@ -139,8 +153,9 @@ void _stdcall SaveConfiguration(void) INISetValueInt(CFGSECNAME, L"LogMode", LogMode, CfgFilePath); INISetValueInt(CFGSECNAME, L"PLogOnly", PLogOnly, CfgFilePath); INISetValueInt(CFGSECNAME, L"PEnabled", PEnabled, CfgFilePath); - INISetValueInt(CFGSECNAME, L"AllowInject", AllowInject, CfgFilePath); + INISetValueInt(CFGSECNAME, L"AllowInject", AllowInject, CfgFilePath); INISetValueInt(CFGSECNAME, L"AllowInjNew", AllowInjNew, CfgFilePath); + INISetValueInt(CFGSECNAME, L"SuspendProc", SuspendProc, CfgFilePath); INISetValueInt(CFGSECNAME, L"InjectFlags", InjFlags, CfgFilePath); INISetValueInt(CFGSECNAME, L"WaitForInj", WaitForInj, CfgFilePath); } @@ -170,13 +185,32 @@ void _cdecl MenuHandler(CBTYPE Type, PLUG_CB_MENUENTRY *info) AllowInjNew = !AllowInjNew; SaveConfiguration(); break; + case MENU_ID_SUSPPROCESS: + SuspendProc = !SuspendProc; + SaveConfiguration(); + break; + case MENU_ID_USERAWTHREADS: + if(InjFlags & InjLdr::mfRawRMTH)InjFlags &= ~InjLdr::mfRawRMTH; + else InjFlags |= InjLdr::mfRawRMTH; + SaveConfiguration(); + break; case MENU_ID_ABOUT: { char Hdr[64]; // XDBGPLG_BUILD char About[128]; - wsprintfA(Hdr,"%s v%u.0",XDBGPLG_NAME,XDBGPLG_VER); + MSGBOXPARAMSA mpar; + wsprintfA(Hdr,"%s v%u.%u",XDBGPLG_NAME,XDBGPLG_VERH,XDBGPLG_VERL); wsprintfA(About,"Build: %s\nAuthor: Vicshann\nEmail: vicshann@gmail.com",XDBGPLG_BUILD); - MessageBoxA(hXDbgWnd, About, Hdr, MB_OK | MB_ICONINFORMATION); + memset(&mpar,0,sizeof(mpar)); + mpar.cbSize = sizeof(mpar); + mpar.hInstance = hInst; + mpar.hwndOwner = hXDbgWnd; + mpar.dwStyle = MB_OK | MB_USERICON; + mpar.dwLanguageId = MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL); + mpar.lpszIcon = MAKEINTRESOURCE(5); // Why can`t it accept the "LOGOICON" name? + mpar.lpszText = About; + mpar.lpszCaption = Hdr; + MessageBoxIndirectA(&mpar); } break; } @@ -191,7 +225,7 @@ extern "C" __declspec(dllexport) bool _cdecl pluginit(PLUG_INITSTRUCT* initStruc #else HMODULE pXDbgLib = GetModuleHandleA("x32dbg.dll"); #endif - + *(FARPROC*)&plugin_registercallback = GetProcAddress(pXDbgLib, "_plugin_registercallback"); *(FARPROC*)&plugin_menuentrysetchecked = GetProcAddress(pXDbgLib, "_plugin_menuentrysetchecked"); // *(FARPROC*)&plugin_menuentrysetname = GetProcAddress(pXDbgLib, "_plugin_menuentrysetname"); @@ -200,10 +234,10 @@ extern "C" __declspec(dllexport) bool _cdecl pluginit(PLUG_INITSTRUCT* initStruc *(FARPROC*)&plugin_menuseticon = GetProcAddress(pXDbgLib, "_plugin_menuseticon"); *(FARPROC*)&plugin_menuclear = GetProcAddress(pXDbgLib, "_plugin_menuclear"); *(FARPROC*)&plugin_menuadd = GetProcAddress(pXDbgLib, "_plugin_menuadd"); - *(FARPROC*)&plugin_logprintf = GetProcAddress(pXDbgLib, "_plugin_logprintf"); - + *(FARPROC*)&plugin_logprintf = GetProcAddress(pXDbgLib, "_plugin_logprintf"); + PluginHandle = initStruct->pluginHandle; - initStruct->pluginVersion = XDBGPLG_VER; + initStruct->pluginVersion = XDBGPLG_VERH; lstrcpyA(initStruct->pluginName, XDBGPLG_NAME); initStruct->sdkVersion = PLUG_SDKVERSION; plugin_registercallback(initStruct->pluginHandle, CB_MENUENTRY, (CBPLUGIN)MenuHandler); @@ -227,7 +261,10 @@ extern "C" __declspec(dllexport) void _cdecl plugsetup(PLUG_SETUPSTRUCT* setupSt plugin_menuaddentry(setupStruct->hMenu, MENU_ID_ENABLED, "Enabled"); plugin_menuaddentry(setupStruct->hMenu, MENU_ID_CHK_CANINJ, "Allow Injection"); // Open Process plugin_menuaddentry(setupStruct->hMenu, MENU_ID_CHK_CANINJNEW, "Allow Inject New"); // Create Process -// plugin_menuaddentry(setupStruct->hMenu, MENU_ID_CHK_CANINJ, "Allow Ejection"); // On Detach, if DLL ha been injected +// plugin_menuaddentry(setupStruct->hMenu, MENU_ID_CHK_CANEJ, "Allow Ejection"); // On Detach, if DLL has been injected + plugin_menuaddentry(setupStruct->hMenu, MENU_ID_SUSPPROCESS, "Suspend Process"); + plugin_menuaddentry(setupStruct->hMenu, MENU_ID_USERAWTHREADS, "Use Raw Threads"); + plugin_menuaddseparator(setupStruct->hMenu); DbgCliMenu = plugin_menuadd(setupStruct->hMenu, "Client Config"); plugin_menuaddseparator(setupStruct->hMenu); @@ -236,6 +273,8 @@ extern "C" __declspec(dllexport) void _cdecl plugsetup(PLUG_SETUPSTRUCT* setupSt plugin_menuentrysetchecked(PluginHandle,MENU_ID_ENABLED,PEnabled); plugin_menuentrysetchecked(PluginHandle,MENU_ID_CHK_CANINJ,AllowInject); plugin_menuentrysetchecked(PluginHandle,MENU_ID_CHK_CANINJNEW,AllowInjNew); + plugin_menuentrysetchecked(PluginHandle,MENU_ID_SUSPPROCESS,SuspendProc); + plugin_menuentrysetchecked(PluginHandle,MENU_ID_USERAWTHREADS,InjFlags & InjLdr::mfRawRMTH); ICONDATA ico; UINT ResSize = 0; @@ -252,7 +291,7 @@ int _stdcall EnablePlugin(void) DBGMSG("Enter"); BreakWrk = false; HooksInstalled = true; - + HookNtClose.SetHook("NtClose","ntdll.dll"); HookNtOpenThread.SetHook("NtOpenThread","ntdll.dll"); HookNtOpenProcess.SetHook("NtOpenProcess","ntdll.dll"); @@ -261,6 +300,8 @@ int _stdcall EnablePlugin(void) HookNtProtectVirtualMemory.SetHook("NtProtectVirtualMemory","ntdll.dll"); HookNtQueryInformationThread.SetHook("NtQueryInformationThread","ntdll.dll"); HookNtQueryInformationProcess.SetHook("NtQueryInformationProcess","ntdll.dll"); + + HookNtTerminateProcess.SetHook("NtTerminateProcess","ntdll.dll"); HookNtTerminateThread.SetHook("NtTerminateThread","ntdll.dll"); HookNtSuspendThread.SetHook("NtSuspendThread","ntdll.dll"); HookNtResumeThread.SetHook("NtResumeThread","ntdll.dll"); @@ -275,9 +316,9 @@ int _stdcall EnablePlugin(void) HookContinueDebugEvent.SetHook("ContinueDebugEvent","kernel32.dll"); HookWaitForDebugEvent.SetHook("WaitForDebugEvent","kernel32.dll"); HookDebugBreakProcess.SetHook("DebugBreakProcess","kernel32.dll"); - HookTerminateProcess.SetHook("TerminateProcess","kernel32.dll"); HookCreateProcessA.SetHook("CreateProcessA","kernel32.dll"); HookCreateProcessW.SetHook("CreateProcessW","kernel32.dll"); + HookIsWow64Process.SetHook("IsWow64Process","kernel32.dll"); ThList = new XNI::CThreadList; DbgIPC = new SHM::CMessageIPC; @@ -299,7 +340,8 @@ int _stdcall DisablePlugin(void) HookNtQueryVirtualMemory.Remove(); HookNtProtectVirtualMemory.Remove(); HookNtQueryInformationThread.Remove(); - HookNtQueryInformationProcess.Remove(); + HookNtQueryInformationProcess.Remove(); + HookNtTerminateProcess.Remove(); HookNtTerminateThread.Remove(); HookNtSuspendThread.Remove(); HookNtResumeThread.Remove(); @@ -314,7 +356,6 @@ int _stdcall DisablePlugin(void) HookContinueDebugEvent.Remove(); HookWaitForDebugEvent.Remove(); HookDebugBreakProcess.Remove(); - HookTerminateProcess.Remove(); HookCreateProcessA.Remove(); HookCreateProcessW.Remove(); @@ -336,7 +377,7 @@ int _stdcall LoadDbgClienConfig(void) SHM::CArgPack<> apo; DBGMSG("Enter"); plugin_menuclear(DbgCliMenu); - if(DbgIPC->ExchangeMsg(XNI::miDbgGetConfigs,XNI::mtUsrReq, &api, &apo) < 0)return -1; + if(DbgIPC->ExchangeMsg(XNI::miDbgGetConfigs,XNI::mtUsrReq, &api, &apo) < 0){DBGMSG("miDbgGetConfigs failed!"); return -1;} for(;;) { char Name[128]; @@ -354,6 +395,7 @@ int _stdcall LoadDbgClienConfig(void) } else if(Type & XNI::dtNull)plugin_menuaddseparator(DbgCliMenu); } + DBGMSG("Done"); return 0; } //------------------------------------------------------------------------------------ @@ -396,39 +438,77 @@ int _stdcall InjectProcess(HANDLE hProcess, DWORD ProcessID) else InjLib = GetResource(hInst, "InjLib", RT_RCDATA, &ResSize); if(!InjLib || !ResSize){DBGMSG("No InjLib found!"); return -1;} bool POpened = (hProcess == NULL); - if(POpened)hProcess = InjLdr::OpenRemoteProcess(ProcessID, Flags); - if(!hProcess)return -2; - int res = InjLdr::InjModuleIntoProcessAndExec(hProcess, InjLib, ResSize, Flags); - if(POpened)CloseHandle(hProcess); - if(res < 0)return -3; + if(POpened)hProcess = InjLdr::OpenRemoteProcess(ProcessID, Flags, SuspendProc); + if(!hProcess)return -2; + if(SuspendProc) + { + if(!POpened) + { + DuplicateHandle(NtCurrentProcess, hProcess, NtCurrentProcess, &hLstProc, 0, 0, DUPLICATE_SAME_ACCESS); + NTSTATUS stat = NtSuspendProcess(hLstProc); + DBGMSG("Entire process been suspended: Status=%08X, DupHandle=%p",stat,hLstProc); + } + else hLstProc = hProcess; + } + if(!POpened && NNTDLL::IsWinXPOrOlder())Flags &= ~InjLdr::mfRawRMTH; // On Windows XP this Csr unfriendly thread will catch a process initialization APC! // A DebugApi remote thread will also catch this APC and DebugApi threas is also not registered with Csr // On latest Win10 raw threads can`t be injected in notepad.exe (Access Denied) + int res = InjLdr::InjModuleIntoProcessAndExec(hProcess, InjLib, ResSize, Flags|InjLdr::mfResSyscall, 3, NULL, NULL, NtDllBase, 0x10000); // mfResSyscall is required to avoid self interception // Only .text(Data merged), .bss and .rdata + if(POpened && !SuspendProc)CloseHandle(hProcess); // Close after OpenRemoteProcess + if(res < 0){DBGMSG("InjModuleIntoProcessAndExec failed with %i",res); if(SuspendProc)NtResumeProcess(hProcess); return -3;} // Cannot terminate without a specific permission for(int ctr=WaitForInj;ctr > 0;ctr-=100) { Sleep(100); if(SHM::CMessageIPC::IsExistForID(ProcessID))break; } - if(!SHM::CMessageIPC::IsExistForID(ProcessID))return FALSE; + if(!SHM::CMessageIPC::IsExistForID(ProcessID))return -4; return 0; } //------------------------------------------------------------------------------------ +// Main threas is suspended, but some additional threads are not +// BOOL _stdcall ProcessCreateInjWrk(LPPROCESS_INFORMATION lpProcessInformation) -{ - int ires = InjectProcess(lpProcessInformation->hProcess, lpProcessInformation->dwProcessId); - CloseHandle(lpProcessInformation->hThread); - CloseHandle(lpProcessInformation->hProcess); - if(ires < 0){DBGMSG("Failed to inject a new process: %08X(%u)",lpProcessInformation->dwProcessId,lpProcessInformation->dwProcessId); return FALSE;} - if(lpProcessInformation->dwProcessId != DbgIPC->GetID())DbgIPC->Disconnect(); // Has been connected to some other process - if(DbgIPC->Connect((DWORD)lpProcessInformation->dwProcessId, 0, true) < 0){DBGMSG("Connect failed: %08X(%u)",lpProcessInformation->dwProcessId,lpProcessInformation->dwProcessId); return FALSE;} - DbgProcID = lpProcessInformation->dwProcessId; +{ + BOOL res = FALSE; + HANDLE hRealThrd = lpProcessInformation->hThread; + HANDLE hRealProc = lpProcessInformation->hProcess; + hSuspTh = NULL; lpProcessInformation->hProcess = XNI::CDbgClient::UintToFakeHandle(lpProcessInformation->dwProcessId); // All communication with a target process is only through IPC - lpProcessInformation->hThread = NULL; // TODO: Get it from target`s DbgClient - SHM::CArgPack<> api; - SHM::CArgPack<> apo; - api.PushArg(lpProcessInformation->dwProcessId); - if(DbgIPC->ExchangeMsg(XNI::miDebugActiveProcess,XNI::mtDbgReq, &api, &apo) < 0)return FALSE; - BOOL res = FALSE; - apo.PopArg(res); - LoadDbgClienConfig(); - plugin_logprintf("Main thread is in suspended state\n"); + lpProcessInformation->hThread = XNI::CDbgClient::UintToFakeHandle(0); // Any first thread in list! + int ires = InjectProcess(hRealProc, lpProcessInformation->dwProcessId); + if(ires >= 0) + { + if(lpProcessInformation->dwProcessId != DbgIPC->GetID())DbgIPC->Disconnect(); // Has been connected to some other process + if(DbgIPC->Connect((DWORD)lpProcessInformation->dwProcessId, 0, true) >= 0) + { + ThList->Clear(); + LstProcID = DbgProcID = lpProcessInformation->dwProcessId; // Required for ProcWaitForDebugEvent + DBGMSG("hProcess=%08X, hThread=%08X, ProcessId=%u, ThreadId=%u",lpProcessInformation->hProcess,lpProcessInformation->hThread,lpProcessInformation->dwProcessId,lpProcessInformation->dwThreadId); + SHM::CArgPack<> api; + SHM::CArgPack<> apo; + api.PushArg(lpProcessInformation->dwProcessId); + if(DbgIPC->ExchangeMsg(XNI::miDebugActiveProcess,XNI::mtDbgReq, &api, &apo) >= 0) // Do an usual attach but to a suspended process (Should have a single worker thread) + { + DWORD MainThID = 0; + HANDLE hPHandle = NULL; + HANDLE hTHandle = NULL; + PTEB MainThTeb = NULL; + apo.PopArg(res); + apo.PopArg(MainThID); + apo.PopArg(MainThTeb); + apo.PopArg(hTHandle); + apo.PopArg(hPHandle); + lpProcessInformation->hProcess = hPHandle; + lpProcessInformation->hThread = hTHandle; + ThList->AddThreadToList(MainThTeb, MainThID, hTHandle, false); // NOTE: Watch for CloseHandle for thread handles + LoadDbgClienConfig(); + if(res)hSuspTh = hRealThrd; +/////////////// plugin_logprintf("Main thread is in suspended state\n"); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + } + } + else {DBGMSG("Connect failed: %08X(%u)",lpProcessInformation->dwProcessId,lpProcessInformation->dwProcessId);} + } + else {DBGMSG("Failed to inject a new process: %08X(%u): %i",lpProcessInformation->dwProcessId,lpProcessInformation->dwProcessId,ires);} + if(!res){TerminateProcess(hRealProc,0); CloseHandle(hRealThrd);} + CloseHandle(hRealProc); return res; } //------------------------------------------------------------------------------------ @@ -443,6 +523,14 @@ BOOL _stdcall ProcessCreateInjWrk(LPPROCESS_INFORMATION lpProcessInformation) // NTSTATUS NTAPI ProcNtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId) { + DBGMSG("Opening: DbgProcID=%08X, LstProcID=%08X, ProcessID=%08X",DbgProcID,LstProcID,ClientId->UniqueProcess); + if(LstProcID == (ULONG)ClientId->UniqueProcess) // x64Dbg will try multiple times! + { + if(!DbgProcID){DBGMSG("Reopening prevented!"); return STATUS_ACCESS_DENIED;} + *ProcessHandle = XNI::CDbgClient::UintToFakeHandle((ULONG)ClientId->UniqueProcess); + DBGMSG("Reopening emulated!"); // "Reopening allowed!" + return STATUS_SUCCESS; // HookNtOpenProcess.OrigProc(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);; + } bool HaveMapping = SHM::CMessageIPC::IsExistForID((DWORD)ClientId->UniqueProcess); if(PLogOnly || !ClientId || !ProcessHandle || (!HaveMapping && AllowInject)) // Injection(optional) moved to DebugActiveProcess because opening a process is done after DebugActiveProcess { @@ -454,7 +542,8 @@ NTSTATUS NTAPI ProcNtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAcces if(!HaveMapping)return STATUS_UNSUCCESSFUL; // Debugger will try to open every process. Do not show processes without already injected DLLs if((DWORD)ClientId->UniqueProcess != DbgIPC->GetID())DbgIPC->Disconnect(); // Has been connected to some other process if(DbgIPC->Connect((DWORD)ClientId->UniqueProcess, 0, true) < 0){DBGMSG("Connect failed: %08X(%u)",ClientId->UniqueProcess,ClientId->UniqueProcess); return STATUS_UNSUCCESSFUL;} - DbgProcID = (DWORD)ClientId->UniqueProcess; + LstProcID = DbgProcID = (DWORD)ClientId->UniqueProcess; + ThList->Clear(); *ProcessHandle = XNI::CDbgClient::UintToFakeHandle((DWORD)ClientId->UniqueProcess); // All communication with a target process is only through IPC DBGMSG("FakeProcessHandle=%08X, DesiredAccess=%08X, UniqueProcess=%08X, UniqueThread=%08X",*ProcessHandle,DesiredAccess,ClientId->UniqueProcess,ClientId->UniqueThread); return STATUS_SUCCESS; @@ -463,11 +552,13 @@ NTSTATUS NTAPI ProcNtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAcces NTSTATUS NTAPI ProcNtOpenThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId) { HANDLE hTh = NULL; - if(PLogOnly || !DbgProcID || !ClientId || !ThreadHandle || !(hTh = ThList->GetHandleByIndex(ThList->FindThreadIdxInList(NULL, (DWORD)ClientId->UniqueThread, NULL)))) + DBGMSG("Opening: DbgProcID=%08X, ProcessID=%08X, ThreadID=%08X",DbgProcID,ClientId->UniqueProcess,ClientId->UniqueThread); + bool State = PLogOnly || !DbgProcID || !ClientId || !ThreadHandle; + if(State || !(hTh = ThList->GetHandleByIndex(ThList->FindThreadIdxInList(NULL, (DWORD)ClientId->UniqueThread, NULL)))) // TODO: Need a way to know main thread`s index(In GhostDbg list) of a new created process. It may be requested before CREATE_PROCESS_DEBUG_EVENT { + if(State && ClientId->UniqueThread && (ThList->FindThreadIdxInList(NULL, (DWORD)ClientId->UniqueThread, NULL) >= 0)){DBGMSG("Opening prevented!"); return STATUS_ACCESS_DENIED;} // x64Dbg will try multiple times! NTSTATUS Res = HookNtOpenThread.OrigProc(ThreadHandle, DesiredAccess, ObjectAttributes, ClientId); - HANDLE Hnd = (ThreadHandle)?(*ThreadHandle):(INVALID_HANDLE_VALUE); - DBGMSG("Status=%08X, ThreadHandle=%08X, DesiredAccess=%08X, UniqueProcess=%08X, UniqueThread=%08X",Res,Hnd,DesiredAccess,(ClientId)?(ClientId->UniqueProcess):(0),(ClientId)?(ClientId->UniqueThread):(0)); + DBGMSG("Status=%08X, ThreadHandle=%08X, DesiredAccess=%08X, UniqueProcess=%08X, UniqueThread=%08X",Res,((ThreadHandle)?(*ThreadHandle):(INVALID_HANDLE_VALUE)),DesiredAccess,(ClientId)?(ClientId->UniqueProcess):(0),(ClientId)?(ClientId->UniqueThread):(0)); return Res; } *ThreadHandle = hTh; @@ -498,33 +589,44 @@ NTSTATUS NTAPI ProcNtClose(HANDLE Handle) //------------------------------------------------------------------------------------ BOOL WINAPI ProcCreateProcessA(LPCSTR lpApplicationName,LPSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCSTR lpCurrentDirectory,LPSTARTUPINFOA lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation) { - DBGMSG("dwCreationFlags=%08X, lpApplicationName='%s',lpCommandLine='%s',",dwCreationFlags,lpApplicationName?lpApplicationName:"",lpCommandLine?lpCommandLine:""); + DBGMSG("dwCreationFlags=%08X, lpApplicationName='%s',lpCommandLine='%s'",dwCreationFlags,lpApplicationName?lpApplicationName:"",lpCommandLine?lpCommandLine:""); bool ForDebug = AllowInjNew && (dwCreationFlags & (DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS)); if(ForDebug) { dwCreationFlags &= ~(DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS); - dwCreationFlags |= CREATE_SUSPENDED; + dwCreationFlags |= CREATE_SUSPENDED; // Instead of DEBUG_PROCESS } BOOL res = HookCreateProcessA.OrigProc(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); + DBGMSG("ProcessId=%u, ThreadId=%u",lpProcessInformation->dwProcessId,lpProcessInformation->dwThreadId); if(res && ForDebug && lpProcessInformation)res = ProcessCreateInjWrk(lpProcessInformation); return res; } //------------------------------------------------------------------------------------ BOOL WINAPI ProcCreateProcessW(LPCWSTR lpApplicationName,LPWSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation) { - DBGMSG("dwCreationFlags=%08X, lpApplicationName='%ls',lpCommandLine='%ls',",dwCreationFlags,lpApplicationName?lpApplicationName:L"",lpCommandLine?lpCommandLine:L""); + DBGMSG("dwCreationFlags=%08X, lpApplicationName='%ls',lpCommandLine='%ls'",dwCreationFlags,lpApplicationName?lpApplicationName:L"",lpCommandLine?lpCommandLine:L""); bool ForDebug = AllowInjNew && (dwCreationFlags & (DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS)); if(ForDebug) { dwCreationFlags &= ~(DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS); - dwCreationFlags |= CREATE_SUSPENDED; + dwCreationFlags |= CREATE_SUSPENDED; // Instead of DEBUG_PROCESS } BOOL res = HookCreateProcessW.OrigProc(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); + DBGMSG("ProcessId=%u, ThreadId=%u",lpProcessInformation->dwProcessId,lpProcessInformation->dwThreadId); if(res && ForDebug && lpProcessInformation)res = ProcessCreateInjWrk(lpProcessInformation); return res; } //------------------------------------------------------------------------------------ - +BOOL WINAPI ProcIsWow64Process(HANDLE hProcess, PBOOL Wow64Process) // In Windows 10 (2019) it passes process` handle to NtQuerySystemInformationEx +{ + PVOID Value = NULL; + ULONG RetLen = 0; + NTSTATUS Status = NtQueryInformationProcess(hProcess, ProcessWow64Information, &Value, sizeof(Value), &RetLen); + if(Status < 0)RtlSetLastWin32Error(RtlNtStatusToDosError(Status)); + else if(Wow64Process)*Wow64Process = (bool)Value; + DBGMSG("Status=%08X, hProcess=%p, Value=%u",Status,hProcess,(UINT)Value); + return (Status >= 0); +} //==================================================================================== // @@ -538,6 +640,7 @@ BOOL WINAPI ProcWaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMillisecon DBGMSG("lpDebugEvent=%p, dwMilliseconds=%u",lpDebugEvent,dwMilliseconds); if(PLogOnly || !DbgProcID) { + DBGMSG("PLogOnly=%u, DbgProcID=%u",PLogOnly,DbgProcID); BOOL res = HookWaitForDebugEvent.OrigProc(lpDebugEvent, dwMilliseconds); if(res){DBGMSG("dwDebugEventCode=%08X, dwProcessId=%08X, dwThreadId=%08X, BASE: %p",lpDebugEvent->dwDebugEventCode,lpDebugEvent->dwProcessId,lpDebugEvent->dwThreadId,lpDebugEvent->u.LoadDll.lpBaseOfDll);} return res; @@ -554,7 +657,7 @@ BOOL WINAPI ProcWaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMillisecon { case EXCEPTION_DEBUG_EVENT: { - // Nothing to do for now + DBGMSG("EXCEPTION_DEBUG_EVENT: dwThreadId=%u, Code=%08X, Addr=%p",Evt->dwThreadId,Evt->u.Exception.ExceptionRecord.ExceptionCode, Evt->u.Exception.ExceptionRecord.ExceptionAddress); } break; case CREATE_THREAD_DEBUG_EVENT: @@ -567,6 +670,8 @@ BOOL WINAPI ProcWaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMillisecon { DBGMSG("CREATE_PROCESS_DEBUG_EVENT: hProcess=%08X, hThread=%08X, dwThreadId=%u",Evt->u.CreateProcessInfo.hProcess,Evt->u.CreateProcessInfo.hThread,Evt->dwThreadId); ThList->AddThreadToList((TEB*)Evt->u.CreateThread.lpThreadLocalBase, Evt->dwThreadId, Evt->u.CreateProcessInfo.hThread, false); // This fake handle is an encoded index in client`s thread list + if(hSuspTh){ResumeThread(hSuspTh); CloseHandle(hSuspTh); hSuspTh = NULL; DBGMSG("Main thread has been resumed");} // Was created as suspended // TODO: Optionally left main thread suspended if a new process started? + if(SuspendProc && hLstProc){NTSTATUS stat = NtResumeProcess(hLstProc); CloseHandle(hLstProc); DBGMSG("Entire process been resumed: Status=%08X, Handle=%p",stat,hLstProc); hLstProc=NULL;} // GhostDbg initialization is finished } break; case EXIT_THREAD_DEBUG_EVENT: @@ -578,8 +683,7 @@ BOOL WINAPI ProcWaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMillisecon case EXIT_PROCESS_DEBUG_EVENT: { DBGMSG("EXIT_PROCESS_DEBUG_EVENT: dwProcessId=%u, dwExitCode=%u",Evt->dwProcessId,Evt->u.ExitProcess.dwExitCode); - if(DbgProcID == Evt->dwProcessId)DbgProcID = 0; - if(DbgIPC)DbgIPC->Disconnect(); + if(DbgProcID == Evt->dwProcessId)DoDisconnAtTerm = true; } break; case LOAD_DLL_DEBUG_EVENT: @@ -606,9 +710,11 @@ BOOL WINAPI ProcWaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMillisecon default: {DBGMSG("Unknown DebugEventCode: %u",Evt->dwDebugEventCode);} } memcpy(lpDebugEvent,&Cmd->Data,sizeof(DEBUG_EVENT)); - DbgIPC->EndMsg(false); // Unlock shared buffer // Only single events can be removed because they read in order of arrival // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + DbgIPC->EndMsg(false); // Unlock shared buffer // Only single events can be removed because they read in order of arrival // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + DBGMSG("CONTINUE"); return TRUE; // Other events will wait untiiiil another call to WaitForDebugEvent } + DBGMSG("FINISH"); DbgProcID = 0; // No DebugActiveProcessStop is called when WaitForDebugEvent is failed DbgIPC->EndMsg(false); // Unlock shared buffer if it is still locked // <<<<<<<<<<<<<<<<<<< FALSE for now! DbgIPC->Disconnect(); @@ -625,18 +731,28 @@ BOOL WINAPI ProcDebugActiveProcess(DWORD dwProcessId) // Thes will make a de { if(AllowInject) { - if(InjectProcess(NULL, dwProcessId) < 0){DBGMSG("Failed to inject!"); return FALSE;} + if(int res = InjectProcess(NULL, dwProcessId); res < 0){DBGMSG("Failed to inject: %i", res); return FALSE;} } else return FALSE; } - if(DbgIPC->Connect(dwProcessId, 0, true) < 0){DBGMSG("Failed to connect!"); return FALSE;} - DbgProcID = dwProcessId; + if(int res = DbgIPC->Connect(dwProcessId, 0, true); res < 0){DBGMSG("Failed to connect: %i", res); return FALSE;} + LstProcID = DbgProcID = dwProcessId; + ThList->Clear(); SHM::CArgPack<> api; SHM::CArgPack<> apo; api.PushArg(dwProcessId); if(DbgIPC->ExchangeMsg(XNI::miDebugActiveProcess,XNI::mtDbgReq, &api, &apo) < 0){DBGMSG("Failed to communicate!"); return FALSE;} BOOL res = FALSE; + DWORD MainThID = 0; + HANDLE hPHandle = NULL; + HANDLE hTHandle = NULL; + PTEB MainThTeb = NULL; apo.PopArg(res); + apo.PopArg(MainThID); + apo.PopArg(MainThTeb); + apo.PopArg(hTHandle); + apo.PopArg(hPHandle); + ThList->AddThreadToList(MainThTeb, MainThID, hTHandle, false); // NOTE: Watch for CloseHandle for thread handles LoadDbgClienConfig(); return res; } @@ -645,7 +761,7 @@ BOOL WINAPI ProcDebugActiveProcessStop(DWORD dwProcessId) // Doesn`t called a { DBGMSG("dwProcessId=%08X",dwProcessId); if(PLogOnly || !DbgIPC || !DbgProcID)return HookDebugActiveProcessStop.OrigProc(dwProcessId); - if(DbgProcID == dwProcessId)DbgProcID = 0; + if(DbgProcID == dwProcessId)LstProcID = DbgProcID = 0; // Allow reopening of a target process SHM::CArgPack<> api; api.PushArg(dwProcessId); int res = DbgIPC->ExchangeMsg(XNI::miDebugActiveProcessStop,XNI::mtDbgReq, &api, NULL); @@ -656,16 +772,28 @@ BOOL WINAPI ProcDebugActiveProcessStop(DWORD dwProcessId) // Doesn`t called a //------------------------------------------------------------------------------------ BOOL WINAPI ProcContinueDebugEvent(DWORD dwProcessId, DWORD dwThreadId, DWORD dwContinueStatus) { - DBGMSG("dwProcessId=%08X, dwThreadId=%08X, dwContinueStatus=%08X",dwProcessId,dwThreadId,dwContinueStatus); - if(PLogOnly || !DbgIPC || !DbgProcID)return HookContinueDebugEvent.OrigProc(dwProcessId, dwThreadId, dwContinueStatus); + DBGMSG("dwProcessId=%08X, dwThreadId=%u, dwContinueStatus=%08X",dwProcessId,dwThreadId,dwContinueStatus); + if(PLogOnly || !DbgIPC || !DbgProcID)return HookContinueDebugEvent.OrigProc(dwProcessId, dwThreadId, dwContinueStatus); SHM::CArgPack<> api; SHM::CArgPack<> apo; api.PushArg(dwContinueStatus); api.PushArg(dwThreadId); api.PushArg(dwProcessId); + if(DoDisconnAtTerm) // Need to wait here to other threads which read Memory/ThreadInfo in a loop or termination will be slow + { + DBGMSG("Waiting for other threads to stop accessing the finished process..."); + Sleep(1000); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + DBGMSG("Continuing target process termination"); + } if(DbgIPC->ExchangeMsg(XNI::miContinueDebugEvent,XNI::mtDbgReq, &api, &apo) < 0)return FALSE; BOOL res = FALSE; apo.PopArg(res); + if(DoDisconnAtTerm && DbgIPC) + { + DoDisconnAtTerm = false; + DbgProcID = 0; + DbgIPC->Disconnect(); + } return res; } //------------------------------------------------------------------------------------ @@ -682,17 +810,19 @@ BOOL WINAPI ProcDebugBreakProcess(HANDLE hProcess) // Does ntdll.RtlpCreateU return res; } //------------------------------------------------------------------------------------ -BOOL WINAPI ProcTerminateProcess(HANDLE hProcess, UINT uExitCode) +NTSTATUS NTAPI ProcNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus) { - DBGMSG("hProcess=%p, uExitCode=%08X",hProcess,uExitCode); - if(PLogOnly || !DbgIPC || !XNI::CDbgClient::IsFakeHandle(hProcess))return HookTerminateProcess.OrigProc(hProcess, uExitCode); + DBGMSG("ProcessHandle=%p, ExitStatus=%08X",ProcessHandle,ExitStatus); + if(PLogOnly || !DbgIPC || !XNI::CDbgClient::IsFakeHandle(ProcessHandle))return HookNtTerminateProcess.OrigProc(ProcessHandle, ExitStatus); SHM::CArgPack<> api; SHM::CArgPack<> apo; - api.PushArg(uExitCode); - api.PushArg(hProcess); + api.PushArg(ExitStatus); + api.PushArg(ProcessHandle); if(DbgIPC->ExchangeMsg(XNI::miTerminateProcess,XNI::mtDbgReq, &api, &apo) < 0)return FALSE; - BOOL res = FALSE; + NTSTATUS res = STATUS_UNSUCCESSFUL; apo.PopArg(res); + if(!res)DbgIPC->Disconnect(); // x64Dbg is still owns the process handle and will try to request the process info with it! + DbgProcID = 0; // ???????????????????? return res; } //------------------------------------------------------------------------------------ @@ -702,20 +832,19 @@ NTSTATUS NTAPI ProcNtGetContextThread(HANDLE ThreadHandle, PCONTEXT Context) if(PLogOnly || !DbgIPC || !XNI::CDbgClient::IsFakeHandle(ThreadHandle) || !ThList->IsThreadInList(0,ThreadHandle))return HookNtGetContextThread.OrigProc(ThreadHandle, Context); NTSTATUS Status; SHM::CArgPack api; - SHM::CArgPack<> apo; + SHM::CArgPack apo; api.PushArg(*Context); // InOut api.PushArg(ThreadHandle); if(DbgIPC->ExchangeMsg(XNI::miGetThreadContext,XNI::mtDbgReq, &api, &apo) < 0)return STATUS_UNSUCCESSFUL; apo.PopArg(Status); apo.PopArg(*Context); - DBGMSG("RET: ThHndle=%p, Context=%p, DbgRegs=%u, DR7=%p, DR0=%p, DR1=%p, DR2=%p, DR3=%p",ThreadHandle,Context,bool(Context->ContextFlags & CONTEXT_DEBUG_REGISTERS), Context->Dr7,Context->Dr0,Context->Dr1,Context->Dr2,Context->Dr3); + DBGMSG("RET: ThHndle=%p, Context=%p, PC=%p, Trace=%u, DbgRegs=%u, DR7=%p, DR0=%p, DR1=%p, DR2=%p, DR3=%p",ThreadHandle,Context,XNI::CDbgClient::GetInstrPtr(Context),bool(Context->EFlags&0x0100),bool(Context->ContextFlags & CONTEXT_DEBUG_REGISTERS), Context->Dr7,Context->Dr0,Context->Dr1,Context->Dr2,Context->Dr3); return Status; } //------------------------------------------------------------------------------------ NTSTATUS NTAPI ProcNtSetContextThread(HANDLE ThreadHandle, PCONTEXT Context) { - DBGMSG("ThreadHandle=%p, Context=%p, DbgRegs=%u, DR7=%p, DR0=%p, DR1=%p, DR2=%p, DR3=%p",ThreadHandle,Context,bool(Context->ContextFlags & CONTEXT_DEBUG_REGISTERS), Context->Dr7,Context->Dr0,Context->Dr1,Context->Dr2,Context->Dr3); - // if(Context->Dr1)DebugBreak(); + DBGMSG("ThreadHandle=%p, Context=%p, PC=%p, Trace=%u, DbgRegs=%u, DR7=%p, DR0=%p, DR1=%p, DR2=%p, DR3=%p",ThreadHandle,Context,XNI::CDbgClient::GetInstrPtr(Context),bool(Context->EFlags&0x0100),bool(Context->ContextFlags & CONTEXT_DEBUG_REGISTERS), Context->Dr7,Context->Dr0,Context->Dr1,Context->Dr2,Context->Dr3); if(PLogOnly || !DbgIPC || !XNI::CDbgClient::IsFakeHandle(ThreadHandle) || !ThList->IsThreadInList(0,ThreadHandle))return HookNtSetContextThread.OrigProc(ThreadHandle, Context); NTSTATUS Status; SHM::CArgPack api; @@ -729,12 +858,12 @@ NTSTATUS NTAPI ProcNtSetContextThread(HANDLE ThreadHandle, PCONTEXT Context) //------------------------------------------------------------------------------------ NTSTATUS NTAPI ProcNtReadVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T BufferLength, PSIZE_T ReturnLength) { -//// DBGMSG("ProcessHandle=%p, BaseAddress=%p, Buffer=%p, BufferLength=%08X, ReturnLength=%p",ProcessHandle,BaseAddress,Buffer,BufferLength,ReturnLength); +// DBGMSG("ProcessHandle=%p, BaseAddress=%p, Buffer=%p, BufferLength=%08X, ReturnLength=%p",ProcessHandle,BaseAddress,Buffer,BufferLength,ReturnLength); if(PLogOnly || !DbgIPC || !XNI::CDbgClient::IsFakeHandle(ProcessHandle))return HookNtReadVirtualMemory.OrigProc(ProcessHandle, BaseAddress, Buffer, BufferLength, ReturnLength); SIZE_T RetLen = 0; NTSTATUS Status; SHM::CArgPack<> api; - SHM::CArgPack<> apo; + SHM::CArgPack<3072> apo; api.PushArg(BufferLength); api.PushArg(BaseAddress); api.PushArg(ProcessHandle); @@ -748,11 +877,11 @@ NTSTATUS NTAPI ProcNtReadVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, //------------------------------------------------------------------------------------ NTSTATUS NTAPI ProcNtWriteVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T BufferLength, PSIZE_T ReturnLength) { - DBGMSG("ProcessHandle=%p, BaseAddress=%p, Buffer=%p, BufferLength=%08X, ReturnLength=%p",ProcessHandle,BaseAddress,Buffer,BufferLength,ReturnLength); + DBGMSG("ProcessHandle=%p, BaseAddress=%p, Buffer=%p, BufferLength=%08X, BYTE=%02X, ReturnLength=%p",ProcessHandle,BaseAddress,Buffer,BufferLength,(Buffer)?(*(PBYTE)Buffer):0,ReturnLength); if(PLogOnly || !DbgIPC || !XNI::CDbgClient::IsFakeHandle(ProcessHandle))return HookNtWriteVirtualMemory.OrigProc(ProcessHandle, BaseAddress, Buffer, BufferLength, ReturnLength); SIZE_T RetLen = 0; NTSTATUS Status; - SHM::CArgPack<1024> api; + SHM::CArgPack<3072> api; SHM::CArgPack<> apo; api.PushBlk(BufferLength, Buffer); api.PushArg(BufferLength); @@ -817,6 +946,7 @@ NTSTATUS NTAPI ProcNtSuspendThread(HANDLE ThreadHandle, PULONG PreviousSuspendCo apo.PopArg(Status); apo.PopArg(SuspCtr); if(PreviousSuspendCount)*PreviousSuspendCount = SuspCtr; + DBGMSG("RET: ThreadHandle=%p, Status=%08X, SuspCtr=%u",ThreadHandle, Status, SuspCtr); return Status; } //------------------------------------------------------------------------------------ @@ -833,6 +963,7 @@ NTSTATUS NTAPI ProcNtResumeThread(HANDLE ThreadHandle, PULONG PreviousSuspendCou apo.PopArg(Status); apo.PopArg(SuspCtr); if(PreviousSuspendCount)*PreviousSuspendCount = SuspCtr; + DBGMSG("RET: ThreadHandle=%p, Status=%08X, SuspCtr=%u",ThreadHandle, Status, SuspCtr); return Status; } //------------------------------------------------------------------------------------ @@ -857,7 +988,7 @@ NTSTATUS NTAPI ProcNtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCL ULONG RetLen = 0; NTSTATUS Status; SHM::CArgPack<> api; - SHM::CArgPack<1024> apo; + SHM::CArgPack<3072> apo; api.PushArg(ProcessInformationLength); api.PushArg(ProcessInformationClass); api.PushArg(ProcessHandle); @@ -883,7 +1014,7 @@ NTSTATUS NTAPI ProcNtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCL default: DBGMSG("Untested information class: %u!",ProcessInformationClass); // DebugBreak(); - } + } return Status; } //------------------------------------------------------------------------------------ @@ -894,7 +1025,7 @@ NTSTATUS NTAPI ProcNtQueryInformationThread(HANDLE ThreadHandle, THREADINFOCLASS ULONG RetLen = 0; NTSTATUS Status; SHM::CArgPack<> api; - SHM::CArgPack<1024> apo; + SHM::CArgPack<3072> apo; api.PushArg(ThreadInformationLength); api.PushArg(ThreadInformationClass); api.PushArg(ThreadHandle); @@ -903,11 +1034,11 @@ NTSTATUS NTAPI ProcNtQueryInformationThread(HANDLE ThreadHandle, THREADINFOCLASS apo.PopArg(RetLen); apo.PopBlk(ThreadInformationLength,ThreadInformation); // Full buffer exchange if(ReturnLength)*ReturnLength = RetLen; - if(Status || !RetLen){DBGMSG("Failed with status: %08X!",Status); return Status;} + if(Status || !RetLen){DBGMSG("Failed %u with status: %08X!",ThreadInformationClass,Status); return Status;} switch(ThreadInformationClass) { case ThreadBasicInformation: - case ThreadSuspendCount: + case ThreadSuspendCount: // Since Windows 8.1 case ThreadCycleTime: case ThreadTimes: break; @@ -926,7 +1057,7 @@ NTSTATUS NTAPI ProcNtQueryVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, SIZE_T RetLen = 0; NTSTATUS Status; SHM::CArgPack<> api; - SHM::CArgPack<1024> apo; + SHM::CArgPack<3072> apo; api.PushArg(MemoryInformationLength); api.PushArg(MemoryInformationClass); api.PushArg(BaseAddress); @@ -936,7 +1067,7 @@ NTSTATUS NTAPI ProcNtQueryVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, apo.PopArg(RetLen); apo.PopBlk(MemoryInformationLength,MemoryInformation); // Full buffer exchange if(ReturnLength)*ReturnLength = RetLen; - if(Status || !RetLen){DBGMSG("Failed with status: %08X!",Status); return Status;} + if(Status || !RetLen){DBGMSG("Failed with status: %08X, %p, %u!",Status,BaseAddress,MemoryInformationClass); return Status;} switch(MemoryInformationClass) { case MemoryBasicInformation: diff --git a/XDbgPlugin/XDbgPlugin.h b/XDbgPlugin/XDbgPlugin.h index d739a4a..d657cf5 100644 --- a/XDbgPlugin/XDbgPlugin.h +++ b/XDbgPlugin/XDbgPlugin.h @@ -1,25 +1,40 @@ #pragma once - +/* + Copyright (c) 2020 Victor Sheinmann, Vicshann@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ #include #include "Utils.h" #include "UniHook.hpp" -#include "FormatPE.h" +#include "NtDllEx.hpp" #include "GhostDbg.hpp" #include "InjDllLdr.hpp" //==================================================================================== #define CFGSECNAME L"Parameters" -#define XDBGPLG_VER 1 +#define XDBGPLG_VERH 2 +#define XDBGPLG_VERL 0 #define XDBGPLG_NAME "GhostDbg" #define XDBGPLG_BUILD __DATE__ " - " __TIME__ -#define MENU_ID_ENABLED 1 -#define MENU_ID_ABOUT 2 -#define MENU_ID_CHK_CANINJ 3 +#define MENU_ID_ENABLED 1 +#define MENU_ID_ABOUT 2 +#define MENU_ID_CHK_CANINJ 3 #define MENU_ID_CHK_CANINJNEW 4 -#define MENU_ID_DBGCLIENT 16 +#define MENU_ID_SUSPPROCESS 5 +#define MENU_ID_USERAWTHREADS 6 +#define MENU_ID_DBGCLIENT 16 //==================================================================================== @@ -41,7 +56,7 @@ BOOL WINAPI ProcDebugActiveProcessStop(DWORD dwProcessId); BOOL WINAPI ProcWaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMilliseconds); BOOL WINAPI ProcContinueDebugEvent(DWORD dwProcessId, DWORD dwThreadId, DWORD dwContinueStatus); BOOL WINAPI ProcDebugBreakProcess(HANDLE Process); -BOOL WINAPI ProcTerminateProcess(HANDLE hProcess, UINT uExitCode); +BOOL WINAPI ProcIsWow64Process(HANDLE hProcess, PBOOL Wow64Process); BOOL WINAPI ProcCreateProcessA(LPCSTR lpApplicationName,LPSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCSTR lpCurrentDirectory,LPSTARTUPINFOA lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation); BOOL WINAPI ProcCreateProcessW(LPCWSTR lpApplicationName,LPWSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation); @@ -57,7 +72,8 @@ NTSTATUS NTAPI ProcNtGetContextThread(HANDLE ThreadHandle, PCONTEXT Context); NTSTATUS NTAPI ProcNtSetContextThread(HANDLE ThreadHandle, PCONTEXT Context); NTSTATUS NTAPI ProcNtReadVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T BufferLength, PSIZE_T ReturnLength); NTSTATUS NTAPI ProcNtWriteVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T BufferLength, PSIZE_T ReturnLength); -NTSTATUS NTAPI ProcNtTerminateThread(HANDLE ThreadHandle, NTSTATUS ExitStatus);; +NTSTATUS NTAPI ProcNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus); +NTSTATUS NTAPI ProcNtTerminateThread(HANDLE ThreadHandle, NTSTATUS ExitStatus); NTSTATUS NTAPI ProcNtSuspendThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount); NTSTATUS NTAPI ProcNtResumeThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount); NTSTATUS NTAPI ProcNtClose(HANDLE Handle); diff --git a/XDbgPlugin/XDbgPlugin.vcxproj b/XDbgPlugin/XDbgPlugin.vcxproj index 22e03c1..a057d6f 100644 --- a/XDbgPlugin/XDbgPlugin.vcxproj +++ b/XDbgPlugin/XDbgPlugin.vcxproj @@ -124,16 +124,15 @@ false Default MultiThreadedDebug - - + Default false - StreamingSIMDExtensions + StreamingSIMDExtensions2 false NotUsing Level3 false ProgramDatabase - StdCall + FastCall false true false @@ -183,16 +182,15 @@ false Default MultiThreadedDebug - - + Default false - StreamingSIMDExtensions + NotSet false NotUsing Level3 false ProgramDatabase - StdCall + FastCall false true false @@ -229,8 +227,8 @@ - MinSpace - OnlyExplicitInline + MaxSpeed + AnySuitable false Speed false @@ -240,17 +238,16 @@ true false MultiThreaded - - + Default false - StreamingSIMDExtensions + StreamingSIMDExtensions2 false NotUsing Level3 false - StdCall + FastCall CompileAsCpp true stdcpplatest @@ -291,8 +288,8 @@ copy /Y "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)BUILD\RELEASE\$(Solu - Disabled - OnlyExplicitInline + MaxSpeed + AnySuitable false Speed false @@ -302,17 +299,16 @@ copy /Y "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)BUILD\RELEASE\$(Solu true false MultiThreaded - - + Default false - StreamingSIMDExtensions + NotSet false NotUsing Level3 false - StdCall + FastCall CompileAsCpp true stdcpplatest @@ -351,6 +347,7 @@ copy /Y "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)BUILD\RELEASE\$(Solu + @@ -358,6 +355,7 @@ copy /Y "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)BUILD\RELEASE\$(Solu + diff --git a/XDbgPlugin/XDbgPlugin.vcxproj.filters b/XDbgPlugin/XDbgPlugin.vcxproj.filters index 6624a0a..027f320 100644 --- a/XDbgPlugin/XDbgPlugin.vcxproj.filters +++ b/XDbgPlugin/XDbgPlugin.vcxproj.filters @@ -24,6 +24,9 @@ Common + + Common + @@ -53,6 +56,9 @@ Common + + Common + diff --git a/XDbgPlugin/pluginsdk/bridgemain.h b/XDbgPlugin/pluginsdk/bridgemain.h index e66fe1a..576cfa4 100644 --- a/XDbgPlugin/pluginsdk/bridgemain.h +++ b/XDbgPlugin/pluginsdk/bridgemain.h @@ -8,7 +8,7 @@ #endif //list structure (and C++ wrapper) -#include "bridgelist.h" +//#include "bridgelist.h" // Disabled because brings in C runtime (vector) //default structure alignments forced #ifdef _WIN64