Per mi, sempre m’ha semblat dolorós depurar programes en Windows, principalment perquè vinc acostumat de treballar en entorns UNIX. La queixa principal que tinc és que no hi ha bones terminals ni eines per la shell, malgrat powershell sigui una millora substancial en quant a consistència, no deixa d’estar bastant lluny de la usabilitat d’una terminal POSIX.
Amb totes les APIs NT amb noms confusos no ajuden a millorar l’experiència, a més està dissenyat per fer-lo anar de forma gràfica i no porto gaire bé això d’anar treient una ma del teclat cada dos per tres per moure el ratolí.
Per sort, Microsoft ha millorat el suport per a la línia d’ordres i la integració amb Unix. Ben entrat a 2020, van afegir suport C99 (estandard de C de 1999), han afegit WSL per defecte, han facilitat l’us de malware gràcies a l’scripting amb PowerShell i també han afegit moltes utilitats de línia d’ordres per administrar sistemes Windows a través de SSH.
Malgrat tot això, els usuaris de Windows estan acostumats a dependre d’aplicacions gràfiques que ocupen desenes de GB en disc i memòria, i triguen en arrencar, si, estic pensant en tu Visual Studio.. per accions com obtenir un backtrace.
Així doncs, m’agradaria compartir la meva experiència de tot plegat en aquest article on explicaré com vaig arreglar un error a Radare utilitzant només la consola CMD de Windows.
Sempre recomano compilar Radare2 des de Git, no només per
desenvolupar sino també per provar els ultims canvis. En sistemes Unix,
aquest és comú compilar-ho tot, i en radare2 és tan senzill com
./sys/install.sh (que executa
./configure+make+make symstall), però també hi ha suport
per a meson+ninja, que és el que s’utilitza a Windows.
Per simplificar tot això, Radare vé amb un grapat d’scripts batch pel
cmd de Windows que es troben al directori arrel del projecte. El primer
és preconfigure.bat, configure.bat i
make.bat. Executal’s en ordre per configurar l’entorn, el
project i compilar-lo.
El primer script configura tot l’entorn de compilació a Windows, busca la versió de Python, crea un entorn virtual per instal·lar meson i ninja, troba Visual Studio i el Windows Debugger SDK i ho posa tot al PATH perquè ho puguis utilitzar.
Jo sempre instal·lo vim, però si et conformes amb
edit.com no et jutjaré.
Quan executes make.bat, s’executarà Meson i Ninja per
compilar el projecte, col·locant els binaris finals, els fitxers pdb (la
versió de Windows de dwarf), les llibreries i els fitxers complementaris
al directori .\prefix.
Només has de fer cd .\prefix\bin i ja estàs a punt per
executar radare2.exe i rax2.exe per
provar-ho!
Just abans de cada llançament, intento provar radare tant com puc i en totes les plataformes i arquitectures imaginables, i malauradament, Windows també està en aquesta llista.
Les ordres de prova d’integració contínua, comportaments unitaris de l’API, assembladors, desassembladors, anàlisi de binaris i sistemes de fitxers “fuzzed”, però res d’això pot cobrir el que l’usuari final experimentaria en executar-lo, entrar en mode visual, utilitzar el depurador, etc.
Porta una estona compilar a mà a cada arquitectura, cada sistema
operatiu i provar-ho tot. El primer que vaig provar en aquesta
compilació va ser realitzar una anàlisi completa del binari
rax2.exe amb l’ordre aaaa. Com que això va
trigar molt, vaig prémer Ctrl+C i inesperadament vaig tornar a la línia
d’ordres del sistema.
Això significa que radare va sortir en lloc de cancel·lar l’anàlisi.
Espera, això no té cap sentit. Control-C no hauria de fer sortir del programa! Esperaria una finestra emergent amb un missatge de fallada, o un “Segmentation Fault” a la consola. Però els programes de Windows fallen tan sovint, que fer aquestes coses preocuparia els seus usuaris i tot això s’amaga!
Per saber què va passar, necessitem fer
echo %ERRORLEVEL%, per descobrir el codi de retorn del
programa. Sí, el número que la funció main retorna al sistema també
s’utilitza per informar sobre excepcions de fallada.
Hauria de limitar-se a aturar el procés perquè Radare en realitat està capturant l’esdeveniment de Control-C. Així que per entendre què passava, perquè el CMD de Windows no et dirà si el programa ha fallat, ha petat o simplement ha executat un exit. (Nota al marge: això es va solucionar a PowerShell, només ens estem divertint amb cmd.exe, recordeu?)
C:\radare2\prefix\bin> radare2.exe rax2.exe
C:\radare2\prefix\bin> echo %ERRORLEVEL%
-1073741819
I llavors obtens un número negatiu, que òbviament no té sentit. Traduir-lo a hexadecimal pot ajudar:
També podem aprendre d’altres números negatius comuns i fàcils de recordar:
$ rax2 -1073741819 -1073741510 -1073740791
0xc0000005
0xc000013a
0xc0000409
Per tant, en aquest cas, hem tingut una fallada de segmentació.
Ara sabem que va ser una fallada de segmentació, però no sabem on.
Per fer-ho, podem comprovar el directori de bolcats de fallades del
nostre usuari. Aquests fitxers .dmp són el mateix que els
coredumps al món UNIX.
C:\Users\pancake\AppData\Local\CrashDumps>copy radare2.exe.12964.dmp C:\users\pancake\prg\radare2\prefix\bin
Es pot llegir un resum dels registres del sistema utilitzant l’eina wevtutil:
C:\Users\pancake\prg\radare2\prefix\bin>wevtutil qe Application /q:"*[System[(Level=2) and (Provider[@Name='Application Error'])]]" /f:text /c:5
Listing package-relative application ID:
Event[2]
Log Name: Application
Source: Application Error
Date: 2025-11-11T11:44:42.9510000Z
Event ID: 1000
Task: Application Crashing Events
Level: Error
Opcode: Info
Keyword: N/A
User: S-1-5-21-3124587653-3600490378-1062075455-1001
User Name: WOP\pancake
Computer: wop
Description:
Faulting application name: radare2.exe, version: 0.0.0.0, time stamp: 0x69130e8f
Faulting module name: ucrtbased.dll, version: 10.0.26100.1, time stamp: 0xd920ed64
Exception code: 0xc0000409
Fault offset: 0x000000000009924c
Faulting process id: 0x48E0
Faulting application start time: 0x1DC52F823F57E9D
Faulting application path: C:\Users\pancake\prg\radare2\prefix\bin\radare2.exe
Faulting module path: C:\WINDOWS\SYSTEM32\ucrtbased.dll
Report Id: 9aac977b-32c7-4c72-992c-b34109ebed80
Faulting package full name:
Faulting package-relative application ID:
En aquest registre de fallada, veiem que es va executar el programa radare2.exe. Estava fallant a ucrtbased.dll, que és la libc de Windows, però no en sabem res més, ni backtrace, ni noms de símbols, res.
Això ens porta al següent pas lògic: la depuració.
Depurar a Windows és una experiència molt penosa. Perquè no tenim GDB/LLDB i les solucions x64dbg i VS depenen d’un dispositiu apuntador com un ratolí o un touchpad. El nostre cervell està connectat a un teclat i allunyar les mans fa mal.
Per sort per a mi, vaig descobrir CDB, el depurador de
consola del Windows Debug SDK, que es pot descarregar des de la pàgina
oficial de Microsoft:
Òbviament, Windows no posarà les coses fàcils, i cdb tampoc estarà al
PATH, però el preconfigure.bat de radare2 et cobrirà les
espatlles i ho farà per tu silenciosament.
CDB se sent com debug.com en els vells
temps de DOS. És directe, clar i fàcil d’utilitzar. Amb ordres
mnemotècniques per executar accions i extensible a través de scripts com
!analyze, és capaç de carregar bolcats de fallades, llançar
programes, adjuntar-se a processos, mostrar backtraces i gestionar
excepcions i coses per l’estil.
Aquestes són les ordres comunes que necessites utilitzar per llistar processos o adjuntar/llançar per depurar:
psEsdeveniments d’excepció, així que fins i tot si li diem a cdb que ignori l’esdeveniment ^C, simplement el captura i imprimeix un missatge, però l’excepció nul·la no s’activa… haurem de comprovar el bolcat de fallada.
El primer que vaig intentar va ser llançar Radare dins de CDB i
prémer Ctrl+C, però això em va tornar a la consola de CDB,
perquè el depurador va capturar l’esdeveniment d’excepció. Un
comportament similar tenim a GDB/LLDB/R2 quan depurem en el mateix
terminal.
Així que vaig provar d’adjuntar, iniciant radare2 en una consola
cmd.exe separada, agafant el PID amb tasklist
i adjuntant amb cdb -p pid, però aquesta vegada estava
preparat per ignorar l’esdeveniment CTRLC utilitzant l’ordre
sxn:
0:001> sxn c000013a
0:001> g
(d04.920): Control-C exception - code 40010005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
KERNELBASE!CtrlRoutine+0x1c4:
00007fff`bfe8c254 0f1f440000 nop dword ptr [rax+rax]
0:001> sxn 40010005
0:001> g
(d04.1f94): Control-C exception - code 40010005 (first chance)
(d04.27a0): Control-C exception - code 40010005 (first chance)
(d04.4544): Control-C exception - code 40010005 (first chance)
(d04.49c8): Control-C exception - code 40010005 (first chance)
(d04.4c28): Control-C exception - code 40010005 (first chance)
(d04.ef0): Control-C exception - code 40010005 (first chance)
(d04.35b8): Control-C exception - code 40010005 (first chance)
(d04.1674): Control-C exception - code 40010005 (first chance)
ModLoad: 00007fff`be540000 00007fff`be55b000 C:\WINDOWS\SYSTEM32\kernel.appcore.dll
ntdll!NtTerminateProcess+0x14:
00007fff`c2782174 c3 ret
L’ordre sxn bàsicament et permet saltar el gestor d’excepcions, el procés no es va interrompre, i el depurador m’informava de l’esdeveniment, però la fallada no va passar, el que significa que el gestor d’excepcions original no es va executar. Va ser un enfocament divertit i inútil.
Anem a provar l’enfocament d’anàlisi post-mortem. Utilitzant
l’indicador -z de cdb puc carregar el fitxer
.dmp:
C:\Users\pancake\prg\radare2\prefix\bin>cdb -z radare2.exe.15500.dmp
************* Preparing the environment for Debugger Extensions Gallery repositories **************
ExtensionRepository : Implicit
UseExperimentalFeatureForNugetShare : true
AllowNugetExeUpdate : true
NonInteractiveNuget : true
AllowNugetMSCredentialProviderInstall : true
AllowParallelInitializationOfLocalRepositories : true
EnableRedirectToV8JsProvider : false
-- Configuring repositories
----> Repository : LocalInstalled, Enabled: true
----> Repository : UserExtensions, Enabled: true
>>>>>>>>>>>>> Preparing the environment for Debugger Extensions Gallery repositories completed, duration 0.015 seconds
************* Waiting for Debugger Extensions Gallery to Initialize **************
>>>>>>>>>>>>> Waiting for Debugger Extensions Gallery to Initialize completed, duration 0.016 seconds
----> Repository : UserExtensions, Enabled: true, Packages count: 0
----> Repository : LocalInstalled, Enabled: true, Packages count: 30
Microsoft (R) Windows Debugger Version 10.0.26100.7175 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
Loading Dump File [C:\Users\pancake\prg\radare2\prefix\bin\radare2.exe.15500.dmp]
User Mini Dump File: Only registers, stack and portions of memory are available
Symbol search path is: srv*
Executable search path is:
Windows 10 Version 26200 MP (16 procs) Free x64
Product: WinNt, suite: SingleUserTS
Edition build lab: 26100.1.amd64fre.ge_release.240331-1435
Debug session time: Sat Nov 22 12:17:15.000 2025 (UTC + 1:00)
System Uptime: 10 days 22:20:51.327
Process Uptime: 0 days 0:03:35.000
........................................................
WARNING: Teb 1 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(3c8c.4054): Access violation - code c0000005 (first/second chance not available)
WARNING: Teb 1 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
For analysis of this file, run !analyze -v
WARNING: Teb 1 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
00000000`00000000 ?? ???
0:001> kb
WARNING: Teb 1 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
RetAddr : Args to Child : Call Site
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
0:001> r
rax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=0000000000000000 rsp=0000000000000000 rbp=0000000000000000
r8=0000000000000000 r9=0000000000000000 r10=0000000000000000
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up di pl nz na pe nc
cs=0000 ss=0000 ds=0000 es=0000 fs=0000 gs=0000 efl=00000000
00000000`00000000 ?? ???
0:001>
Molta més informació de la que esperava, i en realitat bastant inútil perquè tots els valors dels registres són zero, i de nou cap backtrace. Suposo que m’he saltat un pas perquè Windows ha de ser frustrant per defecte, així que millor fer una pausa i aprendre una mica de CDB:
Aquestes són algunes de les ordres més comunes que necessitarem:
Teclejant l’ordre ? ens donarà una llista més
completa:
0:001> ?
Obre debugger.chm per a la documentació completa del depurador
B[C|D|E][<bps>] - esborrar/desactivar/activar punt(s) de ruptura
BL - llistar punts de ruptura
BA <access> <size> <addr> - establir punt de ruptura de processador
BP <address> - establir punt de ruptura de programari
D[type][<range>] - bolcar memòria
DT [-n|y] [[mod!]name] [[-n|y]fields]
[address] [-l list] [-a[]|c|i|o|r[#]|v] - bolcar usant informació de tipus
DV [<name>] - bolcar variables locals
DX [-r[#]] <expr> - mostrar expressió C++ usant el model d'extensió (p. ex.: NatVis)
E[type] <address> [<values>] - introduir valors de memòria
G[H|N] [=<address> [<address>...]] - anar
K <count> - traça de pila
KP <count> - traça de pila amb arguments de codi font
LM[k|l|u|v] - llistar mòduls
LN <expr> - llistar símbols més propers
P [=<addr>] [<value>] - pas per sobre
Q - sortir
R [[<reg> [= <expr>]]] - veure o establir registres
S[<opts>] <range> <values> - cercar a la memòria
SX [{e|d|i|n} [-c "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}] - filtre d'esdeveniments
T [=<address>] [<expr>] - traçar dins
U [<range>] - desassemblar
version - mostrar versió del depurat i depurador
X [<*|module>!]<*|symbol> - veure símbols
? <expr> - mostrar expressió
?? <expr> - mostrar expressió C++
$< <filename> - prendre l'entrada d'un fitxer d'ordres
<expr> operadors unaris: + - not by wo dwo qwo poi hi low
operadors binaris: + - * / mod(%) and(&) xor(^) or(|)
comparacions: == (=) < > !=
operands: número en la base actual, símbol públic, <reg>
<type> : b (byte), w (word), d[s] (doubleword [amb símbols]),
a (ascii), c (dword i Char), u (unicode), l (llista)
f (float), D (double), s|S (cadena ascii/unicode)
q (quadword)
<pattern> : [(nt | <dll-name>)!]<var-name> (<var-name> pot incloure ? i *)
<range> : <address> <address>
: <address> L <count>
Opcions de mode d'usuari:
~ - llistar l'estat dels fils
~#s - establir fil per defecte
| - llistar l'estat dels processos
|#s - establir procés per defecte
Opcions x64:
DG <selector> - bolcar selector
<reg> : [r|e]ax, [r|e]bx, [r|e]cx, [r|e]dx, [r|e]si, [r|e]di, [r|e]bp, [r|e]sp, [r|e]ip, [e]fl,
r8-r15 amb subregistres b/w/d
al, ah, bl, bh, cl, ch, dl, dh, cs, ds, es, fs, gs, ss
sil, dil, bpl, spl
dr0, dr1, dr2, dr3, dr6, dr7
fpcw, fpsw, fptw, st0-st7, mm0-mm7
xmm0-xmm15
<flag> : iopl, of, df, if, tf, sf, zf, af, pf, cf
<addr> : #<adreça de mode protegit de 16 bits [seg:]>,
&<adreça de mode V86 [seg:]>
Obriu debugger.chm per a la documentació completa del depurador
0:001>
Aquesta mala bèstia també suporta plugins. Hi ha el plugin “!analyze” que bàsicament executa un grapat d’ordres i analitza el codi i el backtrace per mostrar-te un informe útil que pots fer servir per entendre per què el programa ha fallat. La qüestió és que, com era d’esperar, aquest script d’anàlisi és bàsicament inútil.
Malauradament, no mostra res més que la desreferència nul·la i un munt de missatges repetitius inútils que no diuen res. Així que hauré de cavar una mica més.
0:003> !analyze -v
WARNING: Teb 3 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
ERROR: FindPlugIns 8007007b
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
WARNING: Teb 3 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
WARNING: Teb 3 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
WARNING: Teb 3 pointer is NULL - defaulting to 00000000`7ffde000
WARNING: 00000000`7ffde000 does not appear to be a TEB
Com que Windows i tot l’entorn de depuració són pura merda, Cdb ve
amb una ordre excrement (.excr). Que bàsicament carrega
l’últim estat de fallada d’excepció a la sessió en curs, permetent-nos
finalment veure informació útil. Gràcies a això, obtens els valors dels
registres, l’adreça del contingut del programa, i bàsicament pots
analitzar i desassemblar.
Bé, en realitat unassemble perquè van utilitzar l’ordre
U per desassemblar, ja que la D estava ocupada per als bolcats
hexadecimals. I acabem descobrint que aquesta instrucció
mov-rcx és la que està causant la fallada, i tot està
fallant dins de la llibreria r_cons.
0:003> .excr
rax=0000000000000000 rbx=0000000000000000 rcx=000001ed3111e6c0
rdx=000001ed3112d3b0 rsi=0000000000000000 rdi=0000000000000001
rip=00007fff912bb796 rsp=0000003875dff640 rbp=0000000000000000
r8=000001ed30f70040 r9=0000003875adf000 r10=0000000000000000
r11=0000000000000246 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
r_cons+0xb796:
00007fff`912bb796 488b08 mov rcx,qword ptr [rax] ds:00000000`00000000=????????????????
0:003> kb
*** Stack trace for last set context - .thread/.cxr resets it
RetAddr : Args to Child : Call Site
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : r_cons+0xb796
0:003> kp
*** Stack trace for last set context - .thread/.cxr resets it
Child-SP RetAddr Call Site
00000038`75dff640 00000000`00000000 r_cons+0xb796
0:003> kv
*** Stack trace for last set context - .thread/.cxr resets it
Child-SP RetAddr : Args to Child
Tenim el valor de RIP, finalment! Comprovem el desassemblat i
comparem-ho amb el binari per esbrinar el nom de la funció i la línia de
codi font associada. Perquè… endevina què, encara que els fitxers
.pdb estiguin al mateix directori que els executables,
cdb no els carregarà si no especifiquem la ruta amb l’ordre
.sympath i després executem l’ordre .reload.
Però ja soc gran per a tot això, així que simplement llegiré
l’assemblador.
0:003> u rip
r_cons+0xb796:
00007fff`912bb796 488b08 mov rcx,qword ptr [rax]
00007fff`912bb799 e8c2c2ffff call r_cons+0x7a60 (00007fff`912b7a60)
00007fff`912bb79e 90 nop
00007fff`912bb79f 4883c428 add rsp,28h
00007fff`912bb7a3 c3 ret
00007fff`912bb7a4 cc int 3
00007fff`912bb7a5 cc int 3
00007fff`912bb7a6 cc int 3
0:003> kb
*** Stack trace for last set context - .thread/.cxr resets it
RetAddr : Args to Child : Call Site
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : r_cons+0xb796
0:003> u rip-10
r_cons+0xb786:
00007fff`912bb786 488b142558000000 mov rdx,qword ptr [58h]
00007fff`912bb78e 488b0cca mov rcx,qword ptr [rdx+rcx*8]
00007fff`912bb792 488b0401 mov rax,qword ptr [rcx+rax]
00007fff`912bb796 488b08 mov rcx,qword ptr [rax]
00007fff`912bb799 e8c2c2ffff call r_cons+0x7a60 (00007fff`912b7a60)
00007fff`912bb79e 90 nop
00007fff`912bb79f 4883c428 add rsp,28h
00007fff`912bb7a3 c3 ret
0:003>
Ara tenim un bon punt de partida! Sabem:
mov rcx, qword ptr[rax]r_cons+0xb786La funció ni tan sols té un nom, i no tenim informació de la línia de codi font, però almenys sabem l’adreça base on es va mapejar la llibreria i les instruccions afectades. Anem a r2 per descobrir més detalls!
C:\Users\pancake\prg\radare2\prefix\bin> radare2.exe r_cons.dll
[0x180057df0]> /x 488b142558000000488b0cca488b0401488b08
0x18000b7c7 hit0_0 488b142558000000488b0cca488b0401488b08
[0x180057df0]> s hit0_0
Pujant amunt trobem l’inici de la funció, que no té nom:
[0x18000b790]> pdf
/ 85: fcn.18000b790 (int64_t arg1);
| `- args(rcx) vars(1:sp[0x20..0x20])
| 0x18000b790 894c2408 mov dword [var_8h], ecx ; arg1
| 0x18000b794 4883ec28 sub rsp, 0x28
| 0x18000b798 b820000000 mov eax, 0x20 ; 32
| 0x18000b79d 8bc0 mov eax, eax
| 0x18000b79f 8b0dab190600 mov ecx, dword [0x18006d150] ; [0x18006d150:4]=0
| 0x18000b7a5 65488b1425.. mov rdx, qword gs:[0x58]
| 0x18000b7ae 488b0cca mov rcx, qword [rdx + rcx*8]
| 0x18000b7b2 48833c0800 cmp qword [rax + rcx], 0
| ,=< 0x18000b7b7 7427 je 0x18000b7e0
| | 0x18000b7b9 b820000000 mov eax, 0x20 ; 32
| | 0x18000b7be 8bc0 mov eax, eax
| | 0x18000b7c0 8b0d8a190600 mov ecx, dword [0x18006d150] ; [0x18006d150:4]=0
| | 0x18000b7c6 ~ 65488b1425.. mov rdx, qword gs:[0x58]
| | ;-- hit0_0:
..
| | 0x18000b7cf 488b0cca mov rcx, qword [rdx + rcx*8]
| | 0x18000b7d3 488b0401 mov rax, qword [rcx + rax]
| | 0x18000b7d7 488b08 mov rcx, qword [rax]
| | 0x18000b7da e881c2ffff call sym.r_cons.dll_r_cons_context_break
| | 0x18000b7df 90 nop
| `-> 0x18000b7e0 4883c428 add rsp, 0x28
\ 0x18000b7e4 c3 ret
[0x18000b790]>
Però… qui la crida? Fem una mica d’anàlisi i comprovem les referències creuades (xrefs):
[0x18000b790]> /r $$
(nofunc) 0x18000bf44 [CALL] call fcn.18000b790
[0x18000b790]> sf..bf44
[0x18000bf30]> pdf
/ 65: fcn.18000bf30 (int64_t arg1);
| `- args(rcx) vars(1:sp[0x20..0x20])
| 0x18000bf30 894c2408 mov dword [var_8h], ecx ; arg1
| 0x18000bf34 4883ec28 sub rsp, 0x28
| 0x18000bf38 837c243000 cmp dword [var_8h], 0
| ,=< 0x18000bf3d 752b jne 0x18000bf6a
| | 0x18000bf3f b902000000 mov ecx, 2
| | 0x18000bf44 e847f8ffff call fcn.18000b790
| | 0x18000bf49 b902000000 mov ecx, 2
| | 0x18000bf4e ff1584e90400 call qword [sym.imp.ucrtbased.dll___acrt_iob_func] ; [0x18005a8d8:8]=0x63996 reloc.ucrtbased.dll___acrt_iob_func
| | 0x18000bf54 488d156587.. lea rdx, str.ctrlc_pressed._n ; 0x1800646c0 ; "{ctrl+c} pressed.\n"
| | 0x18000bf5b 488bc8 mov rcx, rax
| | 0x18000bf5e e85d050000 call 0x18000c4c0
| | 0x18000bf63 b801000000 mov eax, 1
| ,==< 0x18000bf68 eb02 jmp 0x18000bf6c
| |`-> 0x18000bf6a 33c0 xor eax, eax
| | ; CODE XREF from fcn.18000bf30 @ 0x18000bf68(x)
| `--> 0x18000bf6c 4883c428 add rsp, 0x28
\ 0x18000bf70 c3 ret
[0x18000bf30]>
Perfecte, tenim una cadena de text aquí! Fent un grep al codi font trobem aquest codi:
C:\Users\pancake\prg\radare2>git grep "} pressed"
libr/cons/cons.c: eprintf ("{ctrl+c} pressed.\n");
Obrim aquest fitxer a vim i veiem què passa, i què és aquest
eprintf.
static R_TH_LOCAL RCons *I = NULL;
static BOOL __w32_control(DWORD type) {
if (type == CTRL_C_EVENT) {
__break_signal (2); // SIGINT
eprintf ("{ctrl+c} pressed.\n");
return true;
}
return false;
}
static void __break_signal(int sig) {
r_cons_context_break (I->context);
}Bingo, ara tenim la raó d’aquesta desreferència nul·la. Sembla que la
variable global I és NULL, així que podem solucionar el
problema afegint una simple comprovació if(I) i resoldre la
fallada quan l’usuari prem ^C.
Els senyals d’Unix i els gestors d’excepcions de Windows no poden prendre cap paràmetre per a context/userdata. Això és bastant dolent perquè ens obliga a utilitzar variables globals.
Per dir-li a r2 quina consola va rebre el gestor de sigint, bàsicament necessitem establir un punter global abans de configurar el gestor d’esdeveniments. Però… per què això funcionava a UNIX i no a Windows?
Resulta que el gestor CTRL_C_EVENT de Windows s’executa
des d’un fil d’execució diferent! La qual cosa significa que la variable
I no estava inicialitzada perquè és una variable local al
fil (thread-local).
R_TH_LOCAL és un àlies de portabilitat per a r2 per
especificar quines variables globals s’han d’emmagatzemar en l’atribut
_Thread_Local.
Idealment, voldríem tenir múltiples instàncies de RCons per utilitzar un únic terminal. No es tracta només que puguis tenir un únic nucli associat a una instància de RCons per executar des de diferents fils, sinó que també pots tenir múltiples fils amb les seves pròpies instàncies de RCore separades, cadascuna amb el seu propi RCons.
Qui pot rebre l’esdeveniment? Totes? L’última a sol·licitar la interrupció? Les instàncies de RCons poden deixar de ser afectades per aquests esdeveniments?
El que està clar és que aquestes interrupcions poden passar en qualsevol moment i des de qualsevol fil i poden afectar dades de qualsevol procés aleatori. Barrejar això amb el fet que hem d’utilitzar variables globals afegeix més dolor a la recepta. Però bé, això és una història per a una altra publicació.
Espero que hàgiu après alguna cosa i, com a amant d’Unix, hàgiu perdut una mica la por d’utilitzar Windows des del bon vell terminal cmd.
–pancake