Wednesday, October 12, 2011

How to call other function manually in Debugger

Is it possible to call another function manually in the middle of the debugging? If we manipulate stack and jump to the function properly, it is doable. Here I am taking an example of calling a Win32 function in DebugBreak state. We are going to call RtlAdjustPrivilege() function which is in NTDLL.DLL from WinDBG here. We use x86 debugging here. (64 bit debugging is different; more information can be found at 64 bit calling convention)
In this example, I launched mspaint.exe from windbg.

C> windbg mspaint.exe

In the middle of the debugging, let's say I want to adjust one of privileges for the mspaint process.
To check current privileges status, !token command can be used.

0:008> !token -n
Thread is not impersonating. Using process token...
TS Session ID: 0x1
Privs: 
 00 0x000000005 SeIncreaseQuotaPrivilege          Attributes - 
 ......
 11 0x000000013 SeShutdownPrivilege               Attributes - 
 12 0x000000014 SeDebugPrivilege                  Attributes - Enabled 
 13 0x000000016 SeSystemEnvironmentPrivilege      Attributes - 
 14 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default 
 15 0x000000018 SeRemoteShutdownPrivilege         Attributes - 
 16 0x000000019 SeUndockPrivilege                 Attributes - 
 17 0x00000001c SeManageVolumePrivilege           Attributes - 
 18 0x00000001d SeImpersonatePrivilege            Attributes - Enabled Default 
 19 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default 
Auth ID: 0:4d37b
Impersonation Level: Anonymous
TokenType: Primary
Is restricted token: no.

Here, let's try to enable SeUndockPrivilege (0x19). To check current registers, r command is used. It is DbgBreak state at this point.

0:008> r
eax=7ffd6000 ebx=00000000 ecx=00000000 edx=77add23d esi=00000000 edi=00000000
eip=77a73540 esp=026ef9ec ebp=026efa18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77a73540 cc              int     3

Now in order to call RtlAdjustPrivilege() function in NTDLL.DLL, we first manipulate thread stack manually.

 NTSTATUS RtlAdjustPrivilege
 (
  ULONG    Privilege,
  BOOLEAN  Enable,
  BOOLEAN  CurrentThread,
  PBOOLEAN Enabled
 )

Since RtlAdjustPrivilege() API has 4 parameters, 4 parameter values should be pushed into the stack from 4th parameter to 1st parameter (due to calling convention). ESP points to current stack location. 4th parameter is located at ESP-8 (since 4th parameter is output pointer type, it is pointing to another memory address; in this case, ESP-4 memory address), 3rd parameter is at ESP-C, and so on. Once 4 parameters are added into stack, return address should be added. Since we want to return back to current point, the example sets ESP-18 to . (which means current address).

0:008> ed esp-4 0
0:008> ed esp-8 @esp-4
0:008> ed esp-c 0
0:008> ed esp-10 1
0:008> ed esp-14 19
0:008> ed esp-18 .
0:008> resp=@esp-18
0:008> r $ip=ntdll!RtlAdjustPrivilege

Now I set stack pointer (esp) to ESP-18 and set instruction pointer to ntdll!RtlAdjustPrivilege function. So all registers are set to go. Since current execution pointer is set to ntdll!RtlAdjustPrivilege, if we use 'uf .' command, WinDBG will show the whole RtlAdjustPrivilege function in assembly language.

0:008> uf .
ntdll!RtlAdjustPrivilege+0x9d:
77a45414 8b45e8          mov     eax,dword ptr [ebp-18h]
77a45417 d1e8            shr     eax,1
77a45419 2401            and     al,1
77a4541b e9b45a0000      jmp     ntdll!RtlAdjustPrivilege+0xa4 (77a4aed4)
.....

Now, to execute the RtlAdjustPrivilege function, run 'gu' command. gu command runs until the current function is complete.

0:008> gu
(16a0.ec0): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=5ef2406b edx=77a864f4 esi=00000000 edi=00000000
eip=77a73540 esp=026ef9e8 ebp=026efa18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77a73540 cc              int     3

0:008> r esp=@esp+4
0:008> r
eax=00000000 ebx=00000000 ecx=5ef2406b edx=77a864f4 esi=00000000 edi=00000000
eip=77a73540 esp=026ef9ec ebp=026efa18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77a73540 cc              int     3

Once the function is successfully run, we can check new privilege list by using !token command again. But how do we know the run is successful? Return value (NTSTATUS for the function) can be checked by looking at EAX register. Since it is 0, we can figure out that the function execution was successful.

0:008> !token -n
Thread is not impersonating. Using process token...
TS Session ID: 0x1
......
Privs: 
 00 0x000000005 SeIncreaseQuotaPrivilege          Attributes - 
 .......
 12 0x000000014 SeDebugPrivilege                  Attributes - Enabled 
 13 0x000000016 SeSystemEnvironmentPrivilege      Attributes - 
 14 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default 
 15 0x000000018 SeRemoteShutdownPrivilege         Attributes - 
 16 0x000000019 SeUndockPrivilege                 Attributes - Enabled 
 17 0x00000001c SeManageVolumePrivilege           Attributes - 
 18 0x00000001d SeImpersonatePrivilege            Attributes - Enabled Default 
 19 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default 

As you can see, SeUndockPrivilege is now enabled...

NOTE: setting SeUndockPrivilege might not be useful in real world, but enabling some other privileges such as TCB privilege or DEBUG privilege in the debugger might be useful in some situations.

No comments:

Post a Comment