Comments (8)
I see some ways to approach this, but I'm not a great fan of any of them:
- Hooking
KiUserCallbackDispatcher
: I'm pretty sure this is the worst possible way to go about this. The first reason I have for saying this is just my gut feeling about hooking anything namedKi__
. Just look at our famously portable and reliableKiUserExceptionDispatcher
. The second is that this doesn't have any advantage over... - Hooking
__ClientCallWinEventProc
. This function is similarly ominous in that it starts with two underscores (or three if you're still stuck in the 32 bit world). However, it is actually present in XP through 10, and while as you say, it's not an exported function, it is indirectly accessible through the PEB. You see, when new people come to work at Microsoft, they are given an IQ test. If they score below 70, they are assigned to work on win32k.sys and user32.dll. This is why there is aKernelCallbackTable
in the PEB for convenient access to user callbacks from kernel mode. This array includes__ClientCallWinEventProc
. However, the issue is that the index of this function is not fixed and varies per Windows version. On my Windows 7 machine it is located atKernelCallbackTable[0x4E]
, and I can see that your0x58
is correct for Windows 10. So we would need a reliable way to determine this offset. - Hooking
NtUserSetWinEventHook
(the ntdll-style syscall function in user32/win32u, notSetWinEventHook
which is only a wrapper for this). This is in my opinion the least hacky approach: we can keep a native equivalent of anstd::unordered_map<HWINEVENTHOOK, WINEVENTPROC>
around in which we store the original procs (note that we can't assumeSetWinEventHook
is only called once), to be ignored if theHWND
is the debugger's. Drawbacks:- This requires hooking
NtUserUnhookWinEvent
as well for consistency, otherwise the hook is trivially detectable and will also be very brittle unless the target program only unregisters its hook(s) at exit. - Persistent storage of any kind is problematic in HookLibrary. There is one other hook that does something similar: the
NtContinue
hook usesSAVE_DEBUG_REGISTERS ArrayDebugRegister[100]
. The benefit of using such an array is that it's statically allocated and can approximate anstd::unordered_map
fairly easily. However, if we use aSAVE_WINEVENTHOOK EventHooks[N]
, the target process can just makeN
bogus calls toSetWinEventHook
before we run out of storage. Heap storage is preferable, but this is much more complicated to implement correctly.
- This requires hooking
I have some other musings w.r.t. the above that relate to ScyllaHide in general. For example, hooking NtUserSetWinEventHook
/NtUserUnhookWinEvent
would require adding two new RVAs to NtApiCollection.ini
(the one that is generated by PDBReader). To be honest I'd rather see this file and PDBReader disappear completely. It is a clumsy way to obtain some trivial function addresses, all of which as far as I can tell are directly called from other functions that are exported with the call instruction within a few bytes of the export. Something like HDE64 could probably easily achieve the same thing without the need to rely on dbghelp and the MS symbol servers (not to mention running PDBReader after each Windows update). Any thoughts re: this @mrexodia? This disassembler wouldn't need to be implemented in HookLibrary, in fact it would be easier to keep the existing method of having the injector supply the RVAs, but it could load and disassemble user32.dll inside its own (debugger/CLI) process before injection.
Similarly I noticed that the index of __ClientCallWinEventProc
in the PEB array is always +2 relative to that of __ClientNoMemoryPopup
. The latter should be easy to find with a basic disassembler as it is the only function in user32.dll that calls MessageBoxW
.
from scyllahide.
Yeah, likewise. The only commercial protector I know of that (I think) uses this kind of functionality is Obsidium. Not that I studied in great detail, I only noticed that my paid-for copy of NTLite wouldn't start if IDA was running. I sent the author an email to kindly tell him to go fuck himself and that was that.
from scyllahide.
With the specific code used above (edited to check for "Ollydbg" as well), the detection "does not" occur if the debugger executable is is renamed to something random (123.exe as the example). I tested x64dbg and Ollydbg yielding identical results.
from scyllahide.
Just adding more info.
WINEVENTPROC
is called from:
user32.dll!___ClientCallWinEventProc@4()
ntdll.dll!_KiUserCallbackDispatcher@12()
user32.dll!PeekMessageW()
ClientCallWinEventProc
isn't exported, but KiUserCallbackDispatcher
is. So you could hook it, access the HWND
from the 3rd argument and use NtUserQueryWindow
with WindowProcess
to see if it belongs to the debugger:
NTSTATUS __stdcall __ClientCallWinEventProc(int (__stdcall **a1)(int, int, int, int, int, int, int))
{
int (__stdcall *v1)(int, int, int, int, int, int, int); // ST18_4@1
int (__stdcall *v2)(int, int, int, int, int, int, int); // ST14_4@1
int (__stdcall *v3)(int, int, int, int, int, int, int); // ST10_4@1
int (__stdcall *v4)(int, int, int, int, int, int, int); // ST0C_4@1
int (__stdcall *v5)(int, int, int, int, int, int, int); // ST08_4@1
int (__stdcall *v6)(int, int, int, int, int, int, int); // ST04_4@1
int (__stdcall *v7)(int, int, int, int, int, int, int); // ST00_4@1
int (__stdcall *v8)(int, int, int, int, int, int, int); // esi@1
int Result; // [sp+8h] [bp-1Ch]@1
int v11; // [sp+10h] [bp-14h]@1
int v12; // [sp+18h] [bp-Ch]@1
int v13; // [sp+1Ch] [bp-8h]@1
v11 = 0;
v12 = 0;
v13 = 0;
v1 = a1[7];
v2 = a1[6];
v3 = a1[5];
v4 = a1[4];
v5 = a1[3]; // this is the HWND
v6 = a1[2];
v7 = a1[1];
v8 = *a1;
__guard_check_icall_fptr(*a1);
Result = v8(v7, v6, v5, v4, v3, v2, v1);
return NtCallbackReturn(&Result, 0x18u, 0);
}
Since KiUserCallbackDispatcher
can also call other types of callbacks, you might need to filter the ApiNumber in the KiUserCallbackDispatcher_Hook
:
typedef
VOID
(NTAPI
*t_KiUserCallbackDispatcher)(
IN ULONG ApiNumber,
IN PVOID InputBuffer,
IN ULONG InputLength
);
t_KiUserCallbackDispatcher KiUserCallbackDispatcher_Trampoline;
VOID NTAPI KiUserCallbackDispatcher_Hook(ULONG ApiNumber, PVOID InputBuffer, ULONG InputLength) {
if (ApiNumber == 0x58 && InputBuffer) {
HWND &hwnd = *(HWND*)((UINT_PTR)InputBuffer + 0xC);
if (hwnd) {
HANDLE hHandle = NtUserQueryWindow(hwnd, WindowProcess);
if (isProtectedPid(hHandle)) {
hwnd = NULL;
}
}
}
return KiUserCallbackDispatcher_Trampoline(ApiNumber, InputBuffer, InputLength);
}
I'm not sure if this ApiNumber
is the same for other user32.dll
versions, nor if it's safe to simply ignore it and directly read the HWND, but I guess this option is better than messing with NtUserSetWinEventHook
like in my previous post.
from scyllahide.
Personally I never actually run PDBReader because nobody uses unreliable methods like this to detect x64dbg, but adding the disassembly in the ScyllaHide plugin sounds like a good alternative approach.
Also you can just rename x64dbg.exe
to FUCKDAPOLICE.exe
and it will also kinda rename the relevant windows 👍
from scyllahide.
Interestingly (or maybe not so), using Scyllahide with OllyDbg, the debugger window title is changed to match the profile being used by the plugin. And so when editing the code above to test for "OllyDbg", the debugger is not detected by the SetWinEventHook executable if the debugger is already running. ;)
from scyllahide.
Could you try if it detects x64dbg if you rename the executable to something random?
from scyllahide.
Heh. Yeah, checks like these are very frail and not that many protectors actually use them (because they are so easy to circumvent) unless you run the protector with the "paranoid" setting enabled. The issue is though that there are very many ways to query things about windows, desktops, the mouse, anything GUI related really, from win32k.sys (through user32.dll or win32u.dll nowadays in Win 10). The OP already listed several, but a more exhaustive list would be good. Also I'm not sure about how to go about adding this to SH since it could be some 10 or more hooks.
I looked at the various user32.dlls "through the ages" (from XP x86 to Win 10 x64) and unfortunately there are too many small differences to make a heuristic approach with a disassembler (like I talked about above) work I think. There is also the problem that if you want to add a new win32k syscall hook, you have to find a new heuristic and make it work on all OSes.
I still want to get rid of PDBReader and NtApiCollection eventually though, so I currently have a WIP project that uses a table of hardcoded syscall numbers (based on SyscallTables). This sounds like it is impossible to maintain, and it would be, except on builds >= 14393 of Win 10 we can get the syscall numbers from exported names in win32u.dll the same way you can already do it with ntdll. The remaining syscall numbers are fixed since the OSes have already been released. So in pseudocode the function to get an RVA looks something like this:
if os >= 14393:
return GetProcAddress(win32u, "NtUserQueryWindow") - win32uBase
else
syscallNum = GetSyscallIndexFromTable("NtUserQueryWindow")
return ScanForSyscallPattern(user32, syscallNum) - user32Base
I have a prototype app of this and it works on all OS/bitness combinations I've tested, but integrating it into ScyllaHide will take some time since there is a lot of code to replace.
from scyllahide.
Related Issues (20)
- ScyllaHide 2021-08-23_13-27-50 do not load on Windows XP Pro SP3 HOT 6
- Cant be installed on last version of x64dbg
- DbgUiRemoteBreakin not restored HOT 10
- InjectorCLIX86 does not work because it is unable to get the wow64cpu.dll base address HOT 3
- Game closes on any type of breakpoint. (Steam x64dbg) HOT 5
- crash with vmp3.5 (Ultra (Mutation + Virtualization)) HOT 8
- JobObjectBasicProcessIdList AntiDebug
- HandleTable AntiDebug
- ScyllaHide for Rider or Visual Studio HOT 2
- A confusing question when reading the InjectorCLI source code HOT 1
- not working with vmprotect 3.6 HOT 1
- Wow64Transition[0] != 0xEA HOT 3
- Create new release HOT 1
- Bypass detection on start
- HyperTech CrackProof
- Please add API Monitor and Cheat Engine to blacklisted process HOT 1
- [KillAntiAttach] NtContinue_FUNC_SIZE too short HOT 2
- Suggest VMProtect Heaven's Gate syscall Bypass trick. HOT 2
- DetourCreateRemote->ReadProcessMemory failed. (3) HOT 2
- DLL Injection doesn't work
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from scyllahide.