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
Exploit Development
WinDbg
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