![]() |
Unseen Debugger Detection (Ollydbg)
1 Attachment(s)
Maybe somebody came across this before, but i just came across it in a program, it kept catching me, and i couldnt for the life find where, finally after a while i came across the following, and emulated it here, any discussion if you saw it, would be nice.
The program calls ZwQueryObject, with a null handle and fills the OBJECT_ALL_TYPES_INFORMATION structure, it checks if the current object type is "DebugObject", if it is, it then checks if pObject->TotalNumberOfHandles, and pObject->TotalNumberOfObjects are greater than 0, if they then the program is being debugged, i didnt try it with softice, as i didnt get it installed yet, but it detects ollydbg just fine. Heres the emulated code i wrote, and the compiled exe: |
Hello Peter[Pan],
It looks good idea...though I have executed it in my WinXP SP2 withouth any debugger and it detects debugger :( I'll have a deeper look as soon as I can. |
Weird, try uncommenting the printf's and comparing when you have no debugger active etc, should see some differnces, maybe only 1 of the two i saw is affected, also iam running winxp sp2, and both were changed.
No Debugger: TypeName: DebugObject DefaultNonPagedPoolCharge: 30 DefaultPagedPoolCharge: 0 GenericMapping: 20001 HighWaterNumberOfHandles: 5 HighWaterNumberOfObjects: 6 InvalidAttributes: 0 MaintainHandleCount: 0 PoolType: 0 SecurityRequired: 1 TotalNumberOfHandles: 0 TotalNumberOfObjects: 0 Debugger: TypeName: DebugObject DefaultNonPagedPoolCharge: 30 DefaultPagedPoolCharge: 0 GenericMapping: 20001 HighWaterNumberOfHandles: 5 HighWaterNumberOfObjects: 6 InvalidAttributes: 0 MaintainHandleCount: 0 PoolType: 0 SecurityRequired: 1 TotalNumberOfHandles: 1 TotalNumberOfObjects: 1 |
winXP+SP1
ollydbg 1.10, not patched, with olly invisible plugin -- it detects debugger softice in DS3.2, without any plugin -- it didn't detect debugger :) |
When you create/attach a program inside the debugger, the debug api will call a native function called "NtCreateDebugObject" that will create a DebugObject and set the EPROCESS->DebugPort = DebugObject.
SoftICE don't use the Debug API, that is the reason that this trick don't detect it. Regards, Opc0de |
I tried to write a debugger loader (createprocess in debug mode) which calls the ZwSetInformationObject just after having se to 0 those two values, just before resuming detect.exe, and I still get the detection message..
humm interesting.. |
in win98 everything goes ok, there is no message..... any explanation?
|
Quote:
|
1 Attachment(s)
I can add some info too. (Source code/Executable)
(When you run it, Olly crash) |
to me it doesn't crash, Olly it's still alive after debugging of this..
|
Taos, what exactly is the difference between system and process debugger (as shown in your example?) I dont have SI installed to test but is that what it's referring to?
Shub: I think he was referring to his version that crashes in olly. Although when I run it, it detects a process debugger (same w/ msvs it detects process debugger). I think by crash he means it keeps running and doesn't stop. But it's definitately detected. very interesting pan |
Quote:
can you pause and continue?. You can not use Olly to debug another app, you must exit. App crash when you run it with Olly too. Quote:
|
taos, effectively deepeer look revealed those strange things..any glue why?
I were also thinking a way to overcome the problem raised by the first post of this thread: how can I access that structure in a given process so as to proper values? I mean, it's easily possible to patch the ZwQueryObject to return a null buffer and a null lenght, but it's not elegant. For example to avoid IsDebuggerPresent and similar checks, the most elegant way was to access the PEB block and change some values instead of patching the API. Is such approach still doable with the OBJECT_ALL_TYPES_INFORMATION structure, where it is stored in a process? I did some tests but were not able to find it or to find some specifications around. |
Shub-Nigurrath, the information is actually gathered from kernel memory, at least its the case for DebugPort, only way to reset this is, by writing to kernel memory, which in some cases could be bad, take a look at:
http://illhostit.com/files/1787442368222872/Winject%201.5c%20(exe).rar Last time i checked it was able to reset DebugPort from user mode, the way i think they are doing it is by getting the _EPROCESS struct for that particular process id, then doing some hacking to read/write to the memory of ->DebugPort and resetting it, which would be in kernel memory. Atm iam solving with a kmd the DebugObject problem, but a usermode solution would be preferable. |
yo
LPVOID AddrZwQueryInformationProcess = (LPVOID)-1;
BYTE SavedByteZwQueryInformationProcess = 0; bool DisableZwQueryInformationProcessDebugPort(void) { DWORD numread; BYTE bFE = 0xFE; HMODULE ntdll = GetModuleHandle("ntdll.dll"); AddrZwQueryInformationProcess = GetProcAddress(ntdll, "NtQueryInformationProcess"); ReadProcessMemory(hproc, AddrZwQueryInformationProcess, &SavedByteZwQueryInformationProcess, 1, &numread); WriteProcessMemory(hproc, AddrZwQueryInformationProcess, &bFE, 1, &numread); return true; } bool ZwQueryInformationProcessTracer(DEBUG_EVENT evt) { DWORD numread; BYTE bFE = 0xFE; ReadProcessMemory(hproc, AddrZwQueryInformationProcess, &SavedByteZwQueryInformationProcess, 1, &numread); WriteProcessMemory(hproc, AddrZwQueryInformationProcess, &bFE, 1, &numread); return false; // end trace } in debug loop: if (evt.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION && evt.u.Exception.ExceptionRecord.ExceptionAddress == AddrZwQueryInformationProcess) { hthread = ThreadIdToHandle(evt.dwThreadId); DWORD stack[6]; DWORD numread; ReadProcessMemory(hproc, (LPVOID)ctx.Esp, &stack, sizeof(DWORD) * 6, &numread); MsgBoxF("ZwQueryInformationProcess trapped ;) >%08X (%08X, %08X, %08X)", stack[0], stack[1], stack[2], stack[3]); if (stack[2] == 7) { DWORD d0 = 0; WriteProcessMemory(hproc, (LPVOID)stack[3], &d0, 1, &numread); d0 = ctx.Esp - 4 * 3; WriteProcessMemory(hproc, (LPVOID)(ctx.Esp + 3 * 4), &d0, sizeof(DWORD), &numread); } WriteProcessMemory(hproc, AddrZwQueryInformationProcess, &SavedByteZwQueryInformationProcess, 1, &numread); StartTrace(hthread, ZwQueryInformationProcessTracer); ContinueStatus = DBG_CONTINUE; } else // note: plz do not rip this code 1:1 into any pulumbium tutorials |
I tried with a more polite solution, but I had no success...
I closed the DebugObject handle remotely (WinXP), using a CreateRemoteThread and CloseThread as thread address, and, as soon as I did so, I lost the debuggee control from the debugger... Regards, bilbo |
well doh, the debug port exists for a purpose...
guess what is used for |
thx for the explanation, upb...
I would never have been able to find it myself ;-) bilbo |
I wonder if it would be possible to open \Device\PhysicalMemory and map it, and search for the DebugObject structure, and then physically decrement the counts prior to run?
I think it'd be a cute plug-in for Olly, no? Granted, there are some memory regions to stay away from while searching, but it'd be all Ring 3... |
Peter[Pan] for me your dectection works fine..
Xp +sp2 bye NeO |
Peter[Pan],
On my W2K+SP4+Rollup update your trick doesn't work. I can't download your example (i haven't permissions for this) and i've wrote my own. After ZwQueryObject structure ObjectAllTypesInformation fills with 17h OBJECT_TYPE_INFORMATION structures. But there is no "DebugObject" name within. Did you mean "OBJECT_TYPE_INFORMATION.name" member ? |
Peter[Pan]
The protection you've done is quite cool. My original idea was to modify the structures that the Query would return, and while finding them in physical memory is easy, it appears to create a race condition on program termination (ie. debugger tries to release debug object that REALLY has a zero reference count). Yuch! It might be possible to hook Query without detection, but I'm begining to think ring-0 might be the only fool-proof way to do this. Definitely a brain-teaser. |
I don't think it would need to be ring-0. Just hook ZwQuery and watch for the NULL input and then BPX on the return and modify the buffer that gets returned. Regardless of how "elegant" the solution is, nonetheless it is the solution. Remember, if you code your own debugger, you do not have to use int3's for breakpoints. You can use other things too, and I suggest you research into it some more. (how about using privileged exceptions, eh?). It makes your debugger even more undetectable. In fact, I think a worthy addition to Olly would be to allow for custom breakpoints (using a exception instruction of your own rather than INT3)
For example, all Olly has to do is allow you to set a memory read error breakpoint. Overwrite the code with "mov eax,[eax]" or something. Then Olly keeps an internal list of where that exception should occur. When the exception happens, it recognizes it from its internal list (if not found in the list it passes it back to the debugee). Then restores the original instructions. I've built all my unpackers this way which makes them pretty much zero-detectable unless you know what type of "breakpoint" I'm using. Searching for 0xCC won't detect them. So just remember after you get Olly to work by hand (you can always just set a breakpoint on the RET of the function rather than the beginning of the function you know!) then you can go on to write you own tool, loader or unpacker, but make it better in those respects (by avoiding the use of INT3 breakpoints for example). -Lunar |
Peter[Pan], I've just test your ZwQueryObject detection under W2K - it doesn't work too. There is no DebugObject yet!
|
At the end of all my tests the most reliable way I found to ovecome this test and other similars too (all which are based on a system API generally) is to patch the API to always return a friendly result (friendly fo us ^_^).
For example when writing a debug loader I added some operations to essentialy find the ntdll loading base addressof the victim, get the export I want to patch and patch its ealy bytes. For ZwQueryObject I patched it as following Code:
7C91E0D8 > 83FF 00 CMP EDI,0 |
1 Attachment(s)
> I've built all my unpackers this way which makes them pretty much zero-detectable
Respect LD, You make enough for lame peoples. Much better do not even launch target program - if you _realy_ understand how protection work, you can _always_ write static unpacker. Lame old bonus inside attachment. |
Very interesting piece of code, Dr.Golova, thanks for sharing.
|
hmm nice code ...thx ..for sharing ..really nice :P
bye NeOXOeN |
| All times are GMT +8. The time now is 02:12. |
Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2026, vBulletin Solutions, Inc.
Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX