Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.

April 21, 2007 / by Zsolt Soczó

Natív kód debugolási útmutató 10 percben

Szeretnék némi segítséget adni azoknak, akik nem általuk írt natív kódot szeretnének debugolni. A példában az Internet Explorert debugolom meg.
A natív kódú debugolás reménytelen szimbólumok nélkül, amelyek pdb fájlokban laknak. Az MS minden moduljához publikál pdb fájlokat, amelyek persze forráskód információt nem tartalmaznak, de a függvények és osztályok neveit igen. Ezek a stripped pdb fájlok.
Egy dll-hez, exe-hez a hozzá pontosan passzoló pdb fájlra van szükség. Kézzel igen nehéz lenne minden fájlhoz összevadászni a szimbólumokat, főleg, hogy hetente jönnek a hotfixek a gépre. Ezen segít a symbol szerver. Ez azt jelenti, hogy az ms minden modulhoz kirakja nyilvánosan egy webszerverre a szimbólumokat, amit a WinDbg vagy a VS magától le tud tölteni.
A szimbólumszervert be lehet állítani az _NT_SYMBOL_PATH környezeti változóban, vagy a VS-ben a Tool/Options/Debugging/Symbols alatt.
Visual Studio 2005 Debug Symbol beállítások
Nálam így néz ki a könyezeti változó:
_NT_SYMBOL_PATH=SRV*D:\Debugging\symbols*http://msdl.microsoft.com/download/symbols
D:\Debugging\symbols, ide tölti le a debugger a symbol fájlokat, így az első debugolás lassabban indul el, de utána már ebből a cache-ből veszi a pdbket.
Nálam ez a könyvtár 600 mega, ebben az XP és a Vista szimbólumainak egy része lakik.
Jöhet a debugolás. Az igazi macsó debugolás WinDbg vagy NTSD alatt lehetséges, én egyelőre a VS-re mutatom be, de már tanulgatom a WinDbg-ot is.
Létrehozok a VS-ben egy új, üres solutiont, így a töréspontok megmaradnak a debugolások között.
Debug/Attach-csal hozzákapcsolódok a már futó processzhez, esetünkben az IExpolere.exe-hez. Mivel fut a debugolandó program, így sokat nem tudunk vele tenni, állítsuk meg: break gomb a toolbaron, vagy F12.
A Modules ablakban látszanak a betöltött modulok és a hozzájuk tartozó szimbólum infó.
VS 2005 Modules ablak
A következő lépés töréspontok beállítása. Mivel nincs forráskódunk, ez kicsit macerásabb, mint saját kódoknál, de nagyobb az öröm is, ha sikerül összehozni. :)
Pl., a diagnosztizálandó probléma miatt engem érdekel, ha a registryből akar egy értéket olvasni az IE. Ezt valszeg a RegGetValue hívásával teszi meg. Rakjunk erre egy töréspontot!
A doksiból látjuk, hogy a RegGetValue 7 paraméteres. Ez azért fontos, mert az exportált nevek kicsit meg vannak gyúrva, így csak a függvény nevével nem lehet töréspontot berakni. A mangled név így néz ki általában az api függvényeknél (x86): _Fv@a paraméterek összmérete. Így a RegGetValue neve: _RegGetValueW@28 (7×4). A befagyaszott kódban a Breakpoints ablakban, New Breakpoint-tal már be is rakhatjuk a töréspontot:
VS 2005 Breakpoints ablak, benne 3 törésponttal
Ha tele, piros kört látunk, örülünk, ha nem, újragondoljuk a nevet.
Ezután a kódot elindítva előbb-utóbb becsap a kívánt töréspont:
Becsapott a töréspont :)
A képen sokkal több látszik, mint egyszerűen a töréspont. A bal oldalra dokkolt Memory 1 fenti kombójába beírtam, hogy ESP, így ráált a veremmutató címére, azaz az ablak tetején a fv. hívás lokális paraméterei láthatók. A legfelső érték a visszatérési cím, a másik az első paraméter, stb. Kijelöltem a 76daa4b8 paramétert, ami a RegGetValue LPCTSTR lpValue paramétere, azaz egy string mutató, esetünkben Unicode, mert Vistán vagyunk. Rádobtam a Watch ablakra, és átkasztoltam wchar_t*-gá, így azonnal látható lett az értéke. Hasonló módon a Memory 2 kombójába is beledobtam az értéket, így ott is látható a string.
Bajban vagyunk a töréspont szintaxisával, ha Template-et használó osztályokban kell töréspontot beállítani. Ilyenkor a fenti kukacos, számolós eljárás nem jön be, nincs mese, bele kell kukkantani a pdb-be.
A DIA sdk-ban (a VS setup része, ha kérjük) vagy egy kis példa, DIA2DUMP a neve. Ezt lefordítva egy pdb dumpoló eszközt kapunk a kezünkbe.
Engem az urlmon.dll belseje érdekelt. A Modules ablakból tudom, melyik konkrét pdb tartalmaz hozzá infót. Ezután egyszerűen a pdb könyvtárából:
dia2dump urlmon.pdb >dump.txt
A dump 9 mega. Az elejéből megtudhatjuk hol voltak az obj fájlok a MS build gépén, amin a Vista RTM készült:

0001 f:\vistartm.obj.x86fre\shell\lib\cul\objfre\i386\precomp.obj
0002 f:\vistartm.obj.x86fre\inetcore\lib\stock\stock\objfre\i386\stock.obj
0003 f:\vistartm.obj.x86fre\inetcore\urlmon\mon\daytona\objfre\i386\mon.obj
0004 f:\vistartm.obj.x86fre\inetcore\urlmon\trans\daytona\..\..\mon\daytona\objfre\i386\trans.obj
0005 f:\vistartm.obj.x86fre\inetcore\urlmon\iapp\daytona\..\..\mon\daytona\objfre\i386\iapp.obj
0006 f:\vistartm.obj.x86fre\inetcore\urlmon\download\daytona\objfre\i386\cdlpch.obj
0007 f:\vistartm.obj.x86fre\inetcore\urlmon\zones\daytona\objfre\i386\zonepch.obj

018D f:\vistartm.obj.x86fre\inetcore\inetcommon\commonparse\objfre\i386\add2strw.obj

Majd jönnek a szimbólumok:
PublicSymbol: [000a7201][000a7201][0001:000a274d] _OutputBlock@4(_OutputBlock@4)

Én a Call Stack ablakból láttam, hogy a Cwvt::InvokeWinVerifyTrust metódus működése érdekel. Rákeresve a dumpban a InvokeWinVerifyTrust-ra ez a szimbólum jön fel:
?InvokeWinVerifyTrust@Cwvt@@AAEJPAXPAUHWND__@@KPAPAU_JAVA_TRUST@
@PBGPAUIInternetHostSecurityManager@@3PADHPAVCDownload@@@Z
(private: long __thiscall Cwvt::InvokeWinVerifyTrust(void *,
struct HWND__ *,unsigned long,struct _JAVA_TRUST * *,
unsigned short const *,struct IInternetHostSecurityManager *,
unsigned short const *,char *,int,class CDownload *))

Durva? Nagyon. Ez egy szimbólum, egy sor, csak betördeltem. Sajnos a template-ek miatt a compiler nagyon ronda cuccot rak össze, de normális emberek nem is nézegetnek ilyesmit. :)
Ám akármilyen hosszú is, a Breakpoints ablakba bepasztázva rátalál a VS és töréspontra, és meg is áll ott.
Szeretném hangsúlyozni, hogy ehhez a debugoláshoz NEM használtam a forráskód elérésemet, tisztán a mindenki számára elérhető publikus szimbólumokkal dolgoztam. Más kérdés, hogy a hiba felderítéséhez nagyban segít, hogy közben egy másik gépen és monitoron olvasom a forrást. :)
Ha sikerült valamit kinyomoznom a nagy debugolással, elmesélem. Ha nem, megtanultam jobban debugolni, és ti is kaptatok ízelítőt belőle.
Ja, és a témában az alapmű: Debugging Applications for Microsoft .NET and Microsoft Windows. Az új rész már csak manazsolt kóddal foglalkozik, erre figyeljetek.
Update: lett eredménye a 3 napos debugozásnak: az ActiveX Installer Service-szel fel se veszi a kapcsolatot az IE, ha az IE egy LUA account nevében fut. LUA: admin, de nem admin, azaz be van kapcsolva az UAC és az illető user tagja az admin csoportnak. Én ilyen voltam, és azt hittem, hogy emiatt minden program úgy kezel engem, mintha mégse volnék admin. Nos, az IE kódjában van egy elágazás, ami ez alapján másképp végzi az ActiveX telepítést. Így jártam, Zokszigen röhög, hogy még 2007-ben se vagyok igazi nonadmin, csak LUA luzer. Azt hiszem ez meggyőzőtt, hogy valódi nonadminként kell fejlesztenem. Vagy elég csak így tesztelni? ;)

Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.

LEAVE A COMMENT

2 COMMENTS

  • Attila April 21, 2007

    A pdb file-okban a nem exportált szimbólumok is benne vannak? Az exportált függvények dekorált illetve nem dekorált nevének a megtalálásához használható a Dependency Walker is, így nem kell trükközni a név dekorálásának a kitalálásához. Be kell tölteni a fv-t használó, vagy exportáló modult a Depends.exe-be, az szépen megmutatja az összes exportált illetve importált szimbólumot. A dekorálást ki/be lehet kapcsolni.

    Üdv,
    Attila

  • Soczó Zsolt April 21, 2007

    Igen, a pdb tartalmazza a nem publikus neveket is.
    A depens jó ötlet, köszi. Habár úgy látom a sík, C api függvények eredeti nevét nem mutatja meg. Debugolni meg ugye az kellene.