能力值:
(RANK:570 )
2 楼
在调试的过程中,有时我们希望自动化解决一些问题。例如调试使用了UnhandledExceptionFilter的SEH,我们需要自动修改ZwQueryInformationProcess的返回值。或者对于某些API的ANTI DEBUG,如果我们修改了输入参数,同样不能返回应该返回的值。学破解不久,一下子要找用了UnhandledExceptionFilter的软件还真不容易,用别的API代替了。我用OD把NOTEPAD修改一下,改名为TEST放在附件中。
流程MESSAGEBOXW,GETCOMMANDLINEW,MESSAGEBOXW输出COMMANDLINE,最后EXITPROCESS。
现在我要做的是改变GETCOMMANDLINEW的输出,和第二个MESSAGEBOXW的输入。现在让我们看看test2.txt
-------------------------------------------test2.txt-----------------------------------------------
g $exentry
r $t0=0
bp messageboxw "r $t0=$t0+1;j($t0=2)'r $t1=poi(esp+8);f $t1 l4 45;g';g"
bp getcommandlinew "g poi(esp);r $t1=eax+5;f $t1 l4 55;g"
g
------------------------------------------test2.txt完结---------------------------------------------
首先对相关指令作一些介绍
BP 地址或者函数名 "命令" 命令参数是可选的,存在的情况下,中断的同时会先运行那些命令。
J(条件表达式)'命令1';命令2 相当于.if但是又有点不同命令2只能是1个,后面所有命令会被忽略。
POI() 返回指针的指向位置的内容。
!= 不等于
这里用了条件中断的方法实现,第一个条件中断指令用$t0作为计数器,第二次中断的时候变修改堆栈中指针指向位置的内存区域。注意到调用API的返回地址在ESP中,直接跳出去,然后修改EAX就可以达到修改函数输出参数的效果了。
这里提供第二种可行的方法,并且更有可扩展性,现在看看test.txt中的代码。
-------------------------------------------test.txt------------------------------------------------
g $exentry
r $t0=0
bp messageboxw
bp getcommandlinew
bp exitprocess
.while (eip!=77e7b0bb){
g
.if($ip=77e116cc){
r $t0=$t0+1
.if($t0=2){
r $t1=poi(esp+8)
f $t1 l4 45
}
}
.if($ip=77e7c693){
g poi(esp)
r $t1=eax+5
f $t1 l4 55
}
.elsif($ip=77e7b0bb){
.break
}
}
g
------------------------------------------test.txt完结----------------------------------------------
仍然先介绍一些指令:
.while(条件表达式){} 跟C语言中的一样,循环结构,直到条件表示式为真
.elsif(){} 跟前面的.if用法一样,它的作用如字面上意思,只是小心别拼错为ELSEIF
.break 跟C语言中的一样,跳出循环。
如果在条件为真的时候不用.break跳出循环就会出错,这点要注意。
这里构造了一个循环结构,并且通过对比EIP的方法来识别函数,同样地因为我的虚拟机是WIN 2000 连SP1都不是,所以我不肯定该地址在你的机器中仍然可用。不过这里提供了一个思路,你可以用这个方法构造一个SCRIPT来加强WINDBG的功能,例如象OD一样中断的时候自动显示所有参数,并且带上英文提示那是什么参数。同样地,我们可以做一个自动化分析SCRIPT,分析每个CALL中包含了什么API,并且列出输入和输出参数,CALL的深度还指令数,并且自动生成报告文件,假如有人开发出这样一个SCRIPT,调试分析将会变得容易。WINDBG里面有个相似功能的指令。
WT 自动跟踪并生成报告,几乎跟我上面说的一样。带/l参数的时候可以设置深度,不过很多时候,我们看到一个CALL并不知道里面究竟有多深,但是我们希望得到一些关于那个CALL的详细信息来判断是否值得跟进。这里有两个问题:
1 递归,那这个指令不知道运行多久。
2 大量NATIVE API调用,显然大多数情况下,我们并不关心。
比起1,2更加常见,/i参数是用来避开指定模块的,不会用,帮助文件里也没提。。。。希望有大大能答我这个问题
WINDBG提供了下面3个指令用于保存分析过程进文件,通过适当的开关可以过滤一些无意义的信息,使分析过程易于观看。
.logopen 文件路径 带/U参数则以UNICODE方式输入文本。重写整个文件,并记录当前命令窗口在使用该指令之后的所有内容。
.logclose 文件路径 停止记录并关闭文件。
.logappend 文件路径 带/U参数则以UNICODE方式写文件。记录当前命令窗口在使用该指令之后的所有内容,并添加进文件。
提到了功能强化,大家都知道OD里面有个命令是运行到RET处吧,在WINDBG中似乎没有这样的指令,类似的有PC,即运行到CALL。我写了一个SCRIPT来模拟OD中的那个指令。现在我们来看看goret.txt
-------------------------------------------goret.txt------------------------------------------------
r $t0=0
.while(@$t0!=c3){
p
r $t0=by(eip)
.if(@$t0=c3){
.break
}
}
-----------------------------------------goret.txt完结----------------------------------------------
这里是最后一个示例分析,所以除了解释上面的指令之外也给出一些有价值的指令
not 非 and或者& 与
hi() 取高16位 or或者^ 或
low() 取低16位 xor或者| 异或
by() 取低8位 gu 步出,不知道具体原理,有时会出错
wo() 取低16位 t 步入
mod或者% 模运算
这个SCRIPT使用了一个循环,通过EIP取得当前指令的机器码,低8位既为指令,然后把指令存进$t0作比较。C3是RET的机器码,等于则跳出循环,否则一直步过。
这个示例表明,我们可以在SCRIPT里分析每一条指令。我们可以在WINDBG中进行2次开发,动态将那些简单使用JMP+内存指针或者寄存器作为跳转的乱序的程序重新排序,使花指令失效,并且实现自动清除垃圾指令,最后生成优化后的汇编代码文件。本论坛翻译区里的变形多态中的关于收缩器的理论已经为我们奠定了理论基础。
你可能会需要用到反汇编指令
u 起始地址 l长度 L代表的不是地址长度而是指令的个数
---------------------------------后续讨论,用SCRIPT把WINDBG变成脱壳机------------------------------
在准备写这篇文章的时候,我又把DEBUGGER COMMANDS看了一次,发现了这个指令
.writemen filename range 将目标内存区域写进文件。RANGE的格式为 地址 l长度
我没试过L后面是否接受寄存器作为参数。也没实际测试过这个指令的具体操作是怎么样的,无论如何,有这个可能存在。当然我们也可以申请内存区域以程序的方式来完成这个工作,不过我希望它仅用SCRIPT完成。
假如这的确可行,可以通过下面指令组合来自动寻找文件头,当然也能确定文件大小。
$p 伪寄存器,它将返回前一次用d*指令所显示的内存的内容。
假设00100000 01 02 03 04 05 06 07 08
我使用dd 00100000,那么$p = 04030201
显然我们可以通过这个方法来访问内存。
dw 取一个WORD; dd取DWORD; dw取qword
能访问内存也代表说我们在调试程序中插入的代码也能跟SCRIPT通信,并且把一些SCRIPT无法完成的工作交给程序执行,然后把结果返回给SCRIPT。
假如l的参数无法通过寄存器来传递,只能依靠用户按照提示进行操作,那么我们有更简单的方法
.imgscan 它将返回所有模块MZ的地址和它的SIZE
----------------------------------这里给出一些可能的疑问和解答--------------------------------------
Q:在调试SCRIPT文件的时候,我该如何知道寄存器跟内存的变化?
A:我们可以用下面的指令来观察寄存器跟内存的变化
d* 用于显示内存,之前已经提到就不详细说明了
? 寄存器 显示寄存器的值,例如
? poi(esp); ? $t0
这将先显示ESP指向的值,然后显示$t0的值
除了可以使用.echo命令对显示参数作说明之前,也可以使用.printf作格式化输出,它的用法跟C语言中的printf是一样的
Q:我写的SCRIPT文件出错了,语法跟参数都没错,为什么我找不到出错原因?
A:有的指令要注意的,BA只能在进入程序区域之后才能用。.dvalloc申请过的内存,即使用.dvfree释放了,也无法在同样的位置再申请,可能是BUG。
*是一个注释命令,它后面所有的内容都会被当作字符
$$则是以分号为结束
.restart指令跟.wtitle指令,不知道为什么不能放在SCRIPT中使用。
还有就是@这个标记,这个标记是告诉WINDBG后面的是一个伪寄存器而不是程序里的某一个变量的符号。有的指令在没有@标记的时候会报错,例如.while括号里的条件表达式,如果你用了伪寄存器,一定要在前面加上@否则一定报错。此外用帮助文件里的话来说,使用@,可以让SCRIPT文件运行得更快,因为在解读这个代码的时候不需要先搜索一次SYMBOL记录。
Q:我能把功能模块化然后在其他SCRIPT文件中使用吗?
A:我已经测试过$$><指令也能在SCRIPT里面使用
Q:我写的SCRIPT FILE能在64位系统中用吗?
A:如果你仅使用SCRIPT来实现功能,那么很可能与64位兼容。尽量使用伪寄存器。
$ip,$retreg,$csp在32位系统中分别表示EIP,EAX,ESP,而在64位系统中则表示RIP,RAX,RSP也分别对应Itanuim处理器中的相关寄存器。
通过函数名获取不同版本下的地址,可以通过下面代码:
bp messageboxw ; $$第一个断点,断点ID为0
bp getcommandlinew; $$第二个断点,断点ID为1
r $t10 = $bp0; $$将第一个断点的地址转存$t10
r $t11 = $bp1; $$将第二个断点的地址转存$t11
bc *; $$清除所有断点。
这段代码运行之后MESSAGEBOXW的地址便存于$t10中,而$t11里面的则是getcommandlinew的地址。这里要说明,断点用完要释放,否则不好估计断点的ID,此外在内核模式中,最多只允许32个断点。
$peb和$teb返回当前进程的PEB和TEB地址,这里的翻译区有介绍如何仅通过PEB或者TEB判断当前操作系统类型
为不同系统准备不同代码
能力值:
(RANK:570 )
3 楼
总算写完了。。。。再废话几句
WINDBG看起来很难用,是因为用的人不多。
即使没有插件功能,WINDBG SCRIPT的功能也已经很强大了。
如果大家都来做SCRIPT,调试分析难度会降低很多,新手也可以通过阅读SCRIPT FILE来学习。搜索引擎使用得好的确可以学到很多,可惜这跟作者的表达和使用引擎者的表达有关,很可能相同的内容,因为表达方式不同就查不到了。就象我学校的图书馆,在电脑搜索逆向工程是什么都找不到的,但是搜索加密解密,却看到好几本书。
OD虽然好,始终是RING3的,SOFTICE似乎也已经停止开发了。希望大家都能加入WINDBG的行列
能力值:
(RANK:350 )
4 楼
最初由 笨笨雄 发布 OD虽然好,始终是RING3的,SOFTICE似乎也已经停止开发了。希望大家都能加入WINDBG的行列
确实如此,目前看来,掌握WINDBG不是坏事。
文章写的不错。
能力值:
( LV9,RANK:1060 )
5 楼
没有用过 windbg ,什么时候试试看。
能力值:
( LV9,RANK:1130 )
6 楼
这是一种趋势。值得学习。
惭愧,OD都没掌握完,谈不上windbg!
能力值:
(RANK:170 )
7 楼
随着SoftICE停止开发,使用WinDBG的人会越来越多
能力值:
( LV2,RANK:10 )
8 楼
很好 windbg吗
我要学了,不过,有没有基础一点的教程了
能力值:
( LV9,RANK:290 )
9 楼
windbg,try it.
能力值:
( LV9,RANK:250 )
10 楼
好文章实在要顶一下
能力值:
( LV2,RANK:10 )
11 楼
学习了,能否结合一些实例介绍调试bug常用命令与方法
能力值:
( LV9,RANK:140 )
12 楼
很实用..................
能力值:
( LV7,RANK:100 )
13 楼
实用型的文章
能力值:
( LV2,RANK:10 )
14 楼
mark!!!!