Jump to content
Tuts 4 You

NonIntrusive Debugger Library


Nieylana

Recommended Posts

Hey all,


 


It has been a long time since I posted to this forum,but I wanted to post here to this .NET library known to people who may find use of it.


 


The name of the project is NIDebugger (for non-intrusive debugging). There are currently 2 variants, NIDebugger (x86) and NIDebugger64 (x64).


 


The main page for the x86 project is here: http://tslater2006.github.io/NIDebugger/

 

Currently the x64 is experimental and can be found via the github repository: http://github.com/tslater2006/NIDebugger64

 

Because of the experimental state of the x64 library, I will only discuss the x86 one...

 

One of the most simple examples of using it is shown below, please note that the library makes use of method chaining, and while I personally like it, not everyone will. you don't HAVE to chain but the methods are declared such that the majority of them return 'this' and use output variables.

 

Below is a snippet that will launch 32-bit notepad and intercept the first call to SetWindowTextW:



static void ChangeTitle()
        {
            NIStartupOptions opts = new NonIntrusive.NIStartupOptions();
            opts.executable = @"c:\windows\system32\notepad.exe";
            opts.resumeOnCreate = false;             uint memoryCave; 
            uint oldValue;
            String oldString;
            debug.Execute(opts)
                .AllocateMemory(100, out memoryCave)
                .WriteString(memoryCave, "Welcome To NIDebugger", Encoding.Unicode)
                .SetProcBP("user32.dll","SetWindowTextW")
                .Continue()
                .ReadStackValue(8, out oldValue)
                .ReadString(oldValue, 100, Encoding.Unicode, out oldString)
                .WriteStackValue(8, memoryCave)
                .Detach();
                Console.WriteLine("Old String: " + oldString);
        }


 

The library also allows us to do interesting things like intercepting every call to SetWindowTextW:

 



 static void ChangeAllSetText()
        {
            NIStartupOptions opts = new NonIntrusive.NIStartupOptions();
            opts.executable = @"c:\windows\system32\notepad.exe";
            opts.resumeOnCreate = false;
 
            uint oldValue;
            String oldString;
            debug.Execute(opts)
                .AllocateMemory(100, out memoryCave)
                .WriteString(memoryCave, "Welcome To NIDebugger", Encoding.Unicode)
                .While(IsStillRunning,OverwriteText)
                .Detach();
        }
 
        public static bool IsStillRunning()
        {
            return debug.Process.HasExited == false;
        }
 
        public static void OverwriteText()
        {
            uint oldValue;
            String oldString = "";
 
            debug.SingleStep()
                .SetProcBP("user32.dll", "SetWindowTextW")
                .Continue()
                .ReadStackValue(8, out oldValue)
                .ReadString(oldValue, 100, Encoding.Unicode, out oldString)
                .WriteStackValue(8, memoryCave);
 
            Console.WriteLine("Old value: " + oldString);
        }


 

Take a look around if it interests you, and if you're a .net developer who wants to help improve it, fork it and make changes! Changes will be merged at the author's discretion :)

 

There is also an issue tracker through github, the author would appreciate putting anything you find (bugs, enhancement ideas) out there :)

Edited by Nieylana
  • Like 5
Link to comment

This is cool, and I might be using it soon, thank you!


 


One idea I've been toying with for a while is using INT3s instead of 0xEB 0xFE to set the hooks, and then catching the exceptions caused by the INT3 with a VectoredExceptionHandler in the child, which then somehow communicates back to the parent process (still thinking on the last part :P Named pipes communicating through an injected DLL?). Might build it using this if I do.


 


Sorry, just realized I'm rambling.


Cheers,


 


-rendari


Edited by rendari
Link to comment

This is cool, and I might be using it soon, thank you!

One idea I've been toying with for a while is using INT3s instead of 0xEB 0xFE to set the hooks, and then catching the exceptions caused by the INT3 with a VectoredExceptionHandler in the child, which then somehow communicates back to the parent process (still thinking on the last part :P Named pipes communicating through an injected DLL?). Might build it using this if I do.

Sorry, just realized I'm rambling.

Cheers,

-rendari

That's funny you mention such a thing look at the sources on GitHub for the x86 version. I have a new development branch called hwbp. It implements exactly what you mentioned. Injects a VEH into the thread and watches for single step exceptions for hwbp support!

I can be more detailed when I get back to PC

 

Edit:

 

Ok so i'm back at my PC... Basically NIDebugger keeps an internal list of NIBreakPoint objects that hold among other things the address for the breakpoint, threads in the target are run in short (~10ms) bursts, between each burst, every thread in the application has it's EIP scanned to see if it matches on of the NIBreakPoints in our list, this is basically how we know a BP has been hit and which thread we are working with.

 

To add HWBP support (which hasn't made it's way to master yet) I coded up a simple VEH (and installer method) in C++ that looks like this:

 

LONG WINAPI VectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo){if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP){PVOID address = ExceptionInfo->ExceptionRecord->ExceptionAddress;PCONTEXT context = ExceptionInfo->ContextRecord;_asm {push eaxpush ecxmov eax, addressmov ecx, context} _asm {singlestep:jmp singlesteppop ecxpop eax}ExceptionInfo->ContextRecord->EFlags |= 0x10000;return EXCEPTION_CONTINUE_EXECUTION;}else{return EXCEPTION_CONTINUE_SEARCH;}  }// method to install VEHvoid InstallHandler(){/ddVectoredExceptionHandler(1, VectoredHandler); }
Edited by Nieylana
Link to comment

OK, so how do we implement the VEH I posted above in a non-intrusive debugger, well I can detail how NIDebugger does it :)


 


We can outline the steps like such:


  1. Generate ASM Code for VEH
  2. Allocate Memory In Target
  3. Apply Fixups to ASM data
  4. Inject ASM data to memory
  5. Hijack EIP, place at Install location
  6. Run through Installation of VEH
  7. Restore EIP

 


Generate ASM Code for VEH and Installer


 


Easily done w/ VC++, though the Call to the AddVector api gets coded as a CALL DWORD PTR, and I want a CALL ADDRESS.. so we have to fix this in the ASM (ex, via olly)...


 


Allocate Memory in Target:


 


This is made easy by NIDebugger's AllocateMemory function.. simply call it w/ the size you need and an output parameter to take the uint address and boom :)


 


Apply Fixups to ASM data


 


There are a 2 fixups we need for the ASM data...


 


1.) Address of RtlAddVectoredExceptionHandler


2.) Address of VEH


 


Inject ASM data to memory


 


Done through APIs like WriteProcessMemory, or in NIDebugger's case WriteData()


 


Hijack EIP, move to Installer


 


Via GetThreadContext API or NIDebugger's Context property we can get/set EIP to the address of our Installation method (calls the RtlAddVector... method)


 


Restore EIP


 


We then set an EBFE Breakpoint using SetBreakpoint in NIDebugger at the end of the install method, this is used to restore EIP back to it's original value as if nothing happened :)


 


After this is all done, the VEH is set up for the thread... now the VEH code only cares about SINGLE_STEP exceptions (Hardware BPs, and Trap Flags), all other exceptions in passes on to the next handler... however for SINGLE_STEP exceptions we load the ExceptionRecord to EAX, and ContextRecord to ECX and then hang at an infinite loop... after the loop we restore ECX and EAX, then set the Resume Flag on the context so that HWBP doesn't double-trigger.


 


The next part was modifying NIDebugger's Continue() method.. to not only look for EIP's matching one of the registered breakpoints, but also EIP's matching the address of the EBFE inside the VEH...


 


If a thread's EIP matches the VEH EBFE, we know we've hit a HWBP... and that Context.Eax and Context.Ecx can be used to get Exception Info and Context Info (Context prior to the exception)...


 


Converting the Context Info into a Win32.CONTEXT Structure


 


So because ECX is holding the address in target's memory that this structure lies we need to get it with ReadProcessMemory (or ReadData in NIDebugger).. this yields a byte array, however I dont know a way to get a byte[] to become a Win32.Context... so we cheat a little...


 


IntPtr contextPtr = Marshal.AllocHGlobal(contextSize);
Marshal.Copy(contextData, 0, contextPtr, contextSize);
Win32.CONTEXT debugContext = (Win32.CONTEXT)Marshal.PtrToStructure(contextPtr, typeof(Win32.CONTEXT));
Marshal.FreeHGlobal(contextPtr);
Edited by Nieylana
Link to comment

Apparently tuts4you cuts off anything after a CODE tag... ugh...


 


This allocates unmanaged memory, copies the array to it, uses PtrToStructure to read from unmanaged memory into a struct and then frees the unmanaged memory...

 

This gives us all the information we really need (like EIP to see where we were when the BP hit, etc)... although soon you'll be able to use NIDebugger's "LastBreak" property to determine if SWBP or HWBP and the address, and which thread etc...

 

So there you have it, how NIDebugger implemented this without using pipes or any interprocess comms or injected DLLs... 

 

have fun!

Link to comment

Just some advice: please think about x64 and add support for it.


If you need help with C DLL/Shellcode, I can help.


Edited by Aguila
Link to comment

But why a separate project? I just looked at your c++ dll code and it is not useable in x64.

 

The 64 bit version of NIDebugger will be located in a completely different project.. at this time x64 doesn't support the HWBP stuff I was talking about in this thread. At such time that the x64 version of NIDebugger supports HWBPs it will have it's own VEH code... 

 

The C++ Code i pasted here was only for the x86 and was intended as such so I'm not surprised it wont work in x64...

 

as for why? because I figured most people would either need the x86 or the x64.. not both at the same time, and placing both x86 and x64 in the same project would require everyone to carry the weight of both debuggers even if they only used one of them.. with them seperate you can include whichever one you need into your project and only have to carry the weight of that one.

Edited by Nieylana
Link to comment

But why a separate project? I just looked at your c++ dll code and it is not useable in x64.

Just thought about this, if you are referring to the 'project' that my C++ code was in, there isn't one really... I just used it to get the ASM code... when I get around to x64 again I'll write code that will work for x64 and compile/rip the ASM for that too..

Link to comment

This was my point. Probably your overall design is bad, because you don't think about x64. Maybe your hwbp solution is bad, because it is not easy to do in x64. x64 and x86 in one project should not require much more code.


Link to comment

Generate ASM Code for VEH

  1. Allocate Memory In Target

Apply Fixups to ASM data

Inject ASM data to memory

Hijack EIP, place at Install location

Run through Installation of VEH

Restore EIP

 

Heya Nieylana,

 

Thanks for taking the time to tell me what you're doing :)

 

Instead of allocating memory/applying fixups/injecting memory/hijacking EIP... have you ever considered simply doing DLL injection? That way you just have to assemble a LoadLibrary() in a code cave in the remote process, and call it by doing CreateRemoteThread(). This also makes your ASM code more flexible and easier to maintain, since it's all in C++. You can also recompile the C++ to work in 64 bit with a minimum of changes. So in the interests of maintainability, this might be something you want to consider!

 

When I get the time I will look at what you're doing, and hopefully even contribute some of my own changes :) Now I gotta go review a lot of code at work! lol.

 

-rendari

Link to comment

Heya Nieylana,

 

Thanks for taking the time to tell me what you're doing :)

 

Instead of allocating memory/applying fixups/injecting memory/hijacking EIP... have you ever considered simply doing DLL injection? That way you just have to assemble a LoadLibrary() in a code cave in the remote process, and call it by doing CreateRemoteThread(). This also makes your ASM code more flexible and easier to maintain, since it's all in C++. You can also recompile the C++ to work in 64 bit with a minimum of changes. So in the interests of maintainability, this might be something you want to consider!

 

When I get the time I will look at what you're doing, and hopefully even contribute some of my own changes :) Now I gotta go review a lot of code at work! lol.

 

-rendari

 

I chose the way I did because I would like to have a single DLL that holds NIDebugger, not relying on anything else outside of that DLL. This is the same reason I ported LDASM.c to C# so that I wouldn't need a DLL with ldasm in it.

 

Also I've never made a DLL before :) I am not that familiar/good with C++.. getting what I had running was hard enough haha!

Link to comment

This was my point. Probably your overall design is bad, because you don't think about x64. Maybe your hwbp solution is bad, because it is not easy to do in x64. x64 and x86 in one project should not require much more code.

 

Design wasn't bad.. NIDebugger was built specifically for x86, and it's made clear on the project page... on a whim the other day I attempted to port over a lot of it to x64 (HWBP solution wasn't around then) and I got most of it working... so ya I didn't consider x64 years ago when NIDebugger started out in VB6 and I didn't think of it during this implementation because it was specifically for x86... 

 

As for the HWBP solution, if one of the projects goals is to have no dependent DLLs I think it's a fantastic solution that works wonderfully on x86... you can't say the x86 implementation is bad because it doesn't work on x64, that's dumb...

 

Should the x64 project come to fruition it will grow along it's own track. period. author's prerogative.

 

Using your criteria for what a 'bad' design is, I guess you could say that the implementation of notepad.exe was bad because they didn't consider OSX... no kidding they didn't consider OSX... it's different and wasn't their design goal.. nor was NIDebugger's goal (when it started)  x64 support.

 

If you don't like it, or think it's bad you are welcome to not use it.. I dont really need to justify my decisions here, the code is there if you want to use it, download it, if not go elsewhere...

 

I am however open to constructive criticism of the source via the issue tracker... 'ya that's a bad design because XYZ' isn't really constructive.

Edited by Nieylana
Link to comment

can you provide binaries, for those who dont have the .NET build tools installed?

 

I'll see if i can get some up today, though I dont know how much use they'll be if you dont have the tools to compile .NET apps, it's designed as a library to be used by other .NET applications. But yea, I'll see if I can't get a DLL up on the downloads page today.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...