Intro

A Windows oriented exploit development cheatsheet

WinDbg

  • local/remote kernel debugging
    setx _NT_SYMBOL_PATH srv*c:\symbols*HTTP://MSDL.MICROSOFT.COM/DOWNLOAD/SYMBOLS
    on the debuggee machine:
    bcdedit /debug on
    bcdedit /dbgsettings local
    OR if remote
    bcdedit /dbgsettings NET HOSTIP:[DEBUGGER_IP] port:50000 key:1.1.1.1
    OR bootloader/bootmanager debugging (only COM debugging works - no NET)
    bcdedit /bootdebug {bootmgr} on
    bcdedit /set bootdebug on
    bcdedit /dbgsettings serial debugport:1 baudrate:115200
  • set dynamic symbols fetching
    [win 7 and above]
    srv*c:\symbols*https://msdl.microsoft.com/download/symbols
    [win XP]
    srv*c:\mss*http://msdl.microsoft.com/download/symbols

  • attach/open process/file [invasive]
    cdb.exe -p ProcessID
    cdb.exe -pn FilenName.exe

  • attach/open process/file [non-invasive]
    cdb.exe -pv -p ProcessID
    cdb.exe -pv -pn FilenName.exe

  • break points
    bl - list all break points
    bc [bpID] - delete specific bp
    bp [functionName] - sets bp at functionName
    bp - [sets bp at current IP location]

  • execution controls
    g - go until next bp
    t - trace/execute til next instruction {step into a function}
    p - step/execute next instruction {step over a function}
    pt - step until next RET
    pct- step until next RET or CALL
    gu - go up until current function returns
    q - quit

  • [useful commands]
    x [module]![symbol] - Report the address of the specified symbols
    x *! - List all modules currently loaded
    x module!* - List all symbols and their addresses in the specified module
    x module!symbol* - List all of the symbols that match the “arg” wild-card filter
    lm - list all loaded modules (more detailed than x *!)
    !lmi [module] - list module info
    dt [symbol name] - display type of a symbol
    u - disassemble 8 instructions from current address
    u [address] - disassemble 8 from specified address
    u [start - end]
    uf routineName - diass specified route
    dd addressRange - display address range
    .formats [hex value] - display hex translation
    r - display registers

  • KD specific
    lm n - list loaded modules
    !process 0 0 - list all processes
    !process [Cid] 15 - get process details
    .process /r /p [Cid] - switch to process context

  • peek into PEB
    .process [base address]
    !peb

  • registers command
    0rM [0-255] - dump register details with 8-bit mask

  • quit kd
    bc *
    g
    <ctrl b> <enter>

DEP

Configuration

WinXP/2k3

/noexecute=policy [boot.ini]

Win Vista and above

bcdedit /enum all
bcdedit /set nx [one of the following options]
OptIn    : Enables DEP only for operating system components, including the Windows kernel and drivers. Administrators can enable DEP on selected executable files by using the Application Compatibility Toolkit (ACT).
OptOut   : Enables DEP for the operating system and all processes, including the Windows kernel and drivers. However, administrators can disable DEP on selected executable files by using System in Control Panel.<br>
AlwaysOn : Enables DEP for the operating system and all processes, including the Windows kernel and drivers. All attempts to disable DEP are ignored.
AlwaysOff: Disables DEP. Attempts to enable DEP selectively are ignored<br>

DEP-defeating APIs

API Supported OS
NtSetInformationProcess XP/2003 server
SetProcessDEPPolicy XP/Vista SP1/2008 server
Virtual Protect all Win versions
Virtual Alloc all Win versions
WriteProcessMemory all Win versions
LoadLibrary all Win versions
MapViewOfFile all Win versions

Shellcode

JUMPS and CALLS

JUMPS

JMP Type Example Machine Encoding
Short JMP SHORT mylabel 0xEB [signed byte]
Near direct JMP NEAR PTR mylabel 0xE9 [low byte] [high byte]
Near indirect JMP EBX 0xFF 0xE3
Far direct JMP DS:[mylabel] 0xEA [IP low] [IP high] [CS low] [CS high]
Far indirect JMP DWORD PTR[EBX] 0xFF 0x2F

CALLS

JMP Type Example Machine Encoding
Near direct CALL mylabel 0xE8 [low byte] [high byte]
Near indirect CALL EBX 0xFF 0xD3
Far direct CALL DS:[mylabel] 0x9A [IP low] [IP high] [CS low] [CS high]
Far indirect CALL DWORD PTR [EBX] 0xFF 0x1F
Near return RET 0xC3
Far return RETF 0XCB

Examples

Jump short  '\xEB\x06'             JMP 8 bytes ahead  (including start of instruction)
Jump near   '\xE9\xa3\xfd\xff\xff' JMP 600 bytes back (including start of instruction)          

Find Kernel32.dll with WinDBG

x86

Tested on latest Win10 Version 10.0.18362.113

  1. Find offset 0x30 from FS register [Pointer to PEB]

    0:000> dp fs:[30]  L1
    0053:00000030  02e68000
    
  2. Get the pointer to PEB_LDR_DATA

    02e680000:000> dt nt!_PEB Ldr Ldr. 02e68000
    ntdll!_PEB
    +0x00c Ldr  : 0x776ddca0 _PEB_LDR_DATAC
      +0x000 Length : 0x30
      +0x004 Initialized : 0x1 ''
      +0x008 SsHandle : (null)
      +0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x7ae6f58 - 0x838cf58 ]
      +0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x7ae6f60 - 0x838cf60 ]
      +0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x7ae2f68 - 0x7b3ef68 ]
      +0x024 EntryInProgress : (null)
      +0x028 ShutdownInProgress : 0 ''
      +0x02c ShutdownThreadId : (null)
    
  3. Get pointer to first entry in InMemoryOrderModuleList

    0:000> !list -t ntdll!_LIST_ENTRY.Flink -x "dd" -a "L1" 0x7ae6f60
    07ae6f60  07ae2f60 # ntdll.dll
    07ae2f60  07aeef60 # verifier.dll
    07aeef60  07afaf60 # kernel32.dll
    07afaf60  07b06f60 # kernelbase.dll
    
  4. Subtract the 8-byte linked-list data structure header.

    07afaf60 -0x8 = 7afaf58
    
  5. Get the base address from the third entry (it would normally be the 2nd entry!)

    0:000> dt ntdll!_LDR_DATA_TABLE_ENTRY 7afaf58
    +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x7b06f58 - 0x7aeef58 ]
    +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x7b06f60 - 0x7aeef60 ]
    +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x7b3ef68 - 0x7b06f68 ]
    +0x018 DllBase          : 0x772b0000 Void
    +0x01c EntryPoint       : 0x772c5f70 Void
    +0x020 SizeOfImage      : 0xe0000
    +0x024 FullDllName      : _UNICODE_STRING "C:\Windows\System32\KERNEL32.DLL"
    +0x02c BaseDllName      : _UNICODE_STRING "KERNEL32.DLL"
    
  6. Double check dll base address with one-liner

    0:00> !peb
      Base TimeStamp                     Module
          380000 C:\Program Files (x86)\Internet Explorer\iexplore.exe
        775c0000 C:\Windows\SYSTEM32\ntdll.dll
        6a8f0000 C:\Windows\SysWOW64\verifier.dll
        772b0000 12f36500 Jan 28 12:56:32 1980 C:\Windows\System32\KERNEL32.DLL
    
  7. The entire shellcode

    mov ebx, fs:0x30       ; Get pointer to PEB
    mov ebx, [ebx + 0x0C]  ; Get pointer to PEB_LDR_DATA
    mov ebx, [ebx + 0x14]  ; Get pointer to first entry in InMemoryOrderModuleList
    mov ebx, [ebx]         ; Get pointer to first  dll (ntdll.dll) entry in InMemoryOrderModuleList
    mov ebx, [ebx]         ; Get pointer to second dll (verifier.dll) entry in InMemoryOrderModuleList
    mov ebx, [ebx]         ; Get pointer to third  dll (kernel32.dll) entry in InMemoryOrderModuleList
    mov ebx, [ebx + 0x10]  ; Get kernel32.dll base address

x64

  1. Find the TEB address
0:000> !teb
TEB at 00000000003f3000

The TEB on x64 lies in the GS register instead of the FS that we have seen with x86 so far. Keep in mind that on long-mode, the TEB cannot be accessed via regular segmented registers, thus the gs is available only in kd.

The teb is stored in user_gs_base MSR register which can be read via rdmsr c0000102, as long as the thread is living in usermode and not swapped by SWAPGS yet (else the value would be IA32_KERNEL_GS_BASE aka the PCR).

1: kd> !teb
TEB at 000000275e30e000

1: kd> rdmsr c0000102
msr[c0000102] = 00000000'003f3000
  1. Find the PEB

    0:000> dt _TEB 00000000003f3000
    ntdll!_TEB
    +0x000 NtTib            : _NT_TIB
    +0x038 EnvironmentPointer : (null) 
    +0x040 ClientId         : _CLIENT_ID
    +0x050 ActiveRpcHandle  : (null) 
    +0x058 ThreadLocalStoragePointer : 0x00000000`003f3058 Void
    +0x060 ProcessEnvironmentBlock : 0x00000000`003f2000 _PEB
    
  2. Find the PEB_LDR_DATA

    0:000> dt _PEB_LDR_DATA  0x00007ffe`e31bb4c0
    ntdll!_PEB_LDR_DATA
    +0x000 Length           : 0x58
    +0x004 Initialized      : 0x1 ''
    +0x008 SsHandle         : (null) 
    +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x00000000`001f2bb0 - 0x00000000`001f37e0 ]
    +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x00000000`001f2bc0 - 0x00000000`001f37f0 ]
    +0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x00000000`001f2a00 - 0x00000000`001f3120 ]
    +0x040 EntryInProgress  : (null) 
    +0x048 ShutdownInProgress : 0 ''
    +0x050 ShutdownThreadId : (null) 
    
  3. Find the first entry in the list (own process)

    0:000> dt _LDR_DATA_TABLE_ENTRY  poi(0x00007ffe`e31bb4c0+20)
    ntdll!_LDR_DATA_TABLE_ENTRY
    +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000`001f29f0 - 0x00007ffe`e31bb4e0 ]
    +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
    +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000`00400000 - 0x00000000`00401000 ]
    +0x030 DllBase          : 0x00000000`00004000 Void
    +0x038 EntryPoint       : 0x00000000`00520050 Void
    +0x040 SizeOfImage      : 0x1f2728
    +0x048 FullDllName      : _UNICODE_STRING "CMD.EXE"
    
  4. Find the third entry (kernel32.dll)

    0:000> dt _LDR_DATA_TABLE_ENTRY  poi(poi(poi(0x00007ffe`e31bb4c0+20)))
    ntdll!_LDR_DATA_TABLE_ENTRY
    +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000`001f37f0 - 0x00000000`001f29f0 ]
    +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00007ffe`e31bb4f0 - 0x00000000`001f3800 ]
    +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00007ffe`e2dc0000 - 0x00007ffe`e2dd70d0 ]
    +0x030 DllBase          : 0x00000000`000bd000 Void
    +0x038 EntryPoint       : 0x00000000`00420040 Void
    +0x040 SizeOfImage      : 0x1f32d0
    +0x048 FullDllName      : _UNICODE_STRING "KERNEL32.DLL"
    
  5. The entire shellcode

    mov rax, [gs:0x60]     ; Get pointer to PEB
    mov rax, [rax+0x18]    ; Get pointer to PEB_LDR_DATA 
    mov rax, [rax+0x20]    ; Get pointer to first entry in InMemoryOrderModuleList
    mov rax, [rbx]         ; Get pointer to second (ntdll.dll) entry in InMemoryOrderModuleList
    mov rax, [rax]         ; Get pointer to third (kernel32.dll) entry in InMemoryOrderModuleList
    mov r12, [rax+0x20]    ; Get kernel32.dll base address

Heap

WinDBG command reference

!heap -stat                            # stats about process heaps usage
!heap -h <heap_addaress>               # heap + segments info
dt _HEAP <heap_address>                # show heap_base data structure
dt _HEAP_SEGMENT <segment_address>     # segment header
!heap -p -a <memory_address>           # show heap chunk info [user ptr]
!heap -stat -h <heap_address>          # show allocation size breakdown
!heap -flt s <size>                    # show heap allocations of given size
!heap -a -h <heap_address>             # show free list + all chunks
!heap -f <heap_address>                # show free list