winedbgを使う
このページの翻訳です
WineHQ - Using the Wine Debugger
1.3. Wineのデバッガを使う
この章ではWineのデバッグをどこから始めればよいか説明します。もし詰まった場所があったり、助けがほしい時は、どのようにすれば役に立つバグレポートを書けるかという情報を得るために"Wine Users Guide"の"How to Report A Bug"の章を読んでください。
1.3.1. Wineのクラッシュ
Wineはたいてい以下のような出力を表示する:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x0043369e). Register dump: CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b EIP:0043369e ESP:0b3ee90c EBP:0b3ee938 EFLAGS:00010246( R- -- I Z- -P- ) EAX:00000072 EBX:7b8acff4 ECX:00000000 EDX:6f727265 ESI:7ba3b37c EDI:7ffa0000 Stack dump: 0x0b3ee90c: 7b82ced8 00000000 7ba3b348 7b884401 0x0b3ee91c: 7b883cdc 00000008 00000000 7bc36e7b 0x0b3ee92c: 7b8acff4 7b82ceb9 7b8acff4 0b3eea18 0x0b3ee93c: 7b82ce82 00000000 00000000 00000000 0x0b3ee94c: 00000000 0b3ee968 70d7ed7b 70c50000 0x0b3ee95c: 00000000 0b3eea40 7b87fd40 7b82d0d0 Backtrace: =>0 0x0043369e in elementclient (+0x3369e) (0x0b3ee938) 1 0x7b82ce82 CONSOLE_SendEventThread+0xe1(pmt=0x0(nil)) [/usr/src/debug/wine-1.5.14/dlls/kernel32/console.c:1989] in kernel32 (0x0b3eea18) 2 0x7bc76320 call_thread_func_wrapper+0xb() in ntdll (0x0b3eea28) 3 0x7bc7916e call_thread_func+0x7d(entry=0x7b82cda0, arg=0x0(nil), frame=0xb3eeb18) [/usr/src/debug/wine-1.5.14/dlls/ntdll/signal_i386.c:2522] in ntdll (0x0b3eeaf8) 4 0x7bc762fe RtlRaiseException+0x21() in ntdll (0x0b3eeb18) 5 0x7bc7f3da start_thread+0xe9(info=0x7ffa0fb8) [/usr/src/debug/wine-1.5.14/dlls/ntdll/thread.c:408] in ntdll (0x0b3ef368) 6 0xf7597adf start_thread+0xce() in libpthread.so.0 (0x0b3ef468) 0x0043369e: movl %edx,0x0(%ecx) Modules: Module Address Debug info Name (143 modules) PE 340000- 3af000 Deferred speedtreert PE 3b0000- 3d6000 Deferred ftdriver PE 3e0000- 3e6000 Deferred immwrapper PE 400000- b87000 Export elementclient PE b90000- e04000 Deferred elementskill PE e10000- e42000 Deferred ifc22 PE 10000000-10016000 Deferred zlibwapi ELF 41f75000-41f7e000 Deferred librt.so.1 ELF 41ff9000-42012000 Deferred libresolv.so.2 PE 48080000-480a8000 Deferred msls31 PE 65340000-653d2000 Deferred oleaut32 PE 70200000-70294000 Deferred wininet PE 702b0000-70328000 Deferred urlmon PE 70440000-704cf000 Deferred mlang PE 70bd0000-70c34000 Deferred shlwapi PE 70c50000-70ef3000 Deferred mshtml PE 71930000-719b8000 Deferred shdoclc PE 78130000-781cb000 Deferred msvcr80 ELF 79afb000-7b800000 Deferred libnvidia-glcore.so.304.51 ELF 7b800000-7ba3d000 Dwarf kernel32<elf> \-PE 7b810000-7ba3d000 \ kernel32 ELF 7bc00000-7bcd5000 Dwarf ntdll<elf> \-PE 7bc10000-7bcd5000 \ ntdll ELF 7bf00000-7bf04000 Deferred <wine-loader> ELF 7c288000-7c400000 Deferred libvorbisenc.so.2 PE 7c420000-7c4a7000 Deferred msvcp80 ELF 7c56d000-7c5b6000 Deferred dinput<elf> \-PE 7c570000-7c5b6000 \ dinput ELF 7c5b6000-7c600000 Deferred libdbus-1.so.3 ELF 7c70e000-7c715000 Deferred libasyncns.so.0 ELF 7c715000-7c77e000 Deferred libsndfile.so.1 ELF 7c77e000-7c7e5000 Deferred libpulsecommon-1.1.so ELF 7c7e5000-7c890000 Deferred krnl386.exe16.so PE 7c7f0000-7c890000 Deferred krnl386.exe16 ELF 7c890000-7c900000 Deferred ieframe<elf> \-PE 7c8a0000-7c900000 \ ieframe ELF 7ca00000-7ca1a000 Deferred rasapi32<elf> \-PE 7ca10000-7ca1a000 \ rasapi32 ELF 7ca1a000-7ca21000 Deferred libnss_dns.so.2 ELF 7ca21000-7ca25000 Deferred libnss_mdns4_minimal.so.2 ELF 7ca25000-7ca2d000 Deferred libogg.so.0 ELF 7ca2d000-7ca5a000 Deferred libvorbis.so.0 ELF 7cd5d000-7cd9c000 Deferred libflac.so.8 ELF 7cd9c000-7cdea000 Deferred libpulse.so.0 ELF 7cdfe000-7ce23000 Deferred iphlpapi<elf> \-PE 7ce00000-7ce23000 \ iphlpapi ELF 7cff1000-7cffd000 Deferred libnss_nis.so.2 ELF 7d60d000-7d629000 Deferred wsock32<elf> \-PE 7d610000-7d629000 \ wsock32 ELF 7d80d000-7d828000 Deferred libnsl.so.1 ELF 7d8cf000-7d8db000 Deferred libgsm.so.1 ELF 7d8db000-7d903000 Deferred winepulse<elf> \-PE 7d8e0000-7d903000 \ winepulse ELF 7d95c000-7d966000 Deferred libwrap.so.0 ELF 7d966000-7d96d000 Deferred libxtst.so.6 ELF 7d96d000-7d992000 Deferred mmdevapi<elf> \-PE 7d970000-7d992000 \ mmdevapi ELF 7d9b3000-7d9d0000 Deferred msimtf<elf> \-PE 7d9c0000-7d9d0000 \ msimtf ELF 7d9d0000-7d9e5000 Deferred comm.drv16.so PE 7d9e0000-7d9e5000 Deferred comm.drv16 ELF 7da83000-7db5f000 Deferred libgl.so.1 ELF 7db60000-7db63000 Deferred libx11-xcb.so.1 ELF 7db63000-7db78000 Deferred system.drv16.so PE 7db70000-7db78000 Deferred system.drv16 ELF 7db98000-7dca1000 Deferred opengl32<elf> \-PE 7dbb0000-7dca1000 \ opengl32 ELF 7dca1000-7dcb6000 Deferred vdmdbg<elf> \-PE 7dcb0000-7dcb6000 \ vdmdbg ELF 7dcce000-7dd04000 Deferred uxtheme<elf> \-PE 7dcd0000-7dd04000 \ uxtheme ELF 7dd04000-7dd0a000 Deferred libxfixes.so.3 ELF 7dd0a000-7dd15000 Deferred libxcursor.so.1 ELF 7dd16000-7dd1f000 Deferred libjson.so.0 ELF 7dd24000-7dd38000 Deferred psapi<elf> \-PE 7dd30000-7dd38000 \ psapi ELF 7dd78000-7dda1000 Deferred libexpat.so.1 ELF 7dda1000-7ddd6000 Deferred libfontconfig.so.1 ELF 7ddd6000-7dde6000 Deferred libxi.so.6 ELF 7dde6000-7ddef000 Deferred libxrandr.so.2 ELF 7ddef000-7de11000 Deferred libxcb.so.1 ELF 7de11000-7df49000 Deferred libx11.so.6 ELF 7df49000-7df5b000 Deferred libxext.so.6 ELF 7df5b000-7df75000 Deferred libice.so.6 ELF 7df75000-7e005000 Deferred winex11<elf> \-PE 7df80000-7e005000 \ winex11 ELF 7e005000-7e0a5000 Deferred libfreetype.so.6 ELF 7e0a5000-7e0c5000 Deferred libtinfo.so.5 ELF 7e0c5000-7e0ea000 Deferred libncurses.so.5 ELF 7e123000-7e1eb000 Deferred crypt32<elf> \-PE 7e130000-7e1eb000 \ crypt32 ELF 7e1eb000-7e235000 Deferred dsound<elf> \-PE 7e1f0000-7e235000 \ dsound ELF 7e235000-7e2a7000 Deferred ddraw<elf> \-PE 7e240000-7e2a7000 \ ddraw ELF 7e2a7000-7e3e3000 Deferred wined3d<elf> \-PE 7e2b0000-7e3e3000 \ wined3d ELF 7e3e3000-7e417000 Deferred d3d8<elf> \-PE 7e3f0000-7e417000 \ d3d8 ELF 7e417000-7e43b000 Deferred imm32<elf> \-PE 7e420000-7e43b000 \ imm32 ELF 7e43b000-7e46f000 Deferred ws2_32<elf> \-PE 7e440000-7e46f000 \ ws2_32 ELF 7e46f000-7e49a000 Deferred msacm32<elf> \-PE 7e470000-7e49a000 \ msacm32 ELF 7e49a000-7e519000 Deferred rpcrt4<elf> \-PE 7e4b0000-7e519000 \ rpcrt4 ELF 7e519000-7e644000 Deferred ole32<elf> \-PE 7e530000-7e644000 \ ole32 ELF 7e644000-7e6f7000 Deferred winmm<elf> \-PE 7e650000-7e6f7000 \ winmm ELF 7e6f7000-7e7fa000 Deferred comctl32<elf> \-PE 7e700000-7e7fa000 \ comctl32 ELF 7e7fa000-7ea23000 Deferred shell32<elf> \-PE 7e810000-7ea23000 \ shell32 ELF 7ea23000-7eaf9000 Deferred gdi32<elf> \-PE 7ea30000-7eaf9000 \ gdi32 ELF 7eafb000-7eaff000 Deferred libnvidia-tls.so.304.51 ELF 7eaff000-7eb09000 Deferred libxrender.so.1 ELF 7eb09000-7eb0f000 Deferred libxxf86vm.so.1 ELF 7eb0f000-7eb18000 Deferred libsm.so.6 ELF 7eb18000-7eb32000 Deferred version<elf> \-PE 7eb20000-7eb32000 \ version ELF 7eb32000-7ec87000 Deferred user32<elf> \-PE 7eb40000-7ec87000 \ user32 ELF 7ec87000-7ecf1000 Deferred advapi32<elf> \-PE 7ec90000-7ecf1000 \ advapi32 ELF 7ecf1000-7ed8f000 Deferred msvcrt<elf> \-PE 7ed00000-7ed8f000 \ msvcrt ELF 7ef8f000-7ef9c000 Deferred libnss_files.so.2 ELF 7ef9c000-7efc7000 Deferred libm.so.6 ELF 7efc8000-7efe5000 Deferred libgcc_s.so.1 ELF 7efe5000-7f000000 Deferred crtdll<elf> \-PE 7eff0000-7f000000 \ crtdll ELF f73d0000-f73d4000 Deferred libxinerama.so.1 ELF f73d4000-f73d8000 Deferred libxau.so.6 ELF f73da000-f73df000 Deferred libdl.so.2 ELF f73df000-f7591000 Dwarf libc.so.6 ELF f7591000-f75ab000 Dwarf libpthread.so.0 ELF f75ab000-f76ef000 Dwarf libwine.so.1 ELF f7722000-f7728000 Deferred libuuid.so.1 ELF f7729000-f774a000 Deferred ld-linux.so.2 ELF f774a000-f774b000 Deferred [vdso].so Threads: process tid prio (all id:s are in hex) 00000008 (D) C:\Perfect World Entertainment\Perfect World International\element\elementclient.exe 00000031 0 <== 00000035 15 00000012 0 00000021 0 00000045 0 00000044 0 00000043 0 00000038 15 00000037 0 00000036 15 00000034 0 00000033 0 00000032 0 00000027 0 00000009 0 0000000e services.exe 0000000b 0 00000020 0 00000017 0 00000010 0 0000000f 0 00000014 winedevice.exe 0000001e 0 0000001b 0 00000016 0 00000015 0 0000001c plugplay.exe 00000022 0 0000001f 0 0000001d 0 00000023 explorer.exe 00000024 0
- 1. クラッシュの内容を段階的にデバッグします。あなたはそのどこかで止まるかもしれません、しかしそのバグレポートに集められた情報を利用可能な限り報告してください。クラッシュの原因を取得します。これはたいていページフォルト、Wineで未実装の機能、もしくはそのようなものです。クラッシュの報告時はその情報があなたにとって意味を成さないものであってもすべてのクラッシュダンプを報告します。(これがページフォルトや0x00への書き込みアクセスだった場合、大抵の場合WineはアプリケーションにNULLを渡します。)
- 2. クラッシュの原因を特定します。これは、大抵の場合エラーへの直接もしくは副次的な処理もしくはWineの誤った振る舞いのためなので、Wineを WINEDEBUG=+relay という環境変数を設定して再度起動してください。これは大量の出力を表示するでしょうが、大抵その原因は最後の呼び出しに位置しています。これらの行は大抵以下のような感じです:
000d:Call advapi32.RegOpenKeyExW(00000090,7eb94da0 L"Patterns",00000000,00020019,0033f968) ret=7eb39af8 ^^^^ ^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ | | | | | | |返り値のアドレス | | | | | |追加の引数 | | | | |印字可能なパラメータ | | | |引数 | | |呼び出された関数 | |呼び出された関数のモジュール |呼び出されたスレッド 000d:Ret advapi32.RegOpenKeyExW() retval=00000000 ret=7eb39af8 ^^^^^^^^^^^^^^^ |返り値が32bitならばvalueは0
- 3. もしあなたがWineの関数内で誤った動きを見つけたなら、なぜそのような動きをするか見つけようとしてください。その関数をソースコード中で見つけます。渡された引数の意味を捉えてください。たいていソースコードの始めには WINE_DEFAULT_DEBUG_CHANNEL(channel)が存在します。WineをWINEDEBUG=+xyz,+relayという環境変数を設定して再度起動しましょう。そうすると突然そこにはWINE_DECLARE_DEBUG_CHANNEL(channel)の形式でソースコードの始めのほうで定義されたデバッグ用のチャンネルが現れます。さすれば、そのムカつく関数はこれらのチャンネルの一つを使用しているかもしれません。それではTRACE_(channel)(" ... /n")という関数を使用して、中身を見てみましょう。これは、コマンドラインに追加のチャンネルを追加してやります。
- 4. 内部用デバッガを使ってどのようにデバッグするかについての追加情報はprograms/winedbg/READMEで見つけられるでしょう。
- 5. もしこの情報が不明瞭、もしくはその関数自体で何が起こっているか知りたいのであれば、WineをWINEDEBUG=+allで起動させてください、これはWine内のすべてのデバッグ情報を吐かせます。その際は生成されるデバッグ出力を制限する必要があります。それはgrepを使ったりして出力をパイプすることで成し遂げられます、もしくはレジストリキーを使用するという代替手段もあります。それについては1.5.3章を見よ。
1.3.2. プログラムがハングする、何も起きない場合
wineの代わりにwinedbgを使用してプログラムを起動します。プログラムがwinedbgの端末内につなぎとめられたら、Ctrl-Cを押します。これはプログラムを停止して、プログラムがどうしてクラッシュしたかデバッグすることを許します。
1.3.3. メッセージボックスでエラーが報告される場合
ときどきプログラムは漠然としたメッセージボックスを使ってエラーを報告してきます。私たちはそれらをクラッシュとして同じ方法でデバッグできますが、そこには問題があります…メッセージボックスを設定するためにそのプログラムはWineが出力する膨大なデバッグコードを同様に呼び出すのです。そのエラーは大抵メッセージボックスを設定する前に起こるので、あなたはwinedgbを起動させることができ、MessageBoxAに(これはwin16とwin32のプログラムに呼ばれる)ブレークポイントを張れます、そしてデバッグを継続できます。WINEDEBUG=+allを使えばWineは設定しているメッセージボックスの前に止まるでしょう。上記で説明しているように続けます。あなたはWINEDEBUG=+relay wine program.exe 2>&1 | less -iのようなコマンドを使って"MessageBox"の文字列を検索できるでしょう。
1.3.4. プログラムを逆アセンブルする
あなたは文書化されてない機能を確認したり使用したりするためにムカつくプログラムを逆アセンブルしようとしているかもしれない。最良の方法、それは無料で利用可能なWin16向け逆アセンブラ用ツールのWindows Codebackです。ファイル名はwcbxxx.zipです(または wcb105a.zip)。Win32のプログラムを逆アセンブルすることは、例えばAnsgar Trimborn氏のGoVestなどを利用することで可能です。それはここにあります。もしくはもっと新しくてより良いDataRescueの対話型逆アセンブラー(IDA)を使用できるかもしれない。AppDBからたくさんのバージョンのIDAが見つけられるので見てみてください。他の有名な逆アセンブラーとしてはURSoftのWindows Disassembler 32が挙げられます。w32dsm87.zip(もしくはそれに類似する奴)をwinsite.comかsoftpedia.comから探してください。Windows Disassembler 32は現在Wineで使用するには問題がありそうなので、IDAかGoVestを使うのが良さそうです。考えられる逆アセンブラーとしては他にNuMegaのSoftIceが挙げられます。そのソフトウェアはCompuWareとして権利を取得されたためWindowsドライバーの開発元になってしまいました。新しいバージョンのSoftIceはWindowsのサービスを走らせることを要求し、それ故にWineでは動作しようとしないのです。もしどれも動かなければ、Googleで他の逆アセンブラーを探したほうがいいかもです。
逆アセンブルされたコードを理解することが質問に挙がるだろう。多くのコードは標準的なC言語のエントリーにあたる(それは大抵C言語で書かれているからだ)。Win16の関数のエントリーは大抵以下のようなものだ:
push bp mov bp, sp ... function code .. retf XXXX <--------- XXXX は引数のBYTE数
これはデータの保存を伴わないFAR関数*2です。その引数はオフセット値のインクリメントを伴う[bp+6]で始まる。あと、その[bp+6]は右辺の引数に属する、それはPASCALの関数の慣習を使用するwin16の関数群を外に公開するためなのですが。なので、私たちがstrcmp(a,b)を使用する場合、aは[bp+6]にあたり、そしてbは[bp+10]にあたるということになる。
ほとんどの関数はスタックフレーム*3内にデータを保存する:
enter 0086, 00 ... function code ... leave retf XXXX
This does mostly the same as above, but also adds 0x86 bytes of stackstorage, which is accessed using [bp-xx]. Before calling a function, arguments are pushed on the stack using something like this:
push word ptr [bp-02] <- will be at [bp+8] push di <- will be at [bp+6] call KERNEL.LSTRLEN
Here first the selector and then the offset to the passed string are pushed.
1.3.5. デバッグの手順例
Let's debug the infamous Word SHARE.EXE message box:
|marcus@jet $ wine winword.exe | +---------------------------------------------+ | | ! You must leave Windows and load SHARE.EXE| | | before starting Word. | | +---------------------------------------------+ |marcus@jet $ WINEDEBUG=+relay,-debug wine winword.exe |CallTo32(wndproc=0x40065bc0,hwnd=000001ac,msg=00000081,wp=00000000,lp=00000000) |Win16 task 'winword': Breakpoint 1 at 0x01d7:0x001a |CallTo16(func=0127:0070,ds=0927) |Call WPROCS.24: TASK_RESCHEDULE() ret=00b7:1456 ds=0927 |Ret WPROCS.24: TASK_RESCHEDULE() retval=0x8672 ret=00b7:1456 ds=0927 |CallTo16(func=01d7:001a,ds=0927) | AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=0927 BP=0000 ES=11f7 |Loading symbols: /home/marcus/wine/wine... |Stopped on breakpoint 1 at 0x01d7:0x001a |In 16 bit mode. |Wine-dbg>break MessageBoxA <---- Set Breakpoint |Breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190]) |Wine-dbg>c <---- Continue |Call KERNEL.91: INITTASK() ret=0157:0022 ds=08a7 | AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=08a7 ES=11d7 EFL=00000286 |CallTo16(func=090f:085c,ds=0dcf,0x0000,0x0000,0x0000,0x0000,0x0800,0x0000,0x0000,0x0dcf) |... <----- Much debug output |Call KERNEL.136: GETDRIVETYPE(0x0000) ret=060f:097b ds=0927 ^^^^^^ Drive 0 (A:) |Ret KERNEL.136: GETDRIVETYPE() retval=0x0002 ret=060f:097b ds=0927 ^^^^^^ DRIVE_REMOVEABLE (It is a floppy diskdrive.) |Call KERNEL.136: GETDRIVETYPE(0x0001) ret=060f:097b ds=0927 ^^^^^^ Drive 1 (B:) |Ret KERNEL.136: GETDRIVETYPE() retval=0x0000 ret=060f:097b ds=0927 ^^^^^^ DRIVE_CANNOTDETERMINE (I don't have drive B: assigned) |Call KERNEL.136: GETDRIVETYPE(0x0002) ret=060f:097b ds=0927 ^^^^^^^ Drive 2 (C:) |Ret KERNEL.136: GETDRIVETYPE() retval=0x0003 ret=060f:097b ds=0927 ^^^^^^ DRIVE_FIXED (specified as a hard disk) |Call KERNEL.97: GETTEMPFILENAME(0x00c3,0x09278364"doc",0x0000,0927:8248) ret=060f:09b1 ds=0927 ^^^^^^ ^^^^^ ^^^^^^^^^ | | |buffer for fname | |temporary name ~docXXXX.tmp |Force use of Drive C:. |Warning: GetTempFileName returns 'C:~doc9281.tmp', which doesn't seem to be writable. |Please check your configuration file if this generates a failure. Whoops, it even detects that something is wrong! |Ret KERNEL.97: GETTEMPFILENAME() retval=0x9281 ret=060f:09b1 ds=0927 ^^^^^^ Temporary storage ID |Call KERNEL.74: OPENFILE(0x09278248"C:~doc9281.tmp",0927:82da,0x1012) ret=060f:09d8 ds=0927 ^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^ |filename |OFSTRUCT |open mode: OF_CREATE|OF_SHARE_EXCLUSIVE|OF_READWRITE
This fails, since my C: drive is in this case mounted readonly.
|Ret KERNEL.74: OPENFILE() retval=0xffff ret=060f:09d8 ds=0927 ^^^^^^ HFILE_ERROR16, yes, it failed. |Call USER.1: MESSAGEBOX(0x0000,0x09278376"You must close Windows and load SHARE.EXE before you start Word.",0x00000000,0x1030) ret=060f:084f ds=0927
And MessageBox'ed.
|Stopped on breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190]) |190 { <- the sourceline In 32 bit mode. Wine-dbg>
The code seems to find a writable harddisk and tries to create a file there. To work around this bug, you can define C: as a network drive, which is ignored by the code above.
1.3.6. デバッグのチップス
Here are some additional debugging tips:
If you have a program crashing at such an early loader phase that you can't use the Wine debugger normally, but Wine already executes the program's start code, then you may use a special trick. You should do a
WINEDEBUG=+relay wine program
to get a listing of the functions the program calls in its start function. Now you do a
winedbg winfile.exe
This way, you get into winedbg. Now you can set a breakpoint on any function the program calls in the start function and just type c to bypass the eventual calls of Winfile to this function until you are finally at the place where this function gets called by the crashing start function. Now you can proceed with your debugging as usual.
If you try to run a program and it quits after showing an error message box, the problem can usually be identified in the return value of one of the functions executed before MessageBox(). That's why you should re-run the program with e.g.
WINEDEBUG=+relay wine program_name &>relmsg Then do a more relmsg and search for the last occurrence of a call to the string "MESSAGEBOX". This is a line like Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff In my example the lines before the call to MessageBox() look like that: Call KERNEL.96: FREELIBRARY(0x0347) ret=01cf:1033 ds=01ff CallTo16(func=033f:0072,ds=01ff,0x0000) Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1033 ds=01ff Call KERNEL.96: FREELIBRARY(0x036f) ret=01cf:1043 ds=01ff CallTo16(func=0367:0072,ds=01ff,0x0000) Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1043 ds=01ff Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff CallTo16(func=0317:0072,ds=01ff,0x0000) Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:105c ds=01ff Call USER.171: WINHELP(0x02ac,0x01ff05b4 "COMET.HLP",0x0002,0x00000000) ret=01cf:1070 ds=01ff CallTo16(func=0117:0080,ds=01ff) Call WPROCS.24: TASK_RESCHEDULE() ret=00a7:0a2d ds=002b Ret WPROCS.24: TASK_RESCHEDULE() retval=0x0000 ret=00a7:0a2d ds=002b Ret USER.171: WINHELP() retval=0x0001 ret=01cf:1070 ds=01ff Call KERNEL.96: FREELIBRARY(0x01be) ret=01df:3e29 ds=01ff Ret KERNEL.96: FREELIBRARY() retval=0x0000 ret=01df:3e29 ds=01ff Call KERNEL.52: FREEPROCINSTANCE(0x02cf00ba) ret=01f7:1460 ds=01ff Ret KERNEL.52: FREEPROCINSTANCE() retval=0x0001 ret=01f7:1460 ds=01ff Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff
I think that the call to MessageBox() in this example is not caused by a wrong result value of some previously executed function (it's happening quite often like that), but instead the message box complains about a runtime error at 0x0004:0x1056.
As the segment value of the address is only 4, I think that that is only an internal program value. But the offset address reveals something quite interesting: offset 1056 is very close to the return address of FREELIBRARY():
Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff
^^^^
Provided that segment 0x0004 is indeed segment 0x1cf, we now we can use IDA to disassemble the part that caused the error. We just have to find the address of the call to FreeLibrary(). Some lines before that the runtime error occurred. But be careful! In some cases you don't have to disassemble the main program, but instead some DLL called by it in order to find the correct place where the runtime error occurred. That can be determined by finding the origin of the segment value (in this case 0x1cf).
If you have created a relay file of some crashing program and want to set a breakpoint at a certain location which is not yet available as the program loads the breakpoint segment during execution, you may set a breakpoint to GetVersion16/32 as those functions are called very often.
Then do a c until you are able to set this breakpoint without error message.
1.3.7. Some basic debugger usages
After starting your program with
winedbg myprog.exe
the program loads and you get a prompt at the program starting point. Then you can set breakpoints:
b RoutineName (by routine name) OR b *0x812575 (by address) Then you hit c (continue) to run the program. It stops at the breakpoint. You can type step (to step one line) OR stepi (to step one machine instruction at a time; here, it helps to know the basic 386 instruction set) info reg (to see registers) info stack (to see hex values in the stack) info local (to see local variables) list line number (to list source code) x variable name (to examine a variable; only works if code is not compiled with optimization) x 0x4269978 (to examine a memory location) ? (help) q (quit)
By hitting Enter, you repeat the last command.
1.3.8. Useful programs
Some useful programs:
GoVest: govest.zip is available from http://www.oocities.com/govest/.
Simple win32 disassembler that works well with Wine.
IDA:
IDA Pro is highly recommended, but is not free. DataRescue does however make trial versions available.
Take a look in the AppDB for links to various versions of IDA.
XRAY: http://garbo.uwasa.fi/pub/pc/sysinfo/xray15.zip
Traces DOS calls (Int 21h, DPMI, ...). Use it with Windows to correct file management problems etc.
pedump: ftp://ftp.simtel.net/pub/simtelnet/win95/prog/pedump.zip
Dumps the imports and exports of a PE (Portable Executable) DLL.
winedump: (included in wine tree)
Dumps the imports and exports of a PE (Portable Executable) DLL.