mandag 27. oktober 2008

Grunnleggende info om feilsøking med Windbg.

Windbg er da det mest brukte verktøyet jeg bruker for å feilsøke problemer i Windows, enten det gjelder programmer som får uventede avsluttinger,system som kræsjer eller henger seg,mistanke om defekt maskinvare-Windbg kan brukes til å feilsøke alle disse problemene.

Windbg er en del av Debugging Tools For Windows som kan lastes ned gratis fra denne siden:
http://www.microsoft.com/whdc/devtools/debugging/default.mspx

Windbg kan både brukes til å feilsøke i User Mode og i Kernel Mode delen av systemet. Og har en rekke utvidelser som kan brukes for å gi ekstra informasjon så i hovedsak kan man bruke det til å finne ut absolutt alt som kan være nødvendig for å løse problemet. Dog kan det være vanskelig i starten å få overblikk over alle funksjonene, så jeg skal prøve her å gi en rask innføring av feilsøking med Windgb. Det er 3 hovedgrupper man kan dele feilsøkingen med Windbg inn i:

1.Feilsøking av Windows Programmer.
Du kan enten starte programmet via Ctrl+E og kjøre det via Windbg eller trykke F6 for å hekte Windbg på en allerede kjørende prosess. Dette vil da gjøres i et såkalt "Invasive" modus noe som betyr at du får fullstendig kontroll over programmet men som også betyr at Windbg vil kunne forstyrre normal programoppførsel og også at når du stopper feilsøkingen vil også prosessen avsluttes.
Du har også mulighet for å feilsøke det i "Non-invasive" modus som gir deg litt mindre kontroll men vil minske forstyrellsen Windbg har på prosessen.

2.Feilsøking av Kræsj-dumper.
En kræsjdump er rett og slett et sammendrag av tilstanden til minnet idet noe kræsjet og som har blitt dumpet ut til en fil på harddisken. Windbg kan feilsøke både kræsjdumper tatt fra prosesser eller kræsjdumper fra operativsystemet. Typisk kan du konfiguere hvor mye informasjon du vil ta med- du kan velge mellom en Minidump-som bare dumper ut de mest nødvendige strukturene som kan ha med kræsjet å gjøre, En Kjernedump som dumper ut hele Kjerneminnet eller en fullstendig minnedump som dumper ut absolutt hele minnet.

3.Feilsøking av kjernen.
Dette gjøres ved at debuggeren aktiveres i oppstarten av den maskinen man vil feilsøke, (Kan gjøres i feks Msconfig) og deretter kobler en kabel mellom maskinen som skal feilsøkes og datamaskinen som kjører Windbg. (Enten serie.firewire eller USB 2.0) Deretter kan man koble til maskinen og vente til den går på en feil eller stoppe systemet direkte. Man kan også gjøre såkalt lokal kjernefeilsøking men dette fungerer som regel ikke så godt og gir som regel rimelig begrenset informasjon. Angående kablene som brukes kreves det spesielle debugger-kabler for feilsøking via serie og USB 2.0 tilknyttning mens firewire kan bruke regulære kabler. USB 2.0 debugging støttes bare av Vista og nyere.

Symbolfiler
Før man kan begynne å feilsøke med Windbg er det viktig å oppgi en bane til Symbolfilene. Symbolfiler lages av linkeren som lager .exe eller .dll filer men legges ofte ikke ved siden disse ikke er nødvendig får å kunne kjøre disse filene. Dog er det veldig nyttig å ha symbolfilene når man skal feilsøke da disse kan inneholde nyttig data slik som:
  • Globale Variabler
  • Lokale Variabler
  • Navn på funksjoner
  • FPO opptegnelser

Microsoft legger heldigvis ut alle symbolfilene til sine moduler gratis på en symbolserver slik at du fritt laste ned symbolfilene ved behov. Disse inneholder dog bare funksjonnavn og spesifikke globale variabler men er allikavel ekstremt nyttig å konfigurere. Du kan enten konfiguere Symbolbanen ved .sympath kommandoen eller ved å trykke CTRL+S i Windbg. Banen til Microsoft sin symbolserver er: srv*"Symbolbane"*http://msdl.microsoft.com/downloads/symbols hvor "Symbolbane" er banen hvor symbolene blir lastet ned til lokalt til din maskin. Deretter skal symbolene bli lastet inn ved behov straks du kjører .reload. Om du har problemer med å få symbolene til å laste seg inn ordentlig kan du prøve å bruke !sym noisy kommandoen for å undersøke hvordan symbolene lastes.

Husk forøvrig på at en minidump fil ikke inneholder all informasjon om de kjørbare image filene til systemet og det kan være fordelaktig å legge inn banen til microsofts symbolserver også der slik at den kan laste ned image filene automatisk ved behov. Image banen settes ved å trykke CTRL+I eller skrive bruke .exepath kommandoen.




søndag 26. oktober 2008

Oversetting fra virtuell til fysisk adresse på et Windows xp x86 system.

De fleste operativsystem idag kjører med 32-bits addressering, noe som betyr at prosessoren kan adressere opp til 4gb virtuellt minne. Dette betyr ikke nødvendigvis at de kan adressere 4gb fysisk minne men la oss idag se på hvordan en 32 bits virituell addresse er bygd opp og hvordan disse igjen blir oversatt til en fysisk adresse.

En 32-bits addresse er delt inn i 3 hoveddeler de 10 første bitene benyttes til noe som kalles Page Directory Index som brukes til å finne den rette sidevekslings tabellen som inneholder PTE'en som tilhører den virituelle addressen. PTE står for Page Table Entry som er den strukturen som inneholder den fysiske addressen som den virituelle er mappet til.
Deretter består de neste 10 bitene av addressen av en struktur som kalles Page Table Index som har en peker til selve PTE'en i den aktuelle sidevekslingstabellen.
Tilslutt har man altså 12 gjenstående bit'er som er da avsatt til Byte Index som peker til den aktuelle addressen innenfor den fysiske siden.

Måten Windows leter frem til den fysiske adressen er slik at først letes prosessens Page Directory frem. Noe som skjer hvergang prosessoren gjør et context switch, og lagres typisk i et spesielt prosessor-register. (CR3 på X86) Deretter vil pekeren i Page Directory Index (10 første biter av addressen) vise til hvor den kan finne Page Directory Entry (PDE) som da igjen peker til det aktuelle sidevekslingstabellen. PDE er da en struktur som inneholder et såkalt Page Frame Number (PFN) som skal samsvare med det i sidevekslingstabellen.

Videre brukes da pekeren i Page Table Index (Neste 10 biter) som viser til lokaksjonen til den aktuelle PTE oppføringen. Deretter vil PTE brukes for å finne den fysiske addressen. Hvis PTE'en er gyldig vil den inneholde PFN'en til siden i det fysiske minnet som refererer til den virituelle addressen.

Hvis PTE'en peker til en gyldig side vil da Byte Index (Siste 12 biter) brukes for å finne ut hvor i den aktuelle siden dataen som skal hentes er.

For å forstå litt bedre hvordan denne prosessen fungerer kan det være greit å gå gjennom trinnene manuelt med en kjerne-debugger.


Vi tar da først utgangspunkt i prosessen msnmsgr.exe:

0: kd> !process
PROCESS 89f61270 SessionId: 0 Cid: 0d78 Peb: 7ffdb000 ParentCid: 03e4
DirBase: 0a960460 ObjectTable: e3fbe628 HandleCount: 866.
Image: msnmsgr.exe

Vi ser at Dirbase peker mot den fysiske addressen til Page Directoryen, som vi vet skal operativsystemet installere CR3 registeret med denne addressen slik at prosessoren vet hvor den skal finne Page Directory, la oss dumpe ut CR3 registeret for å sjekke at dette stemmer:

0: kd> r cr3
cr3=0a960460

La oss da ta utgangspunkt i den virtuelle addressen til et Hendeles-objekt i msnmsgr som har addresse 89809a30.
Konvert til bit blir det til 1000100110 0000001001 101000110000 og som vi vet skal de 10 første bitene er Page Directory Index som peker til den spesifikke PDE som har addressen til den spesifikke sidevekslingstabellen.
Page Directoryen ligger da altså på 0a960460 og vi har en Page Directory Index på 226 noe som igjen betyr at PDE ligger på fysisk addresse 0a960686

Vi kan da videre kalkulere oss frem til riktig PTE ved denne formelen:
Sidevekslingstabellbase (0XC0000000 på standard x86 systemer uten PAE)
+ Page Directory Index*størrelsen på en side(4kb på et x86 system)
+ Page Table Index*størrelsen på en PTE (4bytes på et x86 system)
= PTE

La oss kalkulere oss frem til addressen til PTE med denne formelen.
0XC0000000
+ 0X0044C000 (0X226*0X1000*2)
+ 0X00000048 (0X9*4*2)
= 0XC044C048

Som du ser har jeg i tillegg multiplisert resultatene med to, dette er fordi systemet har 2 prosessorer.
Vi kan sjekke om vi har funnet korrekt PTE ved å bruke !pte utvidelesen med en virtuell adresse:

0: kd> !pte 89809a10
VA 89809a10
PDE at 00000000C0602260 PTE at 00000000C044C048
contains 0000000009687963 contains 0000000008A51963
pfn 9687 -G-DA--KWEV pfn 8a51 -G-DA--KWEV

Vi kan videre dumpe ut innholdet av PTE'en: (Fremdeles 4 byte)

0: kd> dd 0C044C048 l1
c044c048 08a51963

PTEen inneholder to strukturer-de første 20 bitene er PFN mens de resterende 12 er avsatt til status flagg. For å finne den fysiske siden i minnet multipliserer vi PFN med størrelsen av en side (fremdeles 4kb) noe som gir oss adressen 08A51000. Dette er da bare basen på siden den fysiske siden. For å finne den eksakte adressen til hvor dataen ligger lagret bruker vi bare Byte indexen i den virtuelle adressen og adderer den med basen på den fysiske siden, noe som da gjør at vi ender opp med den fysiske adressen 08A51A30. La oss dumpe ut dataen som ligger her:

0: kd> !dc 8A51A30
# 8a51a30 aa044a01 00000000 8a288120 8a288120
# 8a51a40 0a060006 20646156 00003750 0000375f
# 8a51a50 89c27188 8979cbc0 8979c590 07100001
# 8a51a60 8a06dcc0 e1b97438 fffffffc 40000000
# 8a51a70 00020006 20646156 8a1169d0 89b30a18
# 8a51a80 0a060002 20646156 00000130 00000132
# 8a51a90 8a2cdc50 00000000 00000000 01400000
# 8a51aa0 8a126c58 e10b5d98 e10b5da8 41000000

La oss sammenligne dette med dataen i den virtuelle adressen:

0: kd> dc 89809a30
89809a30 aa044a01 00000000 8a288120 8a288120
89809a40 0a060006 20646156 00003750 0000375f
89809a50 89c27188 8979cbc0 8979c590 07100001
89809a60 8a06dcc0 e1b97438 fffffffc 40000000
89809a70 00020006 20646156 8a1169d0 89b30a18
89809a80 0a060002 20646156 00000130 00000132
89809a90 8a2cdc50 00000000 00000000 01400000
89809aa0 8a126c58 e10b5d98 e10b5da8 41000000

Vi ser da at dataen er identisk, vi kan med andre ord konkludere med at vi har lykkes i å konvertere en virtuell adresse til en fysisk adresse i minnet. Håper dette var nyttig for å forstå hvordan Windows konverterer minne!







fredag 24. oktober 2008

Windows Xp-maskin med bugcheck 0xA (IRQL not less or equal)

Windows maskinen ville gi blåskjerm hver gang bruker logget seg på-bestandig samme type bugcheck og parametre hver gang.
Hentet ut kjerneminnedump fra maskinen og åpnet den i Windbg og dumpet deretter ut stakken:

kd> kv
ChildEBP RetAddr Args to Child
f8959e08 804dbda3 badb0d00 00000000 82266008 nt!KiTrap0E+0x233 (FPO: [0,0] TrapFrame @ f8959e08)
f8959e98 804dc4a8 f8959fc0 0000003e f8959fc0 nt!KiWaitTest+0x30 (FPO: [Non-Fpo])
f8959fa4 804dc378 18d1bda0 00000000 ffdff000 nt!KiTimerListExpire+0x7a (FPO: [Non-Fpo])
f8959fd0 804dbbd4 8055a020 00000000 0000103d nt!KiTimerExpiration+0xaf (FPO: [Non-Fpo])
f8959ff4 804db89e baeead54 00000000 00000000 nt!KiRetireDpcList+0x46 (FPO: [0,0,0])
f8959ff8 baeead54 00000000 00000000 00000000 nt!KiDispatchInterrupt+0x2a (FPO: [Uses EBP] [0,0,1])
WARNING: Frame IP not in any known module. Following frames may be wrong.804db89e 00000000 00000009 bb835675 00000128 0xbaeead54

Nederste stakkramme ser ut til å være ugyldig og referer ikke til noen gyldig instruksjon-men for nå skal vi se på hva som egentlig trigget blåskjermen. Vi ser at feilen er fanget av KiTrap ved f8959e08 la oss endre kontekst ved å sette først .trap f8959e08 og deretter dumpe ut registrene:

kd> .trap f8959e08
ErrCode = 00000000
eax=00000000 ebx=81fd9fe8 ecx=f8959e88 edx=00000000 esi=81fd9fe0 edi=81fda008 eip=804dbda3 esp=f8959e7c ebp=f8959e98 iopl=0 nv up ei pl nz ac pe cycs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010217
nt!KiWaitTest+0x30:804dbda3 6683781601 cmp word ptr [eax+16h],1 ds:0023:00000016=????

Vi kan av dette se at instruksen som feilet var en cmp (compare) instruksjon som hentet en peker fra eax+16. Denne instruksen feiler da fordi at som vi ser er eax registeret nullet ut og dette igjen får oss da til å lese fra minneområde 00000016 som da er et område i minnet som er resverert nullpekere og dermed er et ugyldig område i minnet. Første spørsmål vi har er hva som gjorde at eax ble satt til 0? Vi tar derfor en nærmere titt på instruksjone som ble kjørt innunder KiWaitTest.

kd> uf nt!KiWaitTest+0x30
nt!KiWaitTest:
804dbd7b 8bff mov edi,edi
804dbd7d 55 push ebp
804dbd7e 8bec mov ebp,esp
804dbd80 83ec10 sub esp,10h
804dbd83 53 push ebx
804dbd84 56 push esi
804dbd85 8bf1 mov esi,ecx
804dbd87 837e0400 cmp dword ptr [esi+4],0
804dbd8b 8d4df0 lea ecx,[ebp-10h]
804dbd8e 8d5e08 lea ebx,[esi+8]
804dbd91 8b03 mov eax,dword ptr [ebx]
804dbd93 8955f8 mov dword ptr [ebp-8],edx
804dbd96 894df4 mov dword ptr [ebp-0Ch],ecx
804dbd99 894df0 mov dword ptr [ebp-10h],ecx
804dbd9c 7e5f jle nt!KiWaitTest+0xd7 (804dbdfd)

nt!KiWaitTest+0x27:

804dbd9e 57 push edi

nt!KiWaitTest+0x28:
804dbd9f 3bc3 cmp eax,ebx
804dbda1 744f je nt!KiWaitTest+0xb7 (804dbdf2)

nt!KiWaitTest+0x30:
804dbda3 6683781601 cmp word ptr [eax+16h],1

Vi kan se dermed se at eax registeret blir satt ved instruksjonen:
804dbd91 8b03 mov eax,dword ptr [ebx]

Som henter en dword peker lagret i registeret ebx-hva ligger lagret i ebx?

kd> dd ebx
81fd9fe8 00000000 00000000 18d1af90 00000000
81fd9ff8 f8959fc0 f8959fc0 81fda008 00000000
81fda008 01000013 00000000 00000000 f8370f1a
81fda018 81fd9fe0 00000000 00000000 00000000
81fda028 bad67b5a 81fd7000 820e5ad0 00000000
81fda038 000a0008 00000001 81fda040 81fda040
81fda048 00000000 00000000 00000000 00000000
81fda058 81fda060 00000000 01000013 ffdff980

Som vi kan se er pekerne nullet ut og det er altså slik eax registret har blitt nullet ut.
Jeg slo på pool tagging med gflags for å forsøke å finne ut hvem som eide dette området av minnet og derettet kjøre
!pool opp mot denne addressen.

kd> !pool 81fd9fe8
Pool page 81fd9fe8 region is Nonpaged pool
*81fd7000 : large page allocation, Tag is NALW, size is 0x5000 bytes
Owning component : Unknown (update pooltag.txt)

Som vi kan se har modulen som har allokert dette minneområdet en ukjent minnetag "NALW" og vi kan derfor anta at dette tilhører en tredjepartsmodul. Hvordan finner vi ut hvilken driver som bruker denne tag'en?
Det enkleste er da å bruke programmet strings om kan lastes ned herfra:
http://technet.microsoft.com/en-us/sysinternals/bb897439.aspx

Deretter startet jeg det og kjøte det opp mot den vanligste lokasjonen for systemdrivere (c:\windows\system32\drivers)

Slik så resultatet ut:



Kom da frem til at pool-taggen tilhørte wlcom51b.sys. Som Windows beskrev som en miniport driver for wlan-kortet.

kd> lm kv m wlcom51b
start end module name
bad5e000 bad8ac00 wlcom51b (deferred)
Image path: \SystemRoot\system32\DRIVERS\wlcom51b.sys
Image name: wlcom51b.sys
Timestamp: Fri Oct 22 15:59:55 2004 (4179125B)
CheckSum: 00033C12
ImageSize: 0002CC00
Translations: 0000.04b0 0000.04e0 0409.04b0 0409.04e0

Ved oppdatering av denne forsvant problemet, så det ser ut til at det er denne driveren som har gjort stakken korrupt og skrevet over registeret ebx (som lå på stakken.)