WINE 使用及其调试

一、Wine

 

1、 wine 实现了大多数的windows API,集成了winedbg

2、 windows API

1) kernel32.dll 

允许一个W-process作为debugger 去执行另一个W-process,作为debuggee,包括设置breakpoint,单步执行等等

2) DBGHELP.DLL

让一个debbuger从任意模块查找符号和类型

3、 异常解决

怎么根据下面信息查找crash原因

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  71930000-719b8000   Deferred        shdoclc

PE  78130000-781cb000   Deferred        msvcr80

ELF 79afb000-7b800000   Deferred        libnvidia-glcore.so.304.51

ELF 7b800000-7ba3d000   Dwarf           kernel32

  \-PE  7b810000-7ba3d000   \               kernel32

ELF 7bc00000-7bcd5000   Dwarf           ntdll

  \-PE  7bc10000-7bcd5000   \               ntdll

ELF 7bf00000-7bf04000   Deferred        

ELF 7c288000-7c400000   Deferred        libvorbisenc.so.2

PE  7c420000-7c4a7000   Deferred        msvcp80

ELF 7c56d000-7c5b6000   Deferred        dinput

 

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

 

下面信息的含义:

000d:Call advapi32.RegOpenKeyExW(00000090,7eb94da0 L"Patterns",00000000,00020019,0033f968) ret=7eb39af8

 

000d: 线程id

advapi32: 被调用模块

RegOpenKeyExW: 被调用函数

后面几个都是参数

ret :返回地址

 

4、 Useful memory address

1)  32

linux: 0x08000000 0x00400000 0x40000000  

 

2)  16位(增强模式): segment:offset

segment 如果最低三比特位都是1,就是一个selector,如果最低三笔特除了最低位外都是1,可能是全局内存

0x1f7 (0x40320000, 0x0000ffff, r-x) : 分别是基地址,最大偏移,访问权限 r-x 表示可读可执行

实际地址: selector基地址+offset

 

3)  16位(标准模式):segment:offset

segmentoffset可以是0~0xffff

实际地址: segment×16+offset

 

5、 配置

 

1)  配置debuger

[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] 957636538

"Auto"=dword:00000001

"Debugger"="winedbg %ld %ld"

 

2)  配置winedbg

[HKCU\\Software\\Wine\\WineDbg]

BreakAllThreadsStartup TRUE:所有线程停止  FALSE:第一个线程停止

BreakOnCritSectTimeOut TRUE:在临界区超时5分钟停止 FALSE:不停止

BreakOnAttach 

BreakOnFirstChance  

一个异常产生两个debug 事件,或者说两次chance

first chance:发生异常之后传递给debuggerdebugger 要么继续执行(cont),要么交给exception handler chainpass

last chance:如果没有exception handler 处理异常,会再次传递给debugger,这一次不能pass

 

TRUE: 两次机会都会处理 FALSE :仅仅进入last chance

 

AlwaysShowThunk  TRUE:根据名称显示所有thunks   FALSE

 

3)  +relay 行为配置

可能输出会很多,但可以进行设置

[HKCU\\Software\\Wine\\Debug]

RelayExclude  列出不需要的输出

RelayInclude  仅输出列出的输出

怎么知道哪些输出是不需要的?

WINEDEBUG=+relay wine appname.exe &>relay.log

awk -F'(' '{print $1}' < relay.log | awk '{print $2}' | sort | uniq -c | sort

 

 

二、WineDbg

1、 WineDbg表达式

同 格式总体相同,有少量差异。

1) 表达式名称里可以用 ! 

2) 转换操作时 结构体或联合体都需要带 struct union 关键字

 

winedbg 特殊变量:$ThreadId 即 W-thread id

$ProcessId 即 W-process id

所有CPU寄存器值也是变量

2、 WineDbg 命令

Misc commands

NO.

command

Details

1

abort

abort the debbuger

2

quit

Quit the debbuger

3

Attach W-process ID

附加到另一个进程

4

Detach

分离进程

5

help

 

6

help info

 

 

Flow control commands

NO.

command

Details

1

cont, c

继续执行

2

pass

传递异常给filter chain

3

steps

单步,进入函数调用

4

stepi,si

单个指令

5

next ,n

单步,不进入函数

6

nexti ,ni

单个指令,不进入调用

7

finish ,f

执行直到当前函数退出

Breakpoints, watch points

NO.

command

Details

1

enable N

激活break|watch point N 

2

disable N 

禁用 break|watch point N

3

delete N

删除 break|watch point N

4

cond N

移除任何到 break|watch point N 的条件

5

cond N expr

按表达式设置breakpoint N触发条件

6

break * N 

增加breakpoint N(N为地址)

7

break id

增加breakpoint 符号id的地址 ??

8

break id N

符号id 的第N行  ??

9

break N

当前源文件的第N

10

break

当前$PC 地址设置breakpoint

11

watch * N

观察指令,* N 为地址

12

watch id

符号id的地址

13

info break

列出所有 break|watch point

Stack manipulation

NO.

command

Details

1

bt

打印当前线程栈调用

2

bt N

打印线程IDN的线程栈调用

3

up

往上走 1 frame ??

4

up N

往上走 N frame

5

dn

往下走1frame

6

dn N

 

7

frame N

执行直到当前函数退出

8

info local

局部变量

Directory & source file manipulation

NO.

command

Details

1

show dir

打印源文件查找目录

2

dir pathname

增加路径到查找目录列表

3

dir

删除查找目录列表

4

symbolfile pathnamme

加载外部符号定义

5

symbolfile pathname N

同上,但是有个偏移地址N

6

list  /-/N/file:N

默认列出10行代码,-向后列出

7

list id

列出函数id处的10行代码

8

list * N 

列出地址N10行代码

9

list N1,N2

列出从N1行到N2行的源码

10

list file:N1,N2

列出文件file N1行到N2行的源码

Displaying

NO.

command

Details

1

info display

??

2

display

 

3

display expr

 

4

display lfmt expr

按格式输出

5

del display N,undisplay N

删除display

Disassembly

NO.

command

Details

1

disas

反汇编

2

disas expr

指定地址出汇编代码

3

disas expr,expr

两个地址之间的汇编

Memory (reading, writing, typing)

NO.

command

Details

1

x expr/lfmt expr

显示地址出的值 ,格式??

2

print expr/lfmt expr

打印表达式的值

3

set lval=expr

设置变量

4

whatis expr

打印类型的表达式

5

set!symbol_picker interactive

打印的时候由用户决定选那个符号

6

set!symbol_picker scopedb

优先局部符号,然后才是全局的

v fmt  可以是 letter 或 count letter ??

s   ascii string

u   utf16 string

i    指令

x   32-bit 无符号16进制整数

d   32-bit 无符号10进制整数

w   16-bit无符号16进制整数

c    可打印字符,0x20~0x70 实际是可打印的

b    8-bit 无符号16进制整数

g    GUID

 

Information on Wine internals

NO.

command

Details

1

info class

列举所有窗口类

2

info class id

关于窗口类id的信息

3

info share

列举所有动态库,sodll

4

info share N

地址N的模块信息

5

info regs

CPU寄存器信息

6

info all-regs

CPU 和浮点寄存器

7

info segment N

segment N的信息,仅i386

8

info segment

所有segment ,仅i386

9

info stack

栈信息

10

info map

debbuger 的虚拟映射

11

info map N

wpid N 的虚拟映射

12

info wnd N

打印窗口N的信息

13

info wnd

列举从桌面开始的所有窗口层次

14

info process

列举wine回话中所有w-processes 

15

info thread

列举所有w-threads

16

info exception

异常信息

Debug channels

NO.

command

Details

1

set + warn

打开warn channel

2

set + channel

打开warn/fixme/err/trace

3

set - channel

关闭上述channel

4

set - fixme

关闭 fixme

 

 

三、其他Debuggers

1.GDB 模式

winedbg是一个远程gdb 监视器,增加 --gdb 即可激活gdb模式。

gdbwinegdb的区别

winegdb 掌控一个进程的所有线程,可以处理所有线程断点。gdb只能对单个线程进行调试。

Winegdb支持 stabsstandard Unix format) 和CCodeView,.DBG(Microsoft). gdb 支持stabs 和 Dwarf II

2.DDD

如下命令: 

winedbg --gdb --no-start  *.exe  optional param

然后把输出: target remote localhost12345 粘贴到ddd即可运行

 

3.kdbg

其他同ddd,运行时在kdbg终端运行: kdbg -r localhost:12345 wine

 

 

四、调试技巧

1、 debuging classes

FIXME

ERR

WARN

TRACE

MESSAGE

2、 debugging channels

每个组件都会有一个channel

设置方法: 

WINE_DEFAULT_DEBUG_CHANNEL(xxx);

如果要有多个channel,则增加多个名称不同即可。

使用的时候,需要使用类似FIXME_(xxx)(fmt,...); 此时FIXMEfmt,...;指向第一个声明channel

 

TRACE_ON WARN_ON ERR_ON FIXME_ON 用来判断是否打开了

3、 一些有用的函数

 

1)  debugres

LPSTR debugres(const void* id);

id: 资源id指针

返回:字符串类型,格式化字符串

2)  debugstr_[aw]n

 

处理某些NULL,控制字符或太长,或需要转换成ascii,都可以使用这些函数处理

 

 

4、 修改调试输出

第一种方法: 使用winedbg 命令

第二种方法: 通过taskmgr修改

第三种方法:创建pipe 和运行 WINEDEBUG

mknode  /tmp/debug_pipe p

WINEDEBUG=+relay,+snoop wine setup.exe &>/tmp/debug_pipe

cat  /tmp/debug_pipe

WINEDEBUG 格式:

WINEDEBUG=[yyy]#xxx[,[yyy1]#xxx1]*

yyy: tracedebugwarnfixmeerr等,fixme err 默认是激活的,tracewarn 默认是没激活的

#: + 或 

xxx: channelall 表示所有channel

可以用 , 隔开添加多个

如果使用MessageBox,则

WINEDEBUG=+relay wine program_name &>relmsg

 

5、 风格上的一些注意

输出格式: classchannelfunction  message

 

6、 一些其他技术

i386系统,栈是4字节,小端,地址向下增长,栈指针存放在esp寄存器,指向栈内存最后一次push 进去的数据的地址。可以表示为: 

push操作: *(--esp) =p;  pop 操作:p=*(esp++);

 

额外补充:

1) 调用协议常用场合

__stdcallWindows API默认的函数调用协议。

 __cdeclC/C++默认的函数调用协议。

__fastcall:适用于对性能要求较高的场合。

2) 函数参数入栈方式

__stdcall:函数参数由右向左入栈。

__cdecl:函数参数由右向左入栈。

__fastcall:从左开始不大于4字节的参数放入CPUECXEDX寄存器,其余参数从右向左入栈。

问题一:__fastcall在寄存器中放入不大于4字节的参数,故性能较高,适用于需要高性能的场合。

3) 栈内数据清除方式

 

__stdcall:函数调用结束后由被调用函数清除栈内数据。

__cdecl:函数调用结束后由函数调用者清除栈内数据。

__fastcall:函数调用结束后由被调用函数清除栈内数据。

问题一:不同编译器设定的栈结构不尽相同,跨开发平台时由函数调用者清除栈内数据不可行。

问题二:某些函数的参数是可变的,如printf函数,这样的函数只能由函数调用者清除栈内数据。

问题三:由调用者清除栈内数据时,每次调用都包含清除栈内数据的代码,故可执行文件较大。

4) C语言编译器函数名称修饰规则

 

__stdcall:编译后,函数名被修饰为“_functionname@number”。

__cdecl:编译后,函数名被修饰为“_functionname”。

__fastcall:编译后,函数名给修饰为“@functionname@nmuber”。

注:“functionname”为函数名,“number”为参数字节数。

注:函数实现和函数定义时如果使用了不同的函数调用协议,则无法实现函数调用。

5) C++语言编译器函数名称修饰规则

 

__stdcall:编译后,函数名被修饰为“?functionname@@YG******@Z”。

__cdecl:编译后,函数名被修饰为“?functionname@@YA******@Z”。

__fastcall:编译后,函数名被修饰为“?functionname@@YI******@Z”。

注:“******”为函数返回值类型和参数类型表。

注:函数实现和函数定义时如果使用了不同的函数调用协议,则无法实现函数调用。

C语言和C++语言间如果不进行特殊处理,也无法实现函数的互相调用。

 

 

stdcall 调用: 

从右往左一次将参数push 进栈。例如: function(20,30,40,50);

push  50

push  40

push  30

push  20

//此时栈的状况: 

other variable

50

40

30

20      <---  esp 指向这里

call  function

//此时

other variable   <---  esp 指向这里

但是这里有个问题是调用函数如何知道被调用函数有多少个参数

有两种方法: 第一,记录栈偏移  第二,

 

 

你可能感兴趣的:(windows编程,编程积累)