xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



Exploit Development

WinDbg



Aunque a día de hoy existen muchos debuggers como lo son Immunity Debugger o x32dbg, algo que proporciona WinDbg a diferencia de estos es el soporte a arquitectura x64 además de que es capaz de depurar programas en "user-mode" tanto en "kernel-mode", sin contar que es un debugger desarrollado por el propio Microsoft, algo que lo diferencia de otros debuggers es que usa comandos



Basics


Antes de iniciar con comandos para analizar la memoria podemos aprender los comandos básicos, el comando g nos sirve simplemente para correr el programa

0:000> g  

Con el comando .restart podemos reiniciar la aplicación que estamos depurando

0:000> .restart
(1c48.229c): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=68130000 edx=00000000 esi=77276a68 edi=772769ec  
eip=77321a92 esp=02ddf51c ebp=02ddf548 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
ntdll!LdrpDoDebuggerBreak+0x2b:
77321a92 cc              int     3

El comando qd nos permite salir y desincronizarnos para dejar de depurar el proceso

windbg> qd  


Assemble


Usando el comando a podemos introducir instrucciones asm en una dirección especifica, al terminar de escribir las instrucciones terminamos con un enter

0:000> a esp
003df888 push esi
003df889 pop eax
003df88a mov esi,edi  
003df88c push eax
003df88d ret
003df88e

Con el comando u podemos visualizar las instrucciones asm que contiene una dirección especifica como por ejemplo el stack que acabamos de modificar

0:000> u esp
003df888 56              push    esi
003df889 58              pop     eax
003df88a 8bf7            mov     esi,edi  
003df88c 50              push    eax
003df88d c3              ret


Display


Para mostrar los bytes que contiene una dirección especifica podemos usar db para ver cada uno de los bytes y a la derecha su representación en ascii si existe

0:000> db esp
0043f464  2b 0e a2 36 ec 69 9c 77-68 6a 9c 77 00 00 00 00  +..6.i.whj.w....  
0043f474  b8 2e 48 00 64 f4 43 00-d1 bf a6 77 e4 f6 43 00  ..H.d.C....w..C.  
0043f484  30 af a3 77 7b 37 4d 41-00 00 00 00 f4 f6 43 00  0..w{7MA......C.  
0043f494  e2 bf a6 77 4f 0c a2 36-00 b0 38 00 00 00 00 00  ...wO..6..8.....  
0043f4a4  00 e0 38 00 3e 00 40 00-98 1d 48 00 28 f6 43 00  ..8.>.@...H.(.C.  
0043f4b4  00 00 00 00 01 02 00 00-00 00 00 00 24 f6 43 00  ............$.C.  
0043f4c4  00 00 00 00 f8 2d 48 00-94 62 48 00 00 5d ae 77  .....-H..bH..].w  
0043f4d4  00 00 00 00 e8 31 48 00-1a 00 00 00 00 00 00 00  .....1H.........  

De la misma forma podemos mostrar los bytes en words, dwords o qwords usando dw, dd y dq respectivamente, esto es bastante util para representar direcciones

0:000> dw esp
0043f464  0e2b 36a2 69ec 779c 6a68 779c 0000 0000  
0043f474  2eb8 0048 f464 0043 bfd1 77a6 f6e4 0043  
0043f484  af30 77a3 377b 414d 0000 0000 f6f4 0043  
0043f494  bfe2 77a6 0c4f 36a2 b000 0038 0000 0000  
0043f4a4  e000 0038 003e 0040 1d98 0048 f628 0043  
0043f4b4  0000 0000 0201 0000 0000 0000 f624 0043  
0043f4c4  0000 0000 2df8 0048 6294 0048 5d00 77ae  
0043f4d4  0000 0000 31e8 0048 001a 0000 0000 0000  

0:000> dd esp
0043f464  36a20e2b 779c69ec 779c6a68 00000000
0043f474  00482eb8 0043f464 77a6bfd1 0043f6e4
0043f484  77a3af30 414d377b 00000000 0043f6f4
0043f494  77a6bfe2 36a20c4f 0038b000 00000000
0043f4a4  0038e000 0040003e 00481d98 0043f628
0043f4b4  00000000 00000201 00000000 0043f624
0043f4c4  00000000 00482df8 00486294 77ae5d00
0043f4d4  00000000 004831e8 0000001a 00000000

0:000> dq esp
0043f464  779c69ec`36a20e2b 00000000`779c6a68
0043f474  0043f464`00482eb8 0043f6e4`77a6bfd1
0043f484  414d377b`77a3af30 0043f6f4`00000000
0043f494  36a20c4f`77a6bfe2 00000000`0038b000
0043f4a4  0040003e`0038e000 0043f628`00481d98
0043f4b4  00000201`00000000 0043f624`00000000
0043f4c4  00482df8`00000000 77ae5d00`00486294
0043f4d4  004831e8`00000000 00000000`0000001a

Por defecto dd muestra 32 dwords sin embargo esto podemos controlarlo usando L

0:000> dd esp L4
0043f464  36a20e2b 779c69ec 779c6a68 00000000  

Cuando en la dirección donde se hace el dd se encuentra un puntero como en este caso en esp + 4 podemos usar poi() para ver los dwords de ese puntero

0:000> dd poi(esp + 4) L4
779c69ec  6b6e696d 656e7265 746e5c6c 5c6c6c64  

Algo interesante es que aunque db lo hace dw y dd no muestran la representación en ascii a la derecha, para verla podemos usar dW y dc respectivamente

0:000> db kernel32
75de0000  4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00  MZ..............  
75de0010  b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00  ........@.......  
75de0020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................  
75de0030  00 00 00 00 00 00 00 00-00 00 00 00 e8 00 00 00  ................  
75de0040  0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68  ........!..L.!Th  
75de0050  69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f  is program canno  
75de0060  74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20  t be run in DOS   
75de0070  6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00  mode....$.......  

0:000> dW kernel32
75de0000  5a4d 0090 0003 0000 0004 0000 ffff 0000  MZ..............
75de0010  00b8 0000 0000 0000 0040 0000 0000 0000  ........@.......
75de0020  0000 0000 0000 0000 0000 0000 0000 0000  ................
75de0030  0000 0000 0000 0000 0000 0000 00e8 0000  ................
75de0040  1f0e 0eba b400 cd09 b821 4c01 21cd 6854  ........!..L.!Th
75de0050  7369 7020 6f72 7267 6d61 6320 6e61 6f6e  is program canno
75de0060  2074 6562 7220 6e75 6920 206e 4f44 2053  t be run in DOS 
75de0070  6f6d 6564 0d2e 0a0d 0024 0000 0000 0000  mode....$.......

0:000> dc kernel32
75de0000  00905a4d 00000003 00000004 0000ffff  MZ..............
75de0010  000000b8 00000000 00000040 00000000  ........@.......
75de0020  00000000 00000000 00000000 00000000  ................
75de0030  00000000 00000000 00000000 000000e8  ................
75de0040  0eba1f0e cd09b400 4c01b821 685421cd  ........!..L.!Th
75de0050  70207369 72676f72 63206d61 6f6e6e61  is program canno
75de0060  65622074 6e757220 206e6920 20534f44  t be run in DOS 
75de0070  65646f6d 0a0d0d2e 00000024 00000000  mode....$.......

Si solo quisieramos ver la representación ascii y no los bytes podemos usar da

0:000> da kernel32  
75de0000  "MZ."

Una ventaja de WinDbg es el poder representar estructuras especificas mostrando sus valores de acuerdo a esta, por ejemplo en el controlador de excepciones para hacerlo mas legible mostrando las direcciones del controlador y siguiente SEH

typedef struct _EXCEPTION_REGISTRATION_RECORD  
{
     PEXCEPTION_REGISTRATION_RECORD Next;
     PEXCEPTION_DISPOSITION Handler;
}

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD
   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD  
   +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION

Podemos pasarle la dirección del ExceptionList o la dirección donde esta la estructura para que muestre las direcciones de sus valores como Handler o Next

0:000> !teb
TEB at 02b5b000
    ExceptionList:        0056f280
    StackBase:            00570000
    StackLimit:           0055f000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 02b5b000
    EnvironmentPointer:   00000000
    ClientId:             00001534 . 0000161c
    RpcHandle:            00000000
    Tls Storage:          02d13180
    PEB Address:          02b58000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD 0056f280
   +0x000 Next             : 0x0056f4e4 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x772eaf30     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0  

También se puede indicar un campo especifico como en este caso puede serlo Handler o Next para que solo muestre la información de ese valor y no los demás

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD 0056f280 Handler
   +0x004 Handler : 0x772eaf30     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0  

Usando el parametro -r se mostrará la estructura de forma recursiva si lo permite

0:000> dt -r ntdll!_EXCEPTION_REGISTRATION_RECORD 0056f280 
   +0x000 Next             : 0x0056f4e4 _EXCEPTION_REGISTRATION_RECORD
      +0x000 Next             : 0x0056f53c _EXCEPTION_REGISTRATION_RECORD
         +0x000 Next             : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
         +0x004 Handler          : 0x772eaf30           _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0  
      +0x004 Handler          : 0x772eaf30        _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0
   +0x004 Handler          : 0x772eaf30     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0

Otra cosa que puede hacer es ver el tamaño de una estructura con sizeof, en este caso es 8 ya que la estructura se forma de solo 2 dwords de 4 bytes cada uno

0:000> ?? sizeof(ntdll!_EXCEPTION_REGISTRATION_RECORD)  
unsigned int 8


Enter


En el apartado anterior vimos algunas formas de mostrar la memoria, sin embargo tambien podemos introducir datos en ella, usando eb podemos escribir bytes

0:000> eb esp 41

0:000> db esp L2
0043f464  41 0e                                            A.  

Para escribir words o dwords, tendriamos que usar ew y ed respectivamente

0:000> ed esp 41414141

0:000> dd esp L2
0043f464  41414141 779c69ec  

Para escribir una cadena en ascii podemos usar ea pasandole el texto entre comillas

0:000> ea esp "Hello world!"  

0:000> da esp
0043f464  "Hello world!"

Muy similar a lo anterior podemos escribir en unicode usando eu y mostrarlo con du, solo cambia es la forma de escribirlo ya que utf-16 separa los bytes con un 00

0:000> eu esp "Hello world!"

0:000> du esp
0043f464  "Hello world!"

0:000> db esp L20
0043f464  48 00 65 00 6c 00 6c 00-6f 00 20 00 77 00 6f 00  H.e.l.l.o. .w.o.  
0043f474  72 00 6c 00 64 00 21 00-d1 bf a6 77 e4 f6 43 00  r.l.d.!....w..C.  


Search


Para buscar cadenas o bytes espeficicos tenemos el comando s que necesita algunos parametros, iniciando por el tipo de dato, en este caso como buscaremos un dword usamos -d, la dirección inicial que en este caso es 0, la dirección final donde usamos L?80000000 haciendo referencia a todo el espacio en memoria del proceso y finalmente el patron a buscar que es 41414141 que escribimos previamente

0:000> ed esp 41414141

0:000> s -d 0 L?80000000 41414141
00dff538  41414141 779c69ec 779c6a68 00000000  AAAA.i.whj.w....  

Dependiendo el dato a buscar podemos usar un parametro u otro, si quisieramos buscar cadenas ascii podemos usar -a, para unicode -u, y asi sucesivamente

0:000> s -a 0 L?80000000 "This program cannot be run in DOS mode"
75ae004e  54 68 69 73 20 70 72 6f-67 72 61 6d 20 63 61 6e  This program can  
75de004e  54 68 69 73 20 70 72 6f-67 72 61 6d 20 63 61 6e  This program can  
7618004e  54 68 69 73 20 70 72 6f-67 72 61 6d 20 63 61 6e  This program can  
779b004e  54 68 69 73 20 70 72 6f-67 72 61 6d 20 63 61 6e  This program can  
779c004e  54 68 69 73 20 70 72 6f-67 72 61 6d 20 63 61 6e  This program can  


Registers


La instrucción r nos muestra información sobre el contexto actual, todos los registros y la instrucción a la que apunta el registro eip que se va a ejecutar

0:000> r
eax=00000000 ebx=00000000 ecx=f5b70000 edx=00000000 esi=779c6a68 edi=779c69ec  
eip=77a71a92 esp=00dff538 ebp=00dff564 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
ntdll!LdrpDoDebuggerBreak+0x2b:
77a71a92 cc              int     3

Podemos consultar el valor de un registro especifico como ecx o si se requiere también el valor de una flag como lo puede ser zf que ahora mismo vale 1

0:000> r ecx
ecx=f5b70000  

0:000> r zf
zf=1

Además de consultar el valor del registro también se puede modificar usando =, en el siguiente ejemplo cambiamos el valor del registro ecx a 41414141

0:000> r ecx=41414141  

0:000> r ecx
ecx=41414141


Step


El debugger nos permite ejecutar instrucciones asm y analizar su comportamiento, para ejecutar una instrucción podemos usar el comando p y pasar a la siguiente

0:000> p
eax=00000000 ebx=00000000 ecx=be8e0000 edx=00000000 esi=779c6a68 edi=779c69ec  
eip=77a71a93 esp=0070fa08 ebp=0070fa34 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244  
ntdll!LdrpDoDebuggerBreak+0x2c:
77a71a93 eb07            jmp     ntdll!LdrpDoDebuggerBreak+0x35 (77a71a9c)

Similar al comando p el comando t avanza una instrucción pero lo que lo diferencia es que t cuando se hace una llamada entra en la función y p solo la la ejecuta

0:000> t
eax=00000000 ebx=00000000 ecx=75ef0000 edx=00000000 esi=779c6a68 edi=779c69ec  
eip=77a71a93 esp=005bf4d4 ebp=005bf500 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244  
ntdll!LdrpDoDebuggerBreak+0x2c:
77a71a93 eb07            jmp     ntdll!LdrpDoDebuggerBreak+0x35 (77a71a9c)

Si no queremos ir instrucción por instrucción podemos usar pt para avanzar hasta la siguiente instrucción ret o pc para avanzar hasta el siguiente instrucción call

0:000> pt
eax=00000000 ebx=00000000 ecx=912e89a0 edx=00000000 esi=779c6a68 edi=779c69ec  
eip=77a71ab2 esp=0070fa38 ebp=0070fc98 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
ntdll!LdrpDoDebuggerBreak+0x4b:
77a71ab2 c3              ret

0:000> pc
eax=00000000 ebx=00000000 ecx=912e89a0 edx=00000000 esi=779c6a68 edi=779c69ec  
eip=77a6bfe2 esp=0070fa3c ebp=0070fc98 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
ntdll!LdrpInitializeProcess+0x1c40:
77a6bfe2 e802bcfbff      call    ntdll!LdrpDropLastInProgressCount (77a27be9)


Breakpoints


Algo importante son los breakpoints que nos permiten parar en un punto especifico, como en una llamada a una función, para ello tenemos el comando bp en el cual podemos indicar una dirección y cuando el programa llegue ahi se detendrá, usando bl podemos ver todos los breakpoints que existen actualmente con su id

0:000> bp kernel32!WriteFile

0:000> bl
     0 e Disable Clear  75e03850     0001 (0001)  0:**** KERNEL32!WriteFile  

Usando bd podemos deshabilitar un breakpoint y con el comando be habilitarlo

0:000> bd 0

0:000> bl
     0 d Enable Clear  75e03850     0001 (0001)  0:**** KERNEL32!WriteFile  

0:000> be 0

0:000> bl
     0 e Disable Clear  75e03850     0001 (0001)  0:**** KERNEL32!WriteFile  

Con bc podemos eliiminar el breakpoint indicando su id o * para eliminar todos

0:000> bc 0  

0:000> bl

Cuando establecemos un breakpoint y corremos el programa este se detendrá cuando llegue a ese punto, en este caso WriteFile se llama cuando guardamos un archivo desde notepad.exe que estamos depurando y se detendrá el programa

0:000> bp kernel32!WriteFile

0:000> g
Breakpoint 0 hit
eax=0050ed48 ebx=00000000 ecx=00000007 edx=00000000 esi=00000007 edi=009a37e8
eip=75e03850 esp=0050ed28 ebp=0050ed5c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
KERNEL32!WriteFile:
75e03850 ff255c10e675    jmp     dword ptr [KERNEL32!_imp__WriteFile (75e6105c)] ds:002b:75e6105c={KERNELBASE!WriteFile (76299530)}  

Segun la documentación esta funcion consta de 5 argumentos que en el debugger se representan como 5 dwords los cuales podemos mostrar con dds, por ejemplo lpNumberOfBytesWritten en esp + 0xc indica la cantidad de bytes a escribir (7)

BOOL WriteFile(
  [in]                HANDLE       hFile,
  [in]                LPCVOID      lpBuffer,
  [in]                DWORD        nNumberOfBytesToWrite,
  [out, optional]     LPDWORD      lpNumberOfBytesWritten,  
  [in, out, optional] LPOVERLAPPED lpOverlapped
);

0:000> dds esp L5
0050ed28  00fbfe40 notepad!AnsiWriteFile+0xa0  
0050ed2c  00000488
0050ed30  009a37e8
0050ed34  00000007
0050ed38  0050ed48

Podemos establecer un breakpoint para cuando llegue a este muestre la cantidad de bytes a escribir con .printf, sabemos que esa cantidad esta en esp + 0xc, entonces cuando escribimos un archivo desde el notepad.exe mostrará en el debugger la cantidad de bytes que se estan escribiendo en el archivo

0:000> bc *

0:000> bp kernel32!WriteFile ".printf \"Number Of Bytes Written: %d\", poi(esp + 0xc); .echo; g"  

0:000> g
Number Of Bytes Written: 5
Number Of Bytes Written: 7
Number Of Bytes Written: 13


Modules


Usando el comando lm podemos ver todos los modulos que se han cargado para este programa, con lm m podemos buscar modulos con un patrón especifico

0:000> lm
start    end        module name
00fb0000 00fde000   notepad    (pdb symbols)          c:\symbols\notepad.pdb\21C3A2873E9F57AD16E58CEB784EEFDC1\notepad.pdb
73150000 73360000   COMCTL32   (deferred)             
75860000 75ae0000   combase    (deferred)             
75ae0000 75b9f000   msvcrt     (deferred)             
75de0000 75ed0000   KERNEL32   (pdb symbols)          c:\symbols\wkernel32.pdb\C326144F3CCFAE9FC9E07C0C1F71E8631\wkernel32.pdb
75ed0000 75ff0000   ucrtbase   (deferred)             
76180000 763ba000   KERNELBASE   (deferred)             
763f0000 764af000   RPCRT4     (deferred)             
765a0000 7661b000   msvcp_win   (deferred)             
76620000 767bc000   USER32     (deferred)             
77060000 77078000   win32u     (deferred)             
77080000 77107000   shcore     (deferred)             
77210000 772f8000   gdi32full   (deferred)             
777e0000 77804000   GDI32      (deferred)             
779c0000 77b64000   ntdll      (pdb symbols)          c:\symbols\wntdll.pdb\AE4A9CEFBA27BD00DEBC6271B9E3E4BE1\wntdll.pdb

0:000> lm m kernel*
Browse full module list
start    end        module name
75120000 75210000   KERNEL32   (pdb symbols)          c:\symbols\wkernel32.pdb\C326144F3CCFAE9FC9E07C0C1F71E8631\wkernel32.pdb  
76d30000 76f6a000   KERNELBASE   (deferred)

El comando x nos permite examinar los simbolos de un modulo usando un patrón

0:000> x kernel32!WriteFile*
75e03860          KERNEL32!WriteFileEx (_WriteFileEx@20)
75e03870          KERNEL32!WriteFileGather (_WriteFileGather@20)  
75e03850          KERNEL32!WriteFile (_WriteFile@20)

Otro comando interesante es !dh que nos mostrará con mas detalle la información de un modulo como la direccion base, sus secciones, permisos, offsets y demás

0:000> !dh kernel32

File Type: DLL
FILE HEADER VALUES
     14C machine (i386)
       6 number of sections
F56028CF time date stamp
       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
    2102 characteristics
            Executable
            32 bit word machine
            DLL

OPTIONAL HEADER VALUES
     10B magic #
   14.20 linker version
   65000 size of code
   33000 size of initialized data
       0 size of uninitialized data
   1F8E0 address of entry point
   10000 base of code
         ----- new -----
75120000 image base
   10000 section alignment
    1000 file alignment
       3 subsystem (Windows CUI)
   10.00 operating system version
   10.00 image version
   10.00 subsystem version
   F0000 size of image
    1000 size of headers
   A1F9A checksum
00040000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
    4140  DLL characteristics
            Dynamic base
            NX compatible
            Guard
   92CA0 [    DC60] address [size] of Export Directory
   A0900 [     780] address [size] of Import Directory
   D0000 [     520] address [size] of Resource Directory
       0 [       0] address [size] of Exception Directory
   99000 [    3BD0] address [size] of Security Directory
   E0000 [    482C] address [size] of Base Relocation Directory
   854D0 [      70] address [size] of Debug Directory
       0 [       0] address [size] of Description Directory
       0 [       0] address [size] of Special Directory
       0 [       0] address [size] of Thread Storage Directory
   80138 [      AC] address [size] of Load Configuration Directory
       0 [       0] address [size] of Bound Import Directory
   80B50 [    14E8] address [size] of Import Address Table Directory
   92ACC [      60] address [size] of Delay Import Directory
       0 [       0] address [size] of COR20 Header Directory
       0 [       0] address [size] of Reserved Directory


SECTION HEADER #1
   .text name
   64172 virtual size
   10000 virtual address
   65000 size of raw data
    1000 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60000020 flags
         Code
         (no align specified)
         Execute Read

SECTION HEADER #2
  .rdata name
   2A078 virtual size
   80000 virtual address
   2B000 size of raw data
   66000 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         (no align specified)
         Read Only

SECTION HEADER #3
   .data name
     C78 virtual size
   B0000 virtual address
    1000 size of raw data
   91000 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000040 flags
         Initialized Data
         (no align specified)
         Read Write
......................................................................


Calculator


Una función de windbg es la de calcular, esto se hace a través de la expresión ?, en el siguiente ejemplo se puede ver un ejemplo calculando el tamaño de kernel32 a partir de su dirección final y base, el resultado lo muestra en hex y en decimal

0:000> lm m kernel32
Browse full module list
start    end        module name
75120000 75210000   KERNEL32   (pdb symbols)  

0:000> ? 75ed0000 - 75de0000
Evaluate expression: 983040 = 000f0000

Por defecto ? asume que el valor que recibe es hexadecimal sin embargo podemos usar un prefix, como 0x para hexadecimal, 0n para decimal o 0y para binario

0:000> ? 41414141
Evaluate expression: 1094795585 = 41414141

0:000> ? 0x41414141
Evaluate expression: 1094795585 = 41414141

0:000> ? 0n1094795585
Evaluate expression: 1094795585 = 41414141

0:000> ? 0y01000001010000010100000101000001  
Evaluate expression: 1094795585 = 41414141

Usando .formats podemos ver el valor en todas sus posibles representaciones incluyendo todas las anteriores ademas de octal, ascii y algunas variantes más

0:000> .formats 41414141
Evaluate expression:
  Hex:     41414141
  Decimal: 1094795585
  Decimal (unsigned) : 1094795585
  Octal:   10120240501
  Binary:  01000001 01000001 01000001 01000001  
  Chars:   AAAA
  Time:    Thu Sep  9 23:53:05 2004
  Float:   low 12.0784 high 0
  Double:  5.40901e-315


Others


El comando !teb nos permite ver la informacion de la estructura TEB del hilo actual, esta contiene información como direcciones del stack o las excepciones

0:000> !teb
TEB at 00469000
    ExceptionList:        006af9f0
    StackBase:            006b0000
    StackLimit:           0069f000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 00469000
    EnvironmentPointer:   00000000
    ClientId:             000005e0 . 00000700  
    RpcHandle:            00000000
    Tls Storage:          02933180
    PEB Address:          00466000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0

Usando !exchain podemos listar la estructura SEH que se encarga de controlar las excepciones, de esta forma podemos ver a que excepción se saltará cuando ocurra

0:000> !exchain
006af9f0: ntdll!_except_handler4+0 (772eaf30)
  CRT scope  0, filter: ntdll!LdrpDoDebuggerBreak+2e (77321a95)
                func:   ntdll!LdrpDoDebuggerBreak+32 (77321a99)
006afc54: ntdll!_except_handler4+0 (772eaf30)
  CRT scope  0, func:   ntdll!LdrpInitializeProcess+1e57 (7731c1f9)  
006afcac: ntdll!_except_handler4+0 (772eaf30)
  CRT scope  0, filter: ntdll!_LdrpInitialize+3d9f8 (77313f3f)
                func:   ntdll!_LdrpInitialize+3da0b (77313f52)
Invalid exception stack at ffffffff

Para miras las protecciones que tiene una dirección podemos usar !vprot, por ejemplo el stack actualmente tiene permisos 0x4 que se traduce a READWRITE

0:000> !vprot esp
BaseAddress:       006af000
AllocationBase:    00670000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE  
Type:              00020000  MEM_PRIVATE

Aunque existen muchos más comandos para WinDbg los presentados anteriormente son de los mas comunes o funcionales a la hora de la explotación, sin embargo siempre se puede aprender más leyendo documentación y jugando con el debugger