Page 3 of 5 FirstFirst 12345 LastLast
Results 31 to 45 of 64

Thread: Ring 0 anti-debugger code in Daemon Tools?

  1. #31
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5
    Just thought I'd mention that both DT and the sptd driver (INIT section) show a very strong indication of running a Virtual Machine. I don't know that they are identical VM's but both are structured very similarly. Both are condusive to IDA analysis as well as live tracing.

    I figured there'd be at least a few people interested in exploring that further..

    Kayaker

  2. #32
    uhm... interesting

    from a very random and quick look, the dispatcher seems:
    .text:0003363E push ds: off_33D0A[eax*4]

    It looks like Code Virtualizer, looking the obfuscation kind.

    edit----
    had now the time to do few clicks more, and I found other CV patterns -it is, IMHO.
    (i.e. code that goes to .text:00031803 and follows it)
    Last edited by Maximus; September 27th, 2006 at 08:36.
    I want to know God's thoughts ...the rest are details.
    (A. Einstein)
    --------
    ..."a shellcode is a command you do at the linux shell"...

  3. #33
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5
    That's interesting Maximus. I didn't know the product existed, it looks pretty impressive (ouch - Oreans).

    I've made a few more attempts at trying to figure out how/why the sptd driver is not 'compatible' with Softice. It's now apparent the DriverEntry routine at least is wrapped in CV. I haven't done any testing to see if CV alone detects the presence of a kernel debugger, but it's apparent that one way or another sptd "reacts" to the presence of a KD and modifies it's behaviour.

    One of the things I noticed is that sptd uses PsSetLoadImageNotifyRoutine to detect when certain modules are loaded, notably ntdll and winlogon.exe. Under the 'modified' conditions I gave above of loading sptd *after* Sice you can trace this routine for debugging purposes.
    (Sidenote: Actually there is a jump table used and some addresses point to 'false' routines (i.e. a RET instead of the correct LoadImageNotifyRoutine callback), but the jump table can be 'corrected' to point to the proper (virgin) addresses).

    Anyway, under "goodboy" conditions, there exists a LoadImageNotifyRoutine Callback, all is rosy in the world of the app. Under "badboy" conditions, say you boot VMWare with WinDbg kernel debugger active, then this Callback is never created!

    So, not surprisingly, debugger detection leads to modified behaviour. Since this modified behaviour can be subtle or dramatic (i.e. no NotifyRoutine callback), and all occurs during boot loading, it would be just about impossible to "fix" the program after the fact.


    I'll explain how I determined this business with the LoadImageNotifyRoutine Callback since it's not something normally easily detected, especially without a kernel debugger.

    The PsSetLoadImageNotifyRoutine routine registers a driver-supplied callback that is subsequently notified whenever an image is loaded for execution. There exists a table of callbacks (maximum of 8), which are located at an unexported variable address in ntoskrnl, _PspLoadImageNotifyRoutine. Another variable holds the number of callbacks, _PspLoadImageNotifyRoutineCount. The callbacks themselves, if present, are executed through PsCallImageNotifyRoutines during process initialization, also at other times such as if ZwMapViewOfSection is used. A callback will be called for any usermode module or for a driver module not loaded from a system process.

    To determine if a _PspLoadImageNotifyRoutine callback table existed, under the conditions of not having a kernel debugger handy, I used two utilities. One is LiveKD. The other is a simple full-text kernel mode disassembler I wrote quite some time ago. This allowed me to disassemble a known exported function in order to find the address for the unexported _PspLoadImageNotifyRoutine callback table variable. Then I used LiveKD to view that address, confirming whether there was an active callback or not.


    First step was to understand what to look for in IDA with symbols loaded:
    Code:
    PAGE:005554F7   _PsSetLoadImageNotifyRoutine@4 proc near
    PAGE:005554F7     mov     edi, edi
    ...
    PAGE:00555517     mov     esi, offset _PspLoadImageNotifyRoutine
    The second step was to "live" disassemble this routine by name
    and find the address of the callback table:
    Code:
    >kdasm df PsSetLoadImageNotifyRoutine
    
    1   8062C4F7  8bff     mov edi,edi
    ...
    16  8062C517  be000d5680  mov esi,80560d00 // _PspLoadImageNotifyRoutine
    The third step was to display the _PspLoadImageNotifyRoutine address with LiveKD.
    The address is actually at _PspLoadImageNotifyRoutineCount, which is just before
    the callback table itself (8 dwords):
    Code:
    kd> dd 80560ce8
    
    80560ce8 00000001 00000000 00000000 00000000
    80560cf8 00000000 00000000 e139204f 00000000
    80560d08 00000000 00000000 00000000 00000000
    80560d18 00000000 00000000
    The "address" e139204f is not actually a direct address of the SPTD NotifyRoutine callback, but can be easily calculated from it. (In Win2K it would be a true address, but this changed slightly in XP and above).


    Now if you were to do the exact same thing with the WinDbg kernel debugger active, you would find that the SPTD driver never creates the above callback routine and the table would be empty. Bye bye normal operation..


    Anyway, hope that's a somewhat interesting tidbit, though it doesn't really accomplish a hell of a lot. If interested, the kernel mode disassembler I wrote is attached, I released it a couple of years ago at rootkit.com. It's based on the NASM disassembler, for years I've used that source code compiled into a usermode dll for disassembly projects, this time I compiled it into a kernel mode dll to give full-text kernel disassembly as well. It will disassemble by address or ntoskrnl function name. Full VC++ source and explanations included.


    Thanks again for the heads up on the VM!

    Cheers,
    Kayaker
    Attached Files Attached Files

  4. #34
    Quote Originally Posted by Kayaker
    PsSetLoadImageNotifyRoutine
    ..even if I desinstalled DDK, I know it I used it to 'fix' the Soviet protector (look at CodeProject) behaviour, that was using a bit... messy solution (checked sections load with hooks, not elegant IMHO, even if effective).
    PS* is the only truly viable way for intercepting the launch of an application, and gives you ALL the information you need for performing a r3 hook by debugger (why all this? can be... useful...)
    It costed me a week of research or so, and it is even decently documented in DDK -bah! Sometime I am really really dumb

    nice way to locate the callback lists...
    ..again, let me double Delta's assertion -you da r0 man

    ...also, I looked on virtual earth and sky a r0 disassembler for months, for another project I were discussing with my retired friend (sigh!) HAVOK- THANKS!

    When my WinXP (or Ware) will stand the DDK and debuggers again, I will hopefully make good use of it

    About CV...

    heh, An article on it was planned to be the my 2nd one, I even started to do a comparative analysis of CV machines, but I got request of both simpler and harder things on VMs... Also, I were discussing of this with a TheMida expert, OHPen, but we 'lost' for awhile -RL ...

    Hopefully I'll discuss with him again about my idea on a CV article

    This target sounds pretty interesting, so I guess I will give a better look to it... might be a good target for a comparative article for attack methods of Oreans VMs.

    Maximus
    Last edited by Maximus; September 27th, 2006 at 19:19.
    I want to know God's thoughts ...the rest are details.
    (A. Einstein)
    --------
    ..."a shellcode is a command you do at the linux shell"...

  5. #35
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5
    That would be an interesting paper Maximus. I still can't solve VM's but thanks to your earlier explanations I can at least recognize them!

  6. #36
    Kayaker
    Maximus

    I'm quite impressed.Hope that your efforts would have a positive result

  7. #37
    Super Moderator
    Join Date
    Dec 2004
    Posts
    1,487
    Blog Entries
    15
    nice disassembler there kayaker , could come in usefull when working with floppies dont need windbg installed isnt it ?
    btw it escapes on the first ret that it locates linearly isnt it ?
    or does it do control flow ?

    i mean is df == uf in windbg ?

    but i could just hint a solution for you if you want information with symbols
    if you are on xp where lkd is enabled by default in windbg you can use kd like this from command prompt

    you can pipe it to notepad for future referance


    Code:
    C:\Program Files\Debugging Tools for Windows>kd -kl -c "uf ntDeviceIoControlFile
    ; q"
    ====================
    snipped the version bs crap
    ====================
    lkd> kd: Reading initial command 'uf ntDeviceIoControlFile; q'
    nt!NtDeviceIoControlFile:
    8057fbd0 8bff            mov     edi,edi
    8057fbd2 55              push    ebp
    8057fbd3 8bec            mov     ebp,esp
    8057fbd5 6a01            push    1
    8057fbd7 ff752c          push    dword ptr [ebp+2Ch]
    8057fbda ff7528          push    dword ptr [ebp+28h]
    8057fbdd ff7524          push    dword ptr [ebp+24h]
    8057fbe0 ff7520          push    dword ptr [ebp+20h]
    8057fbe3 ff751c          push    dword ptr [ebp+1Ch]
    8057fbe6 ff7518          push    dword ptr [ebp+18h]
    8057fbe9 ff7514          push    dword ptr [ebp+14h]
    8057fbec ff7510          push    dword ptr [ebp+10h]
    8057fbef ff750c          push    dword ptr [ebp+0Ch]
    8057fbf2 ff7508          push    dword ptr [ebp+8]
    8057fbf5 e8dddbffff      call    nt!IopXxxControlFile (8057d7d7)
    8057fbfa 5d              pop     ebp
    8057fbfb c22800          ret     28h
    quit:
    
    C:\Program Files\Debugging Tools for Windows>
    or your real life example in one go

    Code:
    lkd> kd: Reading initial command 'uf PsSetLoadImageNotifyRoutine; dd 80560ce8 ; q'
    nt!PsSetLoadImageNotifyRoutine:
    8062c4f7 8bff            mov     edi,edi
    8062c4f9 55              push    ebp
    8062c4fa 8bec            mov     ebp,esp
    8062c4fc 53              push    ebx
    8062c4fd 57              push    edi
    8062c4fe 33ff            xor     edi,edi
    8062c500 57              push    edi
    8062c501 ff7508          push    dword ptr [ebp+8]
    8062c504 e833790100      call    nt!ExAllocateCallBack (80643e3c)
    8062c509 8bd8            mov     ebx,eax
    8062c50b 3bdf            cmp     ebx,edi
    8062c50d 7507            jne     nt!PsSetLoadImageNotifyRoutine+0x1f (8062c516)
    
    nt!PsSetLoadImageNotifyRoutine+0x18:
    8062c50f b89a0000c0      mov     eax,0C000009Ah
    8062c514 eb2a            jmp     nt!PsSetLoadImageNotifyRoutine+0x49 (8062c540)
    
    nt!PsSetLoadImageNotifyRoutine+0x1f:
    8062c516 56              push    esi
    8062c517 be000d5680      mov     esi,offset nt!PspLoadImageNotifyRoutine (80560d00)
    
    nt!PsSetLoadImageNotifyRoutine+0x25:
    8062c51c 6a00            push    0
    8062c51e 53              push    ebx
    8062c51f 56              push    esi
    8062c520 e84e790100      call    nt!ExCompareExchangeCallBack (80643e73)
    8062c525 84c0            test    al,al
    8062c527 751d            jne     nt!PsSetLoadImageNotifyRoutine+0x4f (8062c546)
    
    nt!PsSetLoadImageNotifyRoutine+0x32:
    8062c529 83c704          add     edi,4
    8062c52c 83c604          add     esi,4
    8062c52f 83ff20          cmp     edi,20h
    8062c532 72e8            jb      nt!PsSetLoadImageNotifyRoutine+0x25 (8062c51c)
    
    nt!PsSetLoadImageNotifyRoutine+0x3d:
    8062c534 53              push    ebx
    8062c535 e89134f4ff      call    nt!RtlpSysVolFree (8056f9cb)
    8062c53a b89a0000c0      mov     eax,0C000009Ah
    
    nt!PsSetLoadImageNotifyRoutine+0x48:
    8062c53f 5e              pop     esi
    
    nt!PsSetLoadImageNotifyRoutine+0x49:
    8062c540 5f              pop     edi
    8062c541 5b              pop     ebx
    8062c542 5d              pop     ebp
    8062c543 c20400          ret     4
    
    nt!PsSetLoadImageNotifyRoutine+0x4f:
    8062c546 b801000000      mov     eax,1
    8062c54b b9e80c5680      mov     ecx,offset nt!PspLoadImageNotifyRoutineCount (80560ce8)
    8062c550 0fc101          xadd    dword ptr [ecx],eax
    8062c553 c60534cf688001  mov     byte ptr [nt!PsImageNotifyEnabled (8068cf34)],1
    8062c55a 33c0            xor     eax,eax
    8062c55c ebe1            jmp     nt!PsSetLoadImageNotifyRoutine+0x48 (8062c53f)
    80560ce8  00000001 00000000 00000000 00000000
    80560cf8  00000000 00000000 e15d32bf 00000000
    80560d08  00000000 00000000 00000000 00000000
    80560d18  00000000 00000000 e15d15c7 00000000
    80560d28  00000000 00000000 00000000 00000000
    80560d38  00000000 00000000 00000001 00000000
    80560d48  00000000 00000000 00000000 00000000
    80560d58  00000000 00000000 e15d2d5f 00000000
    quit:
    Last edited by blabberer; September 30th, 2006 at 14:50.

  8. #38
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5
    Thanks blabberer. Yeah it could certainly be developed further. The only control flow it does at the moment is to follow any FAR JMP (opcode 0xEA) diversions it finds in order to trace inline hooks of that type. And yes it stops disassembly when it finds a RET/RETN/RETF/IRET.

    It would make sense to disassemble in the manner you showed. I guess what you'd have to do is to first find the RET, then go back and disassemble the full function, following each of the jumps which go to an address past the RET, or before the function start. Then position each of the function chunks in a logical order for output. Output can be piped to a text file in the normal console manner using "> output.txt".

    As for symbols, I never did get around to that but if I did I was thinking of using the relevant ntoskrnl or hal pdb file. "Function" recognition here is based on MmGetSystemRoutineAddress, which works only for ntoskrnl or hal functions.

    I don't know yet exactly how to work with symbol files and how best to use their information for updating a raw disassembly of this sort, but there seems to be good information in Sven Schriebers Undocumented Windows 2000 Secrets on the pdb format. There is also a PdbDump utility which could help (sourceforge), plus whatever can be gleaned from studying Windbg/Kd.


    I wouldn't mind seeing it developed further as a fully functional r0 disassembler, I haven't had time or inclination myself. The ring3 version I use, based on the same source code/disassembly module and part of a dll injection package, has a GUI interface and allows right clicking on call and jump addresses to further disassemble them in another window, also to display memory addresses and other stuff, and will disassemble linearly past RET's. The same thing could be done for a ring 0 version, gui instead of console output would make it more user friendly. And still all fit on a floppy!

    I thought about using it in an Olly plugin, but I'm not sure if that would be of much use. It could be mated with a utility which gets driver information such as IRP entry points and could allow disassembly of drivers (well it can now if you know the address). And of course the disasm source can be compiled directly into a driver, I only put in into a separate kernel module to keep any projects I use it in "clean" from all the disasm source code.

    Kayaker

  9. #39
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5

    Prologue PART 1

    Hello again,

    If you've been following this thread so far you know that the SPTD driver is "incompatible" with Softice. If you try to load Softice you get the very uninformative error message:
    The NTice service failed to start due to the following error:
    A device attached to the system is not functioning.


    I was curious to know *why* it wasn't loading, and to what extent this "incompatibility" was an honest conflict with the two drivers and how much was deliberate anti-debugging for all the usual reasons. I can answer the first part, the second part might still be open to speculation.

    Please bear with me if this is long-winded and I hold the answer till the end. I'm hoping the sequence of reversing steps taken and the code presented will be informative in a more generic way.


    Recall what I said about how a driver is loaded through ntoskrnl!IopLoadDriver and the sequence:

    Code:
    push dword ptr [ebp-0x0090]   // PUNICODE_STRING RegistryPath
    push edi                      // PDRIVER_OBJECT pDriverObject
    call [edi+0x2C]               // DriverEntry
    Prior to this the loading driver is mapped to memory via MmLoadSystemImage. Within the MiMapViewOfImageSection subfunction it checks the PsImageNotifyEnabled variable to see if any LoadImageNotifyRoutine callbacks have been registered with the system, if so it calls them in turn (to a maximum of 8). After MmLoadSystemImage is called the loading continues, a DRIVER_OBJECT is initialized and the driver is eventually loaded.

    A PspLoadImageNotifyRoutine callback allows a covert process to monitor, or indeed subvert, the loading module. An elegant rootkit technique actually. As I mentioned, SPTD registers a PspLoadImageNotifyRoutine callback...



    OK, back to attempting to load NTICE with SPTD active. Note that the boot drivers of Softice such as CptHook have already been normally loaded (though out of order because of SPTD), and it is within CptHook that the IDT entries are hooked and where the new hook functions for Int1 (single step), Int3 (breakpoint), Int0E (page fault), etc. reside. These are always active whether or not the user interface NTICE is loaded, including when SPTD is loaded.

    I'll briefly explain what didn't work as well as what did. So what I first did was to register my own PspLoadImageNotifyRoutine callback just to confirm that NTICE was in the process of being loaded and the code had gotten that far. You can check the definition for the function and see that obtaining the name and base address of the loading module is straightforward.

    Fine so far, what about within the NTICE DriverEntry (INIT) routine itself, how far along was that code executing? I mentioned about using PoolTag to probe if any ExAllocatePoolWithTag calls were being made. Turns out they weren't.

    OK, taking a closer look at the NTICE code you can see that it's nicely laid out with a number of error checks. If an error occurs during initialization it logs that to the system Event Log with IoWriteErrorLogEntry. The various error messages are given in the resource string message table you can see using i.e. ResHack. One of the first things NTICE does is to communicate with CptHook via driver IOCTL calls. If this step fails it records one of these error event messages. What I did was to hard patch (jnz nop) the ntice.sys file to force an Event to be logged - it wasn't!

    Wtf? It looked like even the first few instructions of the NTICE DriverEntry weren't being executed. What the heck was going on?

    Time to take the bull by the horns and get down 'n dirty to see what was happening..

  10. #40
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5

    PART 2

    What I decided to do then was to create a detour of the ntoskrnl driver loading code itself so I could disassemble things and monitor exactly what was going on. Here is the code I used for setting and removing the inline patch:

    Code:
    Data declarations:
    
    // Our Device Extension
    typedef struct _DEVICE_EXTENSION {
    	ULONG PatchAddress;
    	UCHAR OriginalPatchBytes[10];
    } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
    
    #pragma data_seg(".data")
    
    	PDEVICE_EXTENSION pDevExt;
    
    	ULONG DriverEntry_returnvalue;          // value normally returned from DriverEntry
    	ULONG ReturnAddress_DriverEntryDetour;  // return address from our Detour
    
    	// save original DriverEntry variables when we enter our Detour
    	PUNICODE_STRING pusRegistryPath_original;
    	PDRIVER_OBJECT pDriverObject_original;
    	ULONG DriverEntry_original;
    
    	const WCHAR DriverName_Ntice[] = L"\\Driver\\NTice";
    Code:
    The relevant IOCTL calls, as part of a normal DeviceControl function:
    
    //################################################################
    
    case IOCTL_SETHOOK:      // hook
    
    /* BYTES WE PATCH
    ntoskrnl!IopLoadDriver for WinXPsp2	
    805A69C9 FF B5 70 FF FF FF  push dword ptr [ebp-0x0090]   // PUNICODE_STRING RegistryPath
    805A69CF 57                 push edi                      // PDRIVER_OBJECT pDriverObject
    805A69D0 FF 57 2C           call [edi+0x2C]               // DriverEntry
    */
    
    	__try {	
    
    	UnProtectKernelMemory();      // remove write protection
    
    	pDevExt->PatchAddress = 0x805A69C9;
    	
    	// Calculate return address
    		ReturnAddress_DriverEntryDetour = pDevExt->PatchAddress + sizeof pDevExt->OriginalPatchBytes;
    
    	// Save original bytes
    	memcpy( pDevExt->OriginalPatchBytes,
    		(UCHAR*) (ULONG*)pDevExt->PatchAddress,
    		sizeof pDevExt->OriginalPatchBytes);	
    
    	////////////////////////////////////////////////////////////////
    	
    	     // we want to encode a FAR jump in the form
    	     // jmp FAR 0x0008:0xAAAAAAAA
    
    		UCHAR	FarJumpOpcode = 0xEA;
    		ULONG	NewHook;
    		USHORT	FarJumpSegment = 0x0008;
    		UCHAR	FarJumpNop = 0x90;
    
    		NewHook = (ULONG)&DriverEntryDetour;    // address of our naked function detour
    
              // encode FAR JUMP opcode (1 byte)
    		memcpy( (UCHAR*) (ULONG*)pDevExt->PatchAddress,
    		   &FarJumpOpcode,
    		   sizeof FarJumpOpcode);
    
              // encode new detour address (4 bytes)
    		memcpy( (UCHAR*) (ULONG*)pDevExt->PatchAddress + 1,
    		   &NewHook,
    		   4);
    
              // encode segment (2 bytes)
    		memcpy( (UCHAR*) (ULONG*)pDevExt->PatchAddress + 5,
    		   &FarJumpSegment,
    		   sizeof FarJumpSegment);	
    
              // encode at least one NOP opcode (fill remaining overwritten bytes)
    		for (int i=7; i < sizeof pDevExt->OriginalPatchBytes; i++)
    		{
    		     memcpy( (UCHAR*) (ULONG*)pDevExt->PatchAddress + i,
    		          &FarJumpNop,
    		          sizeof FarJumpNop);
    		}
    
    	////////////////////////////////////////////////////////////////
    		
    	ProtectKernelMemory();   // restore write protection
    
    
    	//=============================================================
    
    	} __except (EXCEPTION_EXECUTE_HANDLER)  {
    
    		DbgPrint ("HOOK: ERROR ExceptionCode: %x\n", GetExceptionCode() );
    
    	}	// end __try{	
    	
    
    	/****************************************************************/	
    	
    	//===========================================================
    	// Complete the IRP	
    	//===========================================================
    
    	IoStatus->Information = 1;
    	IoStatus->Status = STATUS_SUCCESS;	
    	
    	//===========================================================
    
        break;
        
    //################################################################
     
    //################################################################
    
    case IOCTL_REMOVEHOOK:	// unhook
    
    	/****************************************************************/	
    	
    	__try {	
    
    	UnProtectKernelMemory();
    
    	// Restore bytes
    
    	memcpy( (UCHAR*) (ULONG*)pDevExt->PatchAddress,
    		pDevExt->OriginalPatchBytes,
    		sizeof pDevExt->OriginalPatchBytes);		
    	
    	ProtectKernelMemory();
    
    	//=============================================================
    
    	} __except (EXCEPTION_EXECUTE_HANDLER)  {
    
    		DbgPrint ("HOOK: ERROR ExceptionCode: %x\n", GetExceptionCode() );
    
    	}	// end __try{	
    	
    	/****************************************************************/	
    
    	//===========================================================
    	// Complete the IRP	
    	//===========================================================
    
    	IoStatus->Information = 1;
    	IoStatus->Status = STATUS_SUCCESS;	
    	
    	//===========================================================
    
       break;
    
    //////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////
    
    /***********************************************************
    			UnProtectKernelMemory 
    	Allows writing to memory pages marked as read-only
    	by disabling the write protect (WP) bit of CR0
    ************************************************************/
    VOID UnProtectKernelMemory()
    {
    	__asm
    	{
            push eax
            mov  eax, CR0
            and  eax, 0FFFEFFFFh
            mov  CR0, eax
            pop  eax
    	}
    }
    /***********************************************************/
    
    /***********************************************************
    				ProtectKernelMemory
    	Reenables write protection of read-only memory pages
    ************************************************************/
    VOID ProtectKernelMemory()
    {
    	__asm
    	{
    		push eax
            mov eax, CR0
            or eax, NOT 0FFFEFFFFh
            mov CR0, eax
            pop eax
    	}
    }
    /***********************************************************/
    Here is our *basic* Detour function which does nothing except execute the overwritten instructions and return. Note that it is a 'naked' function for simplicity, there is no stack so any variables used must be global in nature.

    Code:
    __declspec(naked) DriverEntryDetour(){
    
    	///////////////////////////////////////////////
    
    	// Save the original DriverEntry parameters
    	// DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING RegistryPath)
    
    	_asm{
    		push dword ptr [ebp-0x0090]
    		pop pusRegistryPath_original
    
    		mov pDriverObject_original, edi
    
    		push dword ptr [edi+0x2C]
    		pop DriverEntry_original
    	}
    
    	///////////////////////////////////////////////
    
    	__asm
    	{
    		// Execute overwritten instructions		
    		push dword ptr [ebp-0x0090]
    		push edi
    		call [edi+0x2C]
    	}
    	
    	_asm{
    		mov DriverEntry_returnvalue, eax
    	}
    	
    	///////////////////////////////////////////////
    
    	///////////////////////////////////////////////
    
    	_asm{
    		mov eax, DriverEntry_returnvalue
    	}
    	
    	__asm{
    		push ReturnAddress_DriverEntryDetour
    		ret
    	}
    }
    continued..

  11. #41
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5

    PART 3

    What I did next was to modify the Detour to use the SysDasm full-text disassembler I introduced earlier to output the NTICE DriverEntry code as it existed just BEFORE it was called, and just AFTER it was called. I incorporated the disassembler code directly into the project instead of as a separate kernel driver module, I won't post the entire code but if anyone wants to see how to use it and produce nice DbgPrint output from it let me know.

    In order to understand the following code flow I'll explain now that SPTD overwrites the start of the NTICE DriverEntry with a detour of its own. It returns not to NTICE code but back to the normal return address in ntoskrnl!IopLoadDriver. In actual fact, it's returning to our own Detour, so we still have control over the code flow. Conveniently, SPTD restores the bytes it overwrites in NTICE, so what we can do is to simply call the NTICE DriverEntry again, for *real* this time. This effectively loads Softice, which SPTD tried so hard to prevent in the first place.

    Code:
    		
    __declspec(naked) DriverEntryDetour()
    {
    	///////////////////////////////////////////////
    
    	// Save the original DriverEntry parameters
    	// DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING RegistryPath)
    
    	_asm{
    		push dword ptr [ebp-0x0090]
    		pop pusRegistryPath_original
    
    		mov pDriverObject_original, edi
    
    		push dword ptr [edi+0x2C]
    		pop DriverEntry_original
    	}	
    
    	///////////////////////////////////////////////
    
    	// save context	
    	_asm pushad
    	_asm pushf
    	
    	// If NTICE is being loaded..
    
    	if(!_wcsnicmp(pDriverObject_original->DriverName.Buffer,
    				DriverName_Ntice,
    				pDriverObject_original->DriverName.Length/sizeof(WCHAR)))
    	{
    		// Output disassembly
    		
    		DbgPrint("NTICE DriverEntry is overwritten with SPTD detour	\n");
    		
    		DisasmAddress = (ULONG) pDriverObject_original->DriverInit;
    		NumInstructions = 5;
    
    		OutputDisasm(DisasmAddress, NumInstructions);
    		
    	} // end if NTICE
    
    	// restore context	
    	_asm popf
    	_asm popad
    	
    	///////////////////////////////////////////////
    		
    	__asm
    	{
    		// Execute overwritten instructions		
    		push dword ptr [ebp-0x0090]
    		push edi
    		call [edi+0x2C]     // DriverEntry
    	}
    
    	_asm{
    		mov DriverEntry_returnvalue, eax
    	}
    
    
    	// If NTICE is being loaded, call it for real after SPTD
    	// subverted its original loading
    
    	if(!_wcsnicmp(pDriverObject_original->DriverName.Buffer,
    				DriverName_Ntice,
    				pDriverObject_original->DriverName.Length/sizeof(WCHAR)))
    	{
    
    		///////////////////////////////////////////////
    
    		// Output disassembly
    
    		DbgPrint("NTICE DriverEntry has been restored \n");
    
    		DisasmAddress = (ULONG) pDriverObject_original->DriverInit;
    		NumInstructions = 5;
    
    		OutputDisasm(DisasmAddress, NumInstructions);
    
    		///////////////////////////////////////////////
    
    		__asm
    		{
    			// Execute DriverEntry again	
    			
    			push pusRegistryPath_original
    			push pDriverObject_original
    			call [DriverEntry_original]
    		}				
    				
    		_asm{
    			mov DriverEntry_returnvalue, eax
    		}
    	} // end if NTICE
    
    	///////////////////////////////////////////////
    
    	_asm{
    		mov eax, DriverEntry_returnvalue
    	}
    	
    	__asm{
    		push ReturnAddress_DriverEntryDetour
    		ret
    	}
    }
    If you followed all that, here is the DbgPrint output from my disassembler and captured with DbgView. You can clearly see the SPTD detour.

    Code:
    	
    NTICE DriverEntry is overwritten with SPTD detour	
    #   Address Length Opcode            Disassembly	
    *******************************************************	
    1   F7AAE8C6  5    E99D8F1008        jmp ffbb7868 	
    2   F7AAE8CB  6    008B84249805      add [ebx+5982484],cl 	
    3   F7AAE8D1  2    0000              add [eax],al 	
    4   F7AAE8D3  1    57                push edi 	
    5   F7AAE8D4  2    33FF              xor edi,edi 	
    *******************************************************
    
    NTICE DriverEntry has been restored	
    #   Address Length Opcode            Disassembly	
    *******************************************************	
    1   F7AAE8C6  6    81EC90050000      sub esp,590 	
    2   F7AAE8CC  7    8B842498050000    mov eax,[esp+598] 	
    3   F7AAE8D3  1    57                push edi 	
    4   F7AAE8D4  2    33FF              xor edi,edi 	
    5   F7AAE8D6  4    897C2404          mov [esp+4],edi 	
    *******************************************************
    holy crap, there's more?..

  12. #42
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5

    PART 4

    If we examine the SPTD detour address itself we see this interesting snippet:

    Code:
    	
    FFBB7868  B88877BBFF          mov eax, FFBB7788
    FFBB786D  870424              xchg eax, [esp]     // store return address (to ntoskrnl!IopLoadDriver)
    FFBB7870  50                  push eax            // push return address
    FFBB7871  68C6FCDCF9          push F9DCFCC6       // push further Detour code
    FFBB7876  C3                  ret                 // execute more Detour code
    I'm not going to get into the rest of the SPTD Detour code, partly because I'm not intending to expose it and mostly because I just don't understand it all. You can examine it with full ntoskrnl symbol support however because Softice is now functional. What it does just before returning to ntoskrnl code is, as I mentioned, restore the overwritten NTICE bytes, but it also passes 0xC0000001 as a return value. The detour itself and the STATUS_UNSUCCESSFUL return value is the whole reason why Softice does not load and gives the mundane error message.

    Other things.. I'm assuming SPTD overwrites the NTICE code during its LoadImageNotify callback, can't be anywhere else, but I haven't confirmed it.

    .. If you simple *prevent* the SPTD detour from executing and instead restore the NTICE bytes yourself then call the corrected DriverEntry.. Softice will load and function but will be unstable and crash eventually. One of the first things the SPTD detour code does is to call IoFreeMdl on a previously allocated MDL. I am presuming this MDL may have been allocated in the LoadImageNotify callback for some reason. This seems to indicate that the two code sections are interdependant.

    This does raise questions as to *what* is SPTD doing? If the idea is to simply not allow NTICE to load then the detour doesn't have to do anything except divert and return. Instead it handles MDL's, tests the Irql, flushes the instruction cache, and many other things I haven't figured out.


    Finally, one last, and major caveat! While Softice does seem to work now, I can breakpoint and trace relatively normally. Daemon Tools still works as well, I can map an iso drive and play a game which would normally requried a CD, etc. I did have some problems with Softice. For one, as soon as Softice was enabled I lost keyboard support in Windows (though it still worked in Softice itself). I have only run this under VMWare in XP and Win2K and have occasionally had similar problems when mucking too deeply in Softice anyway. I have not tried this on my "real" system so the problem might or might not show up there. Also, breakpoints in SPTD code itself (for example the LoadImageNotifyRoutine) seem to cause an immediate crash, deliberate or inadvertent I do not know.

    The bottom line is that my "solution" is probably not stable, is unlikely that it can be used for tracing SPTD code itself, and basically I wouldn't recommend it except for those adventurous souls who like to play around with this kind of stuff.. /end caveat


    One thing that did cross my mind.. the people who wrote this are very clever and have my full respect. I don't want to expose their work or make it easier for other "parties" to undermine their efforts. I do however feel it's important to be able to ascertain the extent of any kind of rootkit code, which I categorize this as, to be able to determine its intent, the DMCA notwithstanding. I was wondering though, back to *why* the detour code is so complex, I know you don't *need* a kernel debugger to write driver code, but how did these guys debug their code? I'm curious if, somewhere deep in the LoadImageNotify and Softice detour code, there exists a backdoor "switch" which allowed for debugging of the code during development? This is just a fanciful idea and probably not the case.

    Cheers,
    Kayaker

  13. #43
    Administrator dELTA's Avatar
    Join Date
    Oct 2000
    Location
    Ring -1
    Posts
    4,206
    Blog Entries
    5
    As always, you are the almighty ring 0 wizard Kayaker, and it's a joy to read about your excavations every time.

    About the little "master switch" idea at the end, my experience tells me that these things are normally implemented using compiler directives, not using runtime checks. So yes, I think they do indeed have such a master switch, but rather in the form of a compiler directive that enables/disables/replaces code in one or (more likely) several places before compilation, rather than a runtime flag check.

    But still, if just pinpointed (which is often 95% of the work in a reversing project of course), most protections like this one can be completely neutralized with just a few carefully crafted patches in the right places.

    And like you say, protecting your own code is one thing, but rootkitting the operating system and disabling other programs is another, which makes it quite legit to try to "fix these side effects" I think. I'll be looking forward to any other findings regarding this subject anyway.
    "Give a man a quote from the FAQ, and he'll ignore it. Print the FAQ, shove it up his ass, kick him in the balls, DDoS his ass and kick/ban him, and the point usually gets through eventually."

  14. #44
    I totally agree.
    Such feature must be disabled, and it should really be analized for discovering *why* they don't want sice on. Indeed, Syser loads fine
    I want to know God's thoughts ...the rest are details.
    (A. Einstein)
    --------
    ..."a shellcode is a command you do at the linux shell"...

  15. #45
    Quote Originally Posted by Kayaker
    I know you don't *need* a kernel debugger to write driver code, but how did these guys debug their code?
    They may have a hardware-based debugger, aka ICE (not soft ). You can debug almost anything with those. Even software-based debuggers like SoftICE; and see "anti-debug" in action. The thing is, they're prohibitively expensive.

Similar Threads

  1. Getting around anti-debugger code
    By REBlog in forum Blogs Forum
    Replies: 0
    Last Post: October 19th, 2007, 20:51
  2. Different papers about SMC, polymorph code and anti trace code...
    By OHPen in forum Advanced Reversing and Programming
    Replies: 7
    Last Post: March 29th, 2007, 15:45
  3. Ring 0 -> Ring 3 : Upward calls and downward returns theoretically possible?
    By Clandestiny in forum Advanced Reversing and Programming
    Replies: 9
    Last Post: December 9th, 2004, 19:50
  4. Does asprotect have anti-tracing code???
    By padawan in forum The Newbie Forum
    Replies: 2
    Last Post: February 23rd, 2004, 16:50
  5. Replies: 10
    Last Post: May 24th, 2003, 14:12

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •