首页
社区
课程
招聘
[分享]利用OllyDbg进行源码级调试(Win32汇编语言)
发表于: 2005-8-7 19:07 22905

[分享]利用OllyDbg进行源码级调试(Win32汇编语言)

2005-8-7 19:07
22905

利用OllyDbg进行源码级调试(Win32汇编语言)

OllyDbg是一款能够在Windows环境下的动态调试软件,与Softice不同的是,它运行在用户模式下,且结合了动态调试与静态分析的功能。
OllyDbg通常用于反汇编调试,但事实上,它也能进行源码级调试,这对众多程序员来说是个福音。
下面我就以Win32汇编语言为例,简单介绍一下OllyDbg的源码级调试方法。

=================================================================
先来看一段示例代码:

代码文件列表:
ODbgTest.Asm
ODbgTest.Inc
DlgProc\DlgProc.Asm
ODbgTest.Rc
主要代码如下:
-------------------
ODbgTest.Asm:
-------------------
.386
.model flat, stdcall
option casemap :none
include ODbgTest.inc
include DlgProc\DLgProc.asm
.code
start:
  invoke GetModuleHandle,NULL
  mov    hInstance,eax
  invoke InitCommonControls
  invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
  invoke ExitProcess,0
end start
-------------------
DlgProc\DlgProc.Asm
-------------------
.code
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
   mov eax,uMsg
   .if eax==WM_INITDIALOG
   .elseif eax==WM_COMMAND
      mov eax,wParam
      mov edx,eax
      shr edx,16
      and eax,0FFFFh
      .if edx==BN_CLICKED
         .if eax==IDC_BTN1
             invoke EndDialog,hWin,NULL
         .endif
      .endif
   .elseif eax==WM_CLOSE
      invoke EndDialog,hWin,0
   .else
      mov eax,FALSE
      ret
   .endif
   mov        eax,TRUE
  ret
DlgProc endp
---------------------------------------------------------------
下面的编译链接所用的选项比较重要,就是要添加调试信息供OllyDbg调试:
rc ODbgTest.Rc
ml /c /coff /Cp /Zi ODbgTest.Asm
link /SUBSYSTEM:WINDOWS /DEBUG /DEBUGTYPE:CV ODbgTest.obj ODbgTest.res
完成后,我们便可以得到可执行程序ODbgTest.exe。

================================================================================
接下来,我们用OllyDbg打开刚才得到的可执行程序ODbgTest.exe。
如一下就是OllyDbg中的一段反汇编代码(带调试符):
----------------------------------------------
0040101C >/> \55            push    ebp
0040101D  |.  8BEC          mov     ebp,esp
0040101F  |.  8B45 0C       mov     eax,[arg.uMsg]
00401022  |.  3D 10010000   cmp     eax,110                  ;  Switch (cases 10..111)
00401027  |.  75 02         jnz     short ODbgTest.0040102B
00401029  |.  EB 45         jmp     short ODbgTest.00401070  ;  Case 110 (WM_INITDIALOG) of switch 00401022
0040102B  |>  3D 11010000   cmp     eax,111
00401030  |.  75 24         jnz     short ODbgTest.00401056
00401032  |.  8B45 10       mov     eax,[arg.wParam]         ;  Case 111 (WM_COMMAND) of switch 00401022
00401035  |.  8BD0          mov     edx,eax
00401037  |.  C1EA 10       shr     edx,10
0040103A  |.  25 FFFF0000   and     eax,0FFFF
0040103F  |.  0BD2          or      edx,edx
00401041  |.  75 2D         jnz     short ODbgTest.00401070
00401043  |.  3D E9030000   cmp     eax,3E9
00401048  |.  75 0A         jnz     short ODbgTest.00401054
0040104A  |.  6A 00         push    0                        ; /Result = 0
0040104C  |.  FF75 08       push    [arg.hWin]               ; |hWnd
0040104F  |.  E8 8E000000   call    ODbgTest.EndDialog       ; \EndDialog
00401054  |>  EB 1A         jmp     short ODbgTest.00401070
00401056  |>  83F8 10       cmp     eax,10
00401059  |.  75 0C         jnz     short ODbgTest.00401067
0040105B  |.  6A 00         push    0                        ; /Result = 0; Case 10 (WM_CLOSE) of switch 00401022
0040105D  |.  FF75 08       push    [arg.hWin]               ; |hWnd
00401060  |.  E8 7D000000   call    ODbgTest.EndDialog       ; \EndDialog
00401065  |.  EB 09         jmp     short ODbgTest.00401070
00401067  |>  B8 00000000   mov     eax,0                    ;  Default case of switch 00401022
0040106C  |.  C9            leave
0040106D  |.  C2 1000       retn    10
00401070  |>  B8 01000000   mov     eax,1
00401075  |.  C9            leave
00401076  \.  C2 1000       retn    10
----------------------------------------------------
我们在来看一下用普通方式编译链接的OllyDbg反汇编代码:
----------------------------------------------------
00401000  /.  55            push    ebp
00401001  |.  8BEC          mov     ebp,esp
00401003  |.  8B45 0C       mov     eax,[arg.2]
00401006  |.  3D 10010000   cmp     eax,110                          ;  Switch (cases 10..111)
0040100B  |.  75 02         jnz     short ODbgTest.0040100F
0040100D  |.  EB 45         jmp     short ODbgTest.00401054          ;  Case 110 (WM_INITDIALOG) of switch 00401006
0040100F  |>  3D 11010000   cmp     eax,111
00401014  |.  75 24         jnz     short ODbgTest.0040103A
00401016  |.  8B45 10       mov     eax,[arg.3]                      ;  Case 111 (WM_COMMAND) of switch 00401006
00401019  |.  8BD0          mov     edx,eax
0040101B  |.  C1EA 10       shr     edx,10
0040101E  |.  25 FFFF0000   and     eax,0FFFF
00401023  |.  0BD2          or      edx,edx
00401025  |.  75 2D         jnz     short ODbgTest.00401054
00401027  |.  3D E9030000   cmp     eax,3E9
0040102C  |.  75 0A         jnz     short ODbgTest.00401038
0040102E  |.  6A 00         push    0                                ; /Result = 0
00401030  |.  FF75 08       push    [arg.1]                          ; |hWnd
00401033  |.  E8 66000000   call    <jmp.&user32.EndDialog>          ; \EndDialog
00401038  |>  EB 1A         jmp     short ODbgTest.00401054
0040103A  |>  83F8 10       cmp     eax,10
0040103D  |.  75 0C         jnz     short ODbgTest.0040104B
0040103F  |.  6A 00         push    0                                ; /Result = 0; Case 10 (WM_CLOSE) of switch 00401006
00401041  |.  FF75 08       push    [arg.1]                          ; |hWnd
00401044  |.  E8 55000000   call    <jmp.&user32.EndDialog>          ; \EndDialog
00401049  |.  EB 09         jmp     short ODbgTest.00401054
0040104B  |>  B8 00000000   mov     eax,0                            ;  Default case of switch 00401006
00401050  |.  C9            leave
00401051  |.  C2 1000       retn    10
00401054  |>  B8 01000000   mov     eax,1
00401059  |.  C9            leave
0040105A  \.  C2 1000       retn    10
----------------------------------------------------
我们可以对两者做一些对比,发现前者的可读性好,便于理解
例如:
0040101F  |.  8B45 0C       mov     eax,[arg.uMsg]
00401003  |.  8B45 0C       mov     eax,[arg.2]
很明显,上面一句一看就知道是干什么!

================================================================================
好,下面再讲源代码的读取:

在OllyDbg主菜单中点选:
查看->源码文件,打开“源码文件”窗口,在窗口中我们可以看到如下内容:
---------------------------------------------------------
模块     |  源码         | 源码路径
---------------------------------------------------------
ODbgTest |  (缺位)       | DlgProc\DLgProc.asm
ODbgTest |  ODBGTEST.ASM | E:\debug\ODbgTest\ODbgTest.asm
---------------------------------------------------------
此时,我们用鼠标双击想要查看的源码文件,便可打开“源码文件窗口”
例如我们双击ODBGTEST.ASM,看到的结果就是这样的:
-------------------------
  1.|.386
  2.|.model flat, stdcall
  3.|option casemap :none
  4.|include ODbgTest.inc
  5.|include DlgProc\DLgProc.asm
  6.|.code
  7.|start:
> 8.|  invoke GetModuleHandle,NULL
> 9.|  mov    hInstance,eax
>10.|  invoke InitCommonControls
>11.|  invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
>12.|  invoke ExitProcess,0
13.|end start
---------------------------
是不是与我们编写的源码一摸一样?
-------------------------------
但这里有个问题,DLgProc.asm文件被显示为“ (缺位)”,且当我们双击它时,根本就看不到源码。
做个对比发现,ODbgTest.asm的“ 源码路径”是一个完整路径,而DLgProc.asm的不是。
那么,再看一下我们自己编写的源代码:
-------------------
ODbgTest.Asm:
-------------------
.386
.model flat, stdcall
option casemap :none
include ODbgTest.inc
include DlgProc\DLgProc.asm    ; <--这里,改成完整路径
.code
start:
....
-------------------
将第5行改成include E:\debug\ODbgTest\DlgProc\DlgProc.Asm
然后再重新编译链接,再用OllyDbg打开“源码文件”窗口看到如下:
----------------------------------------------------------------
模块     |  源码         | 源码路径
----------------------------------------------------------------
ODbgTest |  DLgProc.asm  | E:\debug\ODbgTest\DlgProc\DlgProc.Asm
ODbgTest |  ODBGTEST.ASM | E:\debug\ODbgTest\ODbgTest.asm
----------------------------------------------------------------
怎么样,正常了吧。再双击DLgProc.asm文件,便能看到它的源码了。
----------------------------------------------------------------
回到上一步,有朋友可能会发现,在“源码文件”窗口中,就连那些“ (缺位)”的文件都看不到
这可以通过OllyDbg选项对话框中设置:
单击“调试”选项卡,将最后的“隐藏不存在的源文件”前的对勾去掉。
(英文原版是Debug->Hide non-existing source files)

=========================================================================
好,下面再来讲讲源码级调试的一些要点:
首先,我们需要做一些必要的设置和调整:
同时打开“CPU窗口”(就是反汇编窗口)、“源码文件”窗口和“源码”窗口,并适当地调整它们的位置和大小。(这样似乎需要一个大点的显示器,普通17寸的最佳分辨率是1024*768,呵呵)
接下来在“调试选项”对话框中,点选“CPU”选项卡,勾选“根据CPU同步源码”
(英文原版是CPU->Synchronize source with CPU)
这样,我们在“CPU”窗口中作单步调试时,在“源码”窗口就可看到,光标棒也跟着相应的走动了。
怎么样,这样一来我们就很清楚的知道正在调试的汇编指令对应的源码了吧。
==========================================================================
另:我们也可以在“CPU窗口”中,单击第四栏的标题,在注释、源码、统计之间切换,在“源码”状态下,“CPU窗口”可同时看到反汇编代码和汇编源码,只是此时看不到OllyDbg中通过“;”键所做的注释以及在源码中占单独一行的注释。而且,CPU窗口中的源码也看不到代码缩进。使用之前所说的单独“源码窗口”的优势在于可看到源码的全貌以及配合“源码文件”方便查找代码。
另:在源码中,路径不要带中文。
==========================================================================

关于源码级调试的其它内容就不多说了,大家可自行研究。
==========================================================================

倘若大家发现我以上所说有误或有更好的方法,还请来信告知,大家分享嘛,呵呵^_^
陈健良(下雪了)
Chensnowing@yeah.net
2005年8月7日 16:53:57


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (16)
雪    币: 133
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持一下,找这个很长时间了,谢谢
2005-8-7 19:13
0
雪    币: 207
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
另,我在调试区有个求助,希大家能帮帮。
http://bbs.pediy.com/showthread.php?s=&threadid=15856
2005-8-7 19:13
0
雪    币: 1223
活跃值: (469)
能力值: (RANK:460 )
在线值:
发帖
回帖
粉丝
4
不错
2005-8-8 22:00
0
雪    币: 213
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
ollydbg配色不错啊, 是否可以共享 一把.
2005-8-11 16:45
0
雪    币: 98782
活跃值: (201049)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
6
2005-8-11 16:51
0
雪    币: 61
活跃值: (160)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
7
thank you
2005-8-19 17:55
0
雪    币: 208
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
请问你用的什么编译器? 下面的脚本要写在哪呢? 谢谢!!

下面的编译链接所用的选项比较重要,就是要添加调试信息
OllyDbg调试:
rc ODbgTest.Rc
ml /c /coff /Cp /Zi ODbgTest.Asm
link /SUBSYSTEM:WINDOWS /DEBUG /DEBUGTYPE:CV ODbgTest.obj ODbgTest.res
完成后,我们便可以得到可执行程序ODbgTest.exe。
2005-9-4 22:25
0
雪    币: 298
活跃值: (445)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
9
最初由 walaog 发布
请问你用的什么编译器? 下面的肢本要写在哪呢? 谢谢!!

下面的编译链接所用的选项比较重要,就是要添加调试信息
OllyDbg调试:
rc ODbgTest.Rc
........


如果是radasm的话
Tools->notepad(.rap)

-----
[MakeDef]
Menu=1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0
1=4,O,$B\RC.EXE /v,1
2=3,O,$B\ML.EXE /c /coff /Cp /Zi/nologo /I"$I",2
3=5,O,$B\LINK.EXE /SUBSYSTEM:WINDOWS /debug /VERSION:4.0 /LIBPATH:"$L" /OUT:"$5",3,4
4=0,0,,5
5=rsrc.obj,O,$B\CVTRES.EXE,rsrc.res
6=*.obj,O,$B\ML.EXE /c /coff /Cp /nologo /I"$I",*.asm
7=0,0,"$E\OllyDbg",5

----
2005-9-4 23:38
0
雪    币: 234
活跃值: (370)
能力值: ( LV9,RANK:530 )
在线值:
发帖
回帖
粉丝
10
不错的东西
2005-9-5 12:51
0
雪    币: 208
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
我的是EditPlus,怎么设呢?
2005-9-6 23:01
0
雪    币: 111
活跃值: (55)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
请问od在注释区出现参数解析是怎么设置出来的?
我调了好久都没调出来...
-____-
2005-9-7 08:40
0
雪    币: 1223
活跃值: (469)
能力值: (RANK:460 )
在线值:
发帖
回帖
粉丝
13
那个是调试信息。需要包含调试信息的程序才行,就是debug版本的程序。
2005-9-7 09:41
0
雪    币: 216
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
看不懂啊,能不能把ReadASN中的设置讲明白点啊
2005-10-7 16:18
0
雪    币: 216
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
最初由 Immlep 发布


如果是radasm的话
Tools->notepad(.rap) ;(.rap)这是什么意思?

........


能不能换个简单点的例子啊,我是新手看不懂啊
能不能用个只有一个.asm文件的工程来讲解啊,我不知道上面那个程序为什么要有两个.ASM文件

我只会用ReadASM,其他的都看不懂,有没有人能帮帮忙照顾下新人啊
2005-10-7 16:40
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
全世界最简单明了的方法:

在RadASM点工选项→将发行改为调试,再编译,用OD调试就行啦
2006-2-15 00:02
0
雪    币: 207
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
我考,有些久了,又浮上来了。也好,乘此机会上来冒个泡。
=====================================================
事后我也觉得,我写得太?嗦,其实要点也就是几句话:
编译连接时要加入调试信息:
    ml /c /coff /Cp /Zi xxxx.Asm
    link /SUBSYSTEM:WINDOWS /DEBUG /DEBUGTYPE:CV xxxxx.obj xxxxx.res
注意路径的表示方法:
    对于代码中的include使用不带中文的完整路径

不管是用什么编代码,在编译连接时加入调试信息是必须的,除非不准备作源码调试。
我在文中所述的是命令行下手工编译连接的方式(我认为这样说明问题是最合适的),而具体到如RadASM之类的软件,只是设置上的不同,其实质还是一样的。
(回楼上的,RadASM我用过,但在旧版中它的默认设置有些问题,需要手工更正,当然新版的如何我不知,因为好久没升级了)

事实上,使我写此文的原因主要是路径问题。
因为我通常有将代码根据需要分成多个文件的习惯,且可能放于不同文件夹下,这就发现OllyDbg找不到源码文件了。我的解决办法是“完整路径”。这也就是例子中使用两个文件的原因--为说明问题。
=====================================================
泡冒完了,氧气瓶里的氧气也冲足了,继续潜水。。。。。。
2006-2-19 23:57
0
游客
登录 | 注册 方可回帖
返回
//