View Full Version : Win Debug API's
JimmyClif
May 3rd, 2005, 08:19
Hi guys,
Can anyone tell me what I'm doing wrong with this snippet of code? I just try to start an Asprotected Exe through the WinDebug Api and to evade the IsDebuggerPresent call I 'try' to clear the flag.
All my calls to ReadProcessMemory fail though...
Code:
HideIsDebuggerPresent proc
LOCAL Ouch:BYTE
mov tc.ContextFlags, CONTEXT_FULL
invoke GetThreadContext,pi.hThread,o$ tc
mov ecx, tc.regFs
add ecx, 18h
invoke ReadProcessMemory,pi.hProcess,ecx,a$ szTemp,4,NULL
mov ecx, d$ [szTemp]
add ecx, 30h
invoke ReadProcessMemory,pi.hProcess,ecx,a$ szTemp,4,NULL
mov ecx, d$ [szTemp]
invoke ReadProcessMemory,pi.hProcess,ecx,a$ szTemp,4,NULL
mov ecx, d$ [szTemp]
add ecx, 2
mov Ouch, 0
invoke WriteProcessMemory,pi.hProcess,ecx,a$ Ouch,1,NULL
ret
HideIsDebuggerPresent endp
I'm using as base for this Icz's Tut Number 28, only modification is that all calls to ContinueDebugEvent are flagged as DBG_EXCEPTION_NOT_HANDLED.
JimmyClif
May 3rd, 2005, 08:35
Here's the whole source...
sHice
May 3rd, 2005, 09:00
mov ecx, tc.regFs <- let's say FS is 30h
add ecx, 18h <- and you add 18h.this means that you try to read memory from 48h! so ReadProcessMemory fails!
here's the way i do it:
assume fs: nothing
mov eax, fs:[18h]
mov eax, [eax+30h]
add eax, 02h
invoke WriteProcessMemory, t_hProcess, eax, addr DbgPatch, sizeof DbgPatch, 0 ;DbgPatch db 00h
JimmyClif
May 3rd, 2005, 09:29
I see how that can be false.. but still - no go

I can't make the connection.
This is what I'm basing it on...
assume fs: nothing
mov eax, fs:[18h]
mov eax, [eax+30h]
add eax, 02h
And then I load my exe and need to get the address of what RegFs[18h] is pointing to in that context, right?
How would I do that with the indirection of 18h?
disavowed
May 3rd, 2005, 09:29
Quote:
[Originally Posted by sHice]here's the way i do it:
assume fs: nothing
mov eax, fs:[18h]
mov eax, [eax+30h]
add eax, 02h
invoke WriteProcessMemory, t_hProcess, eax, addr DbgPatch, sizeof DbgPatch, 0 ;DbgPatch db 00h |
Actually, this will use the debugger-process's memory to compute EAX. You want to use the debuggee-process's memory to compute EAX, since you will be using the value of EAX in the context of the debuggee. Thus, instead of using the code above to retrieve fs:[18h], you should use GetThreadSelectorEntry(...) to get the address of the fs segment in the debuggee, and then add 18h to that and ReadProcessMemory(...) it. Then ReadProcessMemory(...) that plus 30h, and then add 2.
JimmyClif
May 3rd, 2005, 09:33
I'm on it

JimmyClif
May 3rd, 2005, 11:34
Howdy,
Can someone have a look and tell me if I'm getting closer?
Code:
HideIsDebuggerPresent proc
LOCAL Ouch:BYTE
;int 3
mov tc.ContextFlags, CONTEXT_FULL
invoke GetThreadContext,pi.hThread,o$ tc
invoke GetThreadSelectorEntry,pi.hThread,tc.regFs,o$ ltdentry
xor eax, eax
mov ax, ltdentry.BaseLow
xor edx, edx
mov dl, ltdentry.HighWord1.Bytes.BaseMid
shl edx, 16
xor ecx, ecx
mov cl, ltdentry.HighWord1.Bytes.BaseHi
shl ecx, 24
add eax, edx
add eax, ecx
;int 3
; eax == 7FFDF000 (assume correct)
add eax, 18h
invoke ReadProcessMemory,pi.hProcess,eax,o$ szTemp,4,0
mov eax, o$ szTemp
add d$ [eax], 30h
invoke ReadProcessMemory,pi.hProcess,[eax],o$ szTemp,4,0
mov eax, o$ szTemp
add d$ [eax], 2
mov Ouch, 0
mov edx, eax
invoke WriteProcessMemory,pi.hProcess,edx,a$ Ouch,1,NULL
ret
HideIsDebuggerPresent endp
JimmyClif
May 3rd, 2005, 11:51
YES!!!!!!!!!
Got it!
Last Line I forgot the indirection with [edx]
Correct version below:
Code:
HideIsDebuggerPresent proc
LOCAL Ouch:BYTE
;int 3
mov tc.ContextFlags, CONTEXT_FULL
invoke GetThreadContext,pi.hThread,o$ tc
invoke GetThreadSelectorEntry,pi.hThread,tc.regFs,o$ ltdentry
xor eax, eax
mov ax, ltdentry.BaseLow
xor edx, edx
mov dl, ltdentry.HighWord1.Bytes.BaseMid
shl edx, 16
xor ecx, ecx
mov cl, ltdentry.HighWord1.Bytes.BaseHi
shl ecx, 24
add eax, edx
add eax, ecx
;int 3
add eax, 18h
invoke ReadProcessMemory,pi.hProcess,eax,o$ szTemp,4,0 ; eax == 7FFDF018
mov eax, o$ szTemp
add d$ [eax], 30h ; [EAX]==???
invoke ReadProcessMemory,pi.hProcess,[eax],o$ szTemp,4,0
mov eax, o$ szTemp
add d$ [eax], 2
mov Ouch, 0
mov edx, eax
invoke WriteProcessMemory,pi.hProcess,[edx],a$ Ouch,1,NULL
ret
HideIsDebuggerPresent endp
About bloody time.
sHice
May 3rd, 2005, 12:20
i forgot to mention that the pointer to the PEB, which is located at fs:[30h], is the same for every process (on my machine it's 7FFDF000h).so it doesn't matter if we grab the offset in the process of the debugger or in the process of the debuggee... offset will stay the same.
evaluator
May 3rd, 2005, 13:42
eh, you already recieved:
CREATE_PROCESS_DEBUG_INFO.lpThreadLocalBase;
& for new thread:
CREATE_THREAD_DEBUG_INFO.lpThreadLocalBase;
so you not need those GetThread..apis..
[edit]
p.s.
assume fs: nothing
mov eax, fs:[18h]
mov eax, [eax+30h]
is same:
mov eax, fs:[30h]
nikolatesla20
May 3rd, 2005, 17:24
now now eval, perhaps he isn't handling the CREATE_THREAD debug event in order to keep track of threads, and it makes for clearer code to clearly show what is occuring (get the handle in the code that uses it, not somewhere clear across source files)
Best wishes,
-nt20
Neitsa
May 3rd, 2005, 19:04
Hello,
Quote:
so it doesn't matter if we grab the offset in the process of the debugger or in the process of the debuggee... offset will stay the same. |
This is not the fact on all NT O.S. In my own debugger, this idea was working like a charm on win 2k, but this wasn't exact on win XP Pro SP2.
AFAIK, you shouldn't rely on any segment address on win XP, and shoudn't presume that those segments will be the same in the context of the debugger and in the debugged process/thread.
You could end up in some unknown behaviours or results. Most of the time Read/WriteProcessMemory doesn't return correctly and getlasterror returns "ERROR_PARTIAL_COPY" if you base your code on the same address.
I've previously experienced this problem, and with the same code as JimmyCliff, all results were good.
disavowed
May 4th, 2005, 01:14
This is the same thing that always pisses me off about poorly written remote-process tools that deal with loaded DLLs. They often assume that the address that the module is loaded at in the debugger is the same that the module is loaded at in the debuggee. Though often true, it's not guaranteed.
blabberer
May 4th, 2005, 03:24
7ffdf000 is constant only on w2k in xp it varies to any thing from 7ffda000 to
others
also 7ffde000 aka fs:[0] aka teb also is constant only in w2k not in xp
so dont hardcode them instead always
use assume fs:NOTHING
MOV EAX,fs:[blah]
mov eax,[eax+blah]
code which will work on both platforms
what both these have in common is
env values at 10000
heap at 130000
*processparams at 20000
all other hardcodes dont work
JimmyClif
May 4th, 2005, 10:17
Hi guys,
Sorry for not reading up on it - but where did I receive the CREATE_PROCESS_DEBUG_INFO.lpThreadLocalBase and how would it cut down those two GetThread Apis?
disavowed
May 4th, 2005, 10:21
JimmyClif, read http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/debugging_events.asp ("http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/debugging_events.asp")
Neitsa
May 13th, 2005, 07:56
Hello,
Just two more points...
We were discussing about retrieving the FS segment linear address on a debuggee with friends, when we found that :
1) At Entry point: EBX = P.E.B address (FS:[30h])
2) At Entry point: [ESP+0Ch] = P.E.B address
(Note that it is only under NT plateform)
Ok, that's not a big deal, but... it can save some opcodes (don't need to use GetThreadSelectorEntry then, just GetThreadContext)
All credits to Kharneth & Yolejedi.
FrankRizzo
March 6th, 2006, 23:37
Yes, I'm fluent in asm, but I'm writing this whole app in C++, and just want to keep the inline asm out, so, I wrote the following snippet, which LOOKS like it should work, but doesn't. Any help appreciated!
Code:
// Start the child process.
if( !CreateProcess(
FileName,
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
DEBUG_PROCESS, // Creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
return;
}
hProcess = pi.hProcess;
hThread = pi.hThread;
SuspendThread(hThread);
unsigned long address;
tc.ContextFlags = CONTEXT_FULL;
GetThreadContext(hThread, &tc);
GetThreadSelectorEntry(hThread, tc.SegFs, <dentry);
address = (ltdentry.BaseLow) +
(ltdentry.HighWord.Bytes.BaseMid << 16) +
(ltdentry.HighWord.Bytes.BaseHi << 24) +
0x18;
ReadProcessMemory(hProcess, (LPVOID)address, buffer, 4, &count);
address = (unsigned long)buffer;
address += 0x30;
ReadProcessMemory(hProcess, (LPVOID)address, buffer, 4, &count);
address = (unsigned long)buffer;
address += 2;
buffer[0] = 0;
WriteProcessMemory(hProcess, (LPVOID)address, buffer, 1, &count);
ResumeThread(hThread);
It compiles with no errors, no warnings, so, the structures are correct, although not presented.
Shub-nigurrath
March 7th, 2006, 03:51
FrankRizzo, take a look at my tutorials on loaders available at tutorials.accessroot.com, among other things there's a complete dissection of an HideDebugger routine, in C++..
FrankRizzo
March 7th, 2006, 09:08
OK, then maybe I'm missing something.
Target has "ASProtect 1.23 RC4 Registered". I'm using the "HideDebugger(HANDLE thread, HANDLE hproc)" function from the above mentioned tutorial. Inside my loader, when I use CreateProcess with NULL for the creation flags, it runs fine, when I use DEBUG_PROCESS, I get nothing. Not being an expert on ASProtect, I assumed it was only checking "IsDebuggerPresent", and that HideDebugger would remedy that.
Is there something else that I'm missing?
akane
March 7th, 2006, 11:03
FrankRizzo,
Flag DEBUG_PROCESS in CreateProcess does not block, when the api returns the target process does not yet exists.
(at least once call WaitForDebugEvent/your stuff/ContinueDebugEvent, or change flag to CREATE_SUSPENDED)
Code:
CreateProcess(...)
if WaitForDebugEvent(m_de, dwMilliseconds)
{
m_bContinue = false;
switch(m_de.dwDebugEventCode)
{
case CREATE_PROCESS_DEBUG_EVENT:
m_ModuleBase = m_de.CreateProcessInfo.lpBaseOfImage;
m_EntryPoint = m_de.CreateProcessInfo.lpStartAddress;
HideDebuggerOnTeb();
m_bContinue = OnCreateProcess(
m_de.CreateProcessInfo.hFile,
m_de.CreateProcessInfo.hProcess,
m_de.CreateProcessInfo.hThread);
case ...
}
if (m_bContinue)
ContinueDebugEvent(m_de.dwProcessId, m_de.dwThreadId, DBG_CONTINUE)
else ...
}
FrankRizzo
March 7th, 2006, 18:46
OK, the thing that was missing from my comment was that I use SuspendThread right after the call to CreateProcess.
For what it's worth, I just tried CREATE_SUSPENDED, with a ResumeThread, and STILL get nothing.
nikolatesla20
March 7th, 2006, 21:11
Ok - if you are using DEBUG_PROCESS, you should also include DEBUG_ONLY_THIS_PROCESS. Also, you have to use WaitForDebugEvent if you started the process with DEBUG_PROCESS.
If you start the program as CREATE_SUSPENDED, you may want to also try adding CREATE_NEW_CONSOLE as well, Sometimes Createprocess will fail without it. ALso, MAKE sure your STARTUPINFO and PROCESS_INFORMATION structures are correct. Remember STARTUPINFO you have to set it's "cb" member before using it. If you do all those things, you should be able to create suspended, and then use a ResumeThread call to let the program continue.
However, you cannot do ReadprocessMemory and WriteProcessMemory on a suspended createprocess reliably. The correct way to do it is to grab the entry point bytes of the program (grab two of them), and overwrite them with 0xEBFE which will create an infinite jump. Now resumethread, and wait a couple seconds, then you can do your readprocessmemory for anti-detection,etc. Reason for this is when you create a process as suspended it is not fully loaded into memory right away, in fact it's imports won't even be loaded yet ! But if you do the infinite loop trick and wait, everything will load up. Then suspendthread again and restore the entry point bytes that you ripped, and when you resume again the program will start up.
-nt20
FrankRizzo
March 7th, 2006, 23:28
I guess I've reached the point of complete confusion. I've attached my code, it looks right from everything that I've read. (For reference, I have another app that loads a process, sets breakpoints (via $CC INT 3 instructions), continues, read, writes, and everything works fine. But for some reason this one fails.)
So, one last time, HELP?!?
Also, in the CreateProcess, I have the creation flags as NULL, but next to it is the one that I use, which is DEBUG_PROCESS, and DEBUG_ONLY_THIS_PROCESS. The NULL is there to test, and when it's NULL, it runs fine.
akane
March 8th, 2006, 04:41
You forgot to SuspendThread before GetThreadContext

Read in SDK - "[...]You cannot get a valid context for a running thread"
FrankRizzo
March 8th, 2006, 08:35
In the code that I just posted, I'm inside a "Debug Event Exception", so I didn't think that I NEEDED to do a SuspendThread. But just for discussion I added that to the "HideDebugger" function, along with a matching ResumeThread at the end of the function, and it made no difference.
nikolatesla20
March 8th, 2006, 11:41
You need to also handle at least once the DEBUG_EXCEPTION, which every debugged program will get right away from the windows system. Here is an example from some of my unpacker codes:
Code:
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.
switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
{
// This breakpoint is called once always.
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.
if (DoneOnce == FALSE)
{
// First time thru, breakpoint is from windows system
// Remove ability for Armadillo to detect us
KillIsDebuggerPresent(pi);
// let system know we are OK to continue on
dwContinueStatus = DBG_CONTINUE;
DoneOnce = TRUE;
// Remember to get out here !
break;
}
if (DoneOnce == TRUE)
{
// pass unexpected int3 exception back to program
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
}
break;
default:
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
}
break;
// handle other exceptions below here, such as CREATE_PROCESS exceptions, etc.
Also, sorry I confused you on the DEBUG_PROCESS flag - you should only need the DEBUG_ONLY_THIS_PROCESS flag. What this does is ensures that if the process you are debugging tries to create a process itself, you will only be debugging the first process. If you use DEBUG_PROCESS, you will debug the first process and any of its child processes, which is not what you want. You only need DEBUG_ONLY_THIS_PROCESS. Take out DEBUG_PROCESS altogether.
Also, if you are using the debugger against any protector, you will need to handle lots of other exceptions so they are handled correctly. Most exceptions such as single step exceptions, etc, need to be returned as DEBUG_EXCEPTION_NOT_HANDLED. So just returning DBG_CONTINUE always will not work and will cause the program to exit. Start off by using the debugger against a normal program to get used to what it does.
-nt20
Maximus
March 8th, 2006, 11:54
FrankRizzo, you might wish to look to Iczelion code, http://win32assembly.online.fr/tut28.html
DbgBreak() is called right before jumping EP, causing a bpx exception on a debugging context.
Also, Don't or DEBG and debug-only. From my basic loader:
Flags := CREATE_SUSPENDED or DEBUG_ONLY_THIS_PROCESS
or CREATE_DEFAULT_ERROR_MODE or NORMAL_PRIORITY_CLASS;
Regards,
Maximus
edit-------------
Quote:
[Originally Posted by nikolatesla20]
Most exceptions such as single step exceptions, etc, need to be returned as DEBUG_EXCEPTION_NOT_HANDLED. So just returning DBG_CONTINUE always will not work and will cause the program to exit.
-nt20 |
These are debugger traps, because these situations are properly handled by the application SEH. If your debugger 'reapirs' them by returning a DBG_CONTINUE, the app SEH handler will not be called, and the app will discover itself debugged.
FrankRizzo
March 8th, 2006, 20:48
Thanks guys!
As it turns out, it was the handling of the exceptions that was causing me problems. My "debugger main loop" now looks like this:
Code:
if( WaitForDebugEvent(&DebugEv, INFINITE) )
{
dwContinueStatus = DBG_CONTINUE;
// Process the debugging event code.
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
case CREATE_PROCESS_DEBUG_EVENT:
if(!HideDebugger(hThread, hProcess))
{
puts("Hiding Failed!"

;
done = 1;
}
break;
case EXIT_PROCESS_DEBUG_EVENT:
done = true;
break;
}
ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus);
}
And it works just fine! So, now I'm off to implement a "search and replace" in process memory, and my loader should be complete!
Thanks again!
Shub-nigurrath
March 9th, 2006, 10:25
Hi,
for the S&R algo I found the BMG GSAR (Boyer/Moore/Gosper-assisted) algo to be extremely fast.
In the tutorial "Guide on How to play with processes memory, write loaders and Oraculums" distribution (available at the ARTeam's tutorial's pages) it's included an implementation, I transformed in C++ and modified to to search in a memory range of data. Really fast indeed!
The files gsar.cpp and gsar.h are what you will need most probably.
Maximus
March 9th, 2006, 10:51
It sounds me familiar... isnt it the algo that works with reverse string match? It is actually the fastest around, if you don't need to play with wildcards, else you'd try the mmh... Baetza or something similar. I never be good at names...
edit--------
nope.
it is BEAZA/YATES/GONNET algo.
Admiral
March 11th, 2006, 08:18
It's not a bug, just a consequence of the way the debug API was written. The phenomenon is briefly documented in MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/load_dll_debug_info_str.asp
Quote:
lpImageName
Pointer to the filename associated with hFile. This member may be NULL, or it may contain the address of a string pointer in the address space of the process being debugged. That address may, in turn, either be NULL or point to the actual filename. If fUnicode is a nonzero value, the name string is Unicode; otherwise, it is ANSI.
This member is strictly optional. Debuggers must be prepared to handle the case where lpImageName is NULL or *lpImageName (in the address space of the process being debugged) is NULL. Specifically, the system will never provide an image name for a create process event, and it will not likely pass an image name for the first DLL event. The system will also never provide this information in the case of debugging events that originate from a call to the DebugActiveProcess function. |
If you really need the name of the DLL, you should wait for the DLL to finish loading then extract it from the process using either hFile or lpBaseOfDll:
One way would be to ReadProcessMemory your way through the virtual PE header to find the name, though I'm sure it's possible to use GetModuleFileNameEx, casting lpBaseOfDll to fit into the hModule parameter. Although this is quite reliable, it does seem a little hacky and it involves linking PSAPI (if you haven't already done so), so personally I'd stick with the first, albeit longer, method.
Regards
Admiral
Powered by vBulletin® Version 4.2.2 Copyright © 2018 vBulletin Solutions, Inc. All rights reserved.