我们都知道用Spy++去查看窗口句柄的相关信息,但是这款工具无法找到消息窗口(Message-Only Windows)。所以写了个查看消息窗口的工具,帮我排查一些这方面的问题。
下载:MsgOnlyWnd
我们都知道用Spy++去查看窗口句柄的相关信息,但是这款工具无法找到消息窗口(Message-Only Windows)。所以写了个查看消息窗口的工具,帮我排查一些这方面的问题。
下载:MsgOnlyWnd
c06d007f这个异常通常是在PE的延迟加载dll的时候发生的,加载器找不到对应的dll就会抛出这个异常。如果我们对这个异常不熟悉,按照常规方式去找上下文,那么结果肯定会让你失望。例如3.2526.1373.0版本的libcef在XP上运行的情况。
|
直接看栈回溯或者通过设置cxr看栈回溯,并没有帮助我们找到什么有用的信息。
这里要使用的方法是,利用异常的参数来找到具体延迟加载谁的时候发生了异常。
|
这里的参数0,就是我们要找的目标,记录了出错时候ebp-0x30的数据,也就是含有关键信息的地方。让我们仔细看看:
|
我们可以清楚的看到加载器延迟加载SymGetSearchPathW的时候发生了问题。让我们进一步用depends工具验证一下
如上图所示,XP自带的dbghelp里没有SymGetSearchPathW这个导出函数。要解决这个异常,实际上就需要在运行目录里添加一个稍微新一点的dbghelp文件,我这里替换的是6.2.9200.16384的dbghelp,替换过后问题已经不再出现了。
这篇Blog分享一个Windbg的小技巧,就是让被调试程序更早的中断到调试器。熟悉Windbg的朋友都知道,用调试器运行程序,默认情况下都会中断到ntdll!LdrpDoDebuggerBreak。但是有时候我们会想去调试程序加载的过程,这个时候就需要我们更早的中断下来。那么这里就用利用到调试器最早接受到的调试事件了。CREATE_PROCESS_DEBUG_EVENT,这个调试事件是创建进程的时候进程发给调试器的,在这个时候,你甚至连ntdll都没有完成加载,这也导致ntdll的符号无法加载,很多有用的功能用不上。但幸运的是,虽然ntdll没有完成加载,但是已经加载到了内存,另外我们可以用手动加载符号的方法,把符号文件加载到ntdll的内存上去。
演示如下:
windbg.EXE -xe cpr -xe ld notepad.exe
这里设置中断系统事件cpr,也就是CREATE_PROCESS_DEBUG_EVENT
|
中断下来后我们可以看到,!teb是没法用的
|
我们需要找到ntdll的模块,然后手动加载符号,然后就可以使用和ntdll有关系的命令了。
|
过去的2015年应该是我工作和生活中的一个大转折。
这一年里,我选择从北京回到了武汉。这件事情上还是需要一些魄力的,最主要的就是收入拦腰截断,剩下原来的二分之一。其次发展空间上也不能和帝都相提并论。就拿接到猎头电话这件事情上说,回来大半年里,猎头电话一个接一个,但是绝大部分都是北京打来的。不过,在家乡有父母,有亲戚,有未婚妻,这种“交换”也是值得的,毕竟我不能期待什么都能得到。回来后,健身依然在坚持,只不过没有跑步机,还是觉得缺少了很多东西。自己的兴趣方面,MiniKernel,编译器和虚拟机依旧没什么进展,有一种写不动了的感觉,有进展的依旧是小工具合集,有些工具增加了一些新的功能,比如everything_study就优化了算法,现在查找速度已经和everthing看不出区别了。今年最美好的事情就是求婚,最悲催的事情就是学车。求婚对每个人来说想必都是最美好的事情,这个自然不必多说。至于学车,也是找了个不靠谱的驾校,被坑的不轻。幸运的是自己对车接受的比较快,没被教练坑的太惨,科目一到科目三都是满分通过,现在就剩下科目四了,春节前就把驾照给拿了。另外IXWebhosting这个主机我也不准备用了,换成GitHub Page来当blog,过段时间把0CCh.net这个域名也转移的godaddy算了。
新的2016将会是一个真正新的开始!我将在这一年组建自己的小家,要买车,要装修房子。工作上希望武汉的互联网大环境会更好,希望我的劳动能给公司带来更高的价值。健身方面,我打算在新家里买上一个跑步机,过时如同北京时那样的健康生活。另外,练字也应该继续。兴趣方面,小工具集可以继续壮大,MiniKernel,编译器和虚拟机中,我更倾向多花时间写写编译器。
另外,好友初步完成了自己的梦想,去美国工作了。很羡慕,祝福他能扎根那边,别回来吸雾霾了=v=。
最后,还是祝愿家人,朋友,在新的2016健健康康,平平安安,开开心心,财源广进!
Visual C++ 的编译器用0xE06D7363表示C++异常。 0xE06D7363表示的意思就是.msc。
|
抛出异常代码的同时,还会带有三个到四个参数:
参数0是一个magic code,一般为0x19930520,我们不用管他
参数1是时异常抛出的对象指针
参数2是抛出异常的基本信息
参数3是抛出异常的模块基址(只有64位的程序才会有这个参数),该基址加上异常信息的偏移才能获得信息的真正内存地址。
|
6b5d0298就是我们想要取得的信息,信息存储的格式为_s__ThrowInfo。
|
然后可以取得pCatchableTypeArray,我们可以从中获取抛出异常的类型信息。
|
到这里我们就取得了类型的描述结构体了,最后就能从中获取抛出的异常类型
|
想必大家都知道著名的全盘搜索工具everything,它极速的搜索速度让人眼前一亮。虽然everything提供了SDK,但是SDK是通过IPC的方式,获得everything程序里的数据。也就是说想在自己的程序中使用搜索功能那么必须带everything的主程序,这就是我开发gotcha sdk的主要原因,他能集成到程序当中,不需要依赖其他主程序,只需要你的程序是管理员权限运行,因为这样才能直接访问磁盘数据。另外网上也有一些关于everything原理和实现的代码,但是大部分都有问题,比如崩溃,死锁,内存占用过高等,并不适合直接用到产品当中。而gotcha sdk在自己开发了everything_study,并且使用了相当长的时间,解决性能,内存占用,死锁等问题的基础上提炼出来的开发库,我对其稳定性还是比较有信心的。
利用gotcha sdk,既可以开发出everything_study这样用C++写的程序,也能够开发出如gotcha sdk的sample里的gotcha,一个C#编写的全盘搜索程序,该程序也展示了gotcha sdk的用法。
gotcha sdk的用法非常简单,详细情况可以参考sample里的simple例子,该例子展示了sdk最简单的使用方式,我下一篇blog会介绍这套sdk的用法。
gotcha sdk 代码SVN:
http://code.taobao.org/svn/gotcha_sdk/
拿到程序初始化失败的DUMP,一般情况下我们看到的栈是这个样子的:
|
可以看到最后报错是c0000145,应用程序无法运行。而引起出错的是LdrpInitializationFailure,出错原因内存访问异常。但是具体是哪出错还不无法从此刻的栈看到,我们需要进一步分析。
|
这里我们就可以看到异常发生的栈了。
|
可以看到正在加载someapp.dll,并且处理导入表的时候出了错。来看看这个模块的导入表
|
所以这样就清楚了,someapp.dll的输入表被破坏了,导致加载他的程序无法运行起来。
0cchext.dll是我一直在开发和维护的一个Windbg扩展程序。扩展程序中包含了一些或者有趣,或者实用,或者纯个人偏好的功能。这篇文章就来介绍一些主要的功能:
!a - Assembles instruction mnemonics and puts the resulting
instruction codes into memory.
这个指令是写入汇编代码的扩展,虽然Windbg有自己的汇编命令a,但是这个命令无法配合脚本使用。你一旦输入命令a,Windbg就会进入汇编模式,此时你就无法让脚本继续进行了。所以我开发了!a,这个命令只会对一条命令进行汇编,并且将下一条汇编的地址存储在@#LastAsmAddr中,然后马上执行下面的命令,对脚本而已再好不过了。
例如下面这个脚本,他可以注入dll到debuggee
ad /q ${/v:alloc_addr} |
!autocmd - Execute the debugger commands.(The config file is
autocmd.ini)
自动执行特定指令。有的时候我希望调试器附加到进程或者运行程序的时候能够自动运行一连串的命令,这个功能虽然可以由脚本完成,但是对我而言还是不够简洁,所以就有了这个命令。我可以在0cchext.dll的目录下,创建autocmd.ini文件,然后输入以下内容:
[notepad.exe] |
这样,在调试不同程序的时候输入!autocmd会执行不同的命令。
!bing - Use bing to search.
!google - Use google to search.
这个命令非常简单,就是用bing和google去搜索指定的字符串。
!favcmd - Display the favorite debugger commands.(The config file is
favcmd.ini)
这个命令也非常简单,只需要把自己喜欢的命令一行一行的写在favcmd.ini文件里就行了,当然这个文件也需要和0cchext.dll在同一个目录。然后运行这个命令后,你所喜欢的命令就会打印到Windbg上,你可以用鼠标选择执行这些命令。
例如在文件中分别写入:
~*k
!address
!heap
!hwnd - Show window information by handle.
这个命令很简单,可以输入窗口句柄为参数,查看窗口相关信息。主要作用是在内核调试的时候,用调试器看到窗口信息会比较方便。
!url - Open a URL in a default browser.
这个命令会打开一个url,实际上他就是一个ShellExecute。Windbg本来就有.shell功能了,这个似乎是多余了一点。
!init_script_env - Initialize script environment.
这个命令是我给脚本准备的,他方便了脚本判断系统环境。如下图所示
!import_vs_bps - Import visual studio breakpoints.
这个命令可以将VS存储在suo文件的断点导入到Windbg中。我有的时候会碰到这样的情况,VS里设置了一堆断点,但是调试环境里只有Windbg,那么我需要把这些断点转移到Windbg,有了这个命令,我只需要将VS解决方案的suo文件拷贝到调试环境中,然后运行这条命令即可。
例如
!import_vs_bps c:\proj\xxx.suo
!setvprot - Set the protection on a region of committed pages in the
virtual address space of the debuggee process.
这个命令能帮助我设置debuggee的内存属性,一个有趣的用法就是模仿Ollydbg的内存断点功能,比如给目标内存设置一个PAGE_GUARD属性,这样访问这部分内存的时候就会触发访问异常,调试器就能捕获到它了。
例如
!setvprot 0x410000 0x1000 0x100
!pe_export - Dump PE export functions
!pe_import - Dump PE import modules and functions
这两个命令可以分别帮助我们查看导出和导入函数,他们都支持通配符查找函数,在没有符号的情况下有时候会起到很好的作用。另外,他们配合好参数/b和.foreach命令,可以发挥出API Monitor的作用。
例如
.foreach( place { !pe_export /b kernel32 *Create* } ) { bp place “g” }
!wql - Query system information with WMI.
这也是我比较喜欢的一个功能,他可以在调试的时候通过WQL来查询系统的一些信息,例如:
0:000> !0cchext.wql select * from win32_process where name="explorer.exe" |
!logcmd - Log command line to log file
这个命令是一个开关,打开后,他会记录调试的命令到文件中,这样下次调试相同的程序的时候就不需要在此去输入这些命令了,只需要读取这个命令文件,就可以用鼠标点击执行命令了。
!dpx - Display the contents of memory in the given range.
这个命令是集dps dpa dpu大成者。他的会对目标指针做一个简单的判断,判断是符号,字符串,还是宽字符串。这样在我们查看栈信息的时候就不会漏掉一些有用的线索了。
0:000> !dpx esp 100 |
!dtx - Displays information about structures. (The config file is
struct.ini)
这个命令主要用在逆向工程的时候。因为逆向工程的时候,我们往往没有符号文件,就不可能直接知道内存数据的结构是什么样子的,我们需要自己通过代码推断出来。在IDA中,我们可以自己设置结构体帮助分析。但是在Windbg中,并没有一个功能能方便的帮助我们用这推断的结构体去显示内存。不可否认我们其实可以用其他的办法来完成这个目的,但操作很繁琐。那么这个命令就解决了这些问题。我们可以在struct.ini文件中写入我们推断的结构体,然后通过这个命令去打印内存数据。当然,这个文件也必须在0cchext.dll的同目录下。
到目前位置脚本解析器支持的基本类型有BYTE WORD DWORD CHAR WCHAR,支持数组和指针,支持结构体嵌套,有了这些,对于基本的逆向就能够满足需求了。
现在0cchext.dll就是这些命令了,我也会根据自己的需求继续添加命令,如果你有什么有趣或者实用的想法,可以通过邮件或者留言告诉我。
Foxmail是一款不错的邮件客户端软件,小巧实用,我在公司就是用的它。早上一如既往的先打开Foxmail,然后去倒杯水,回来发现Foxmail还在收取邮件的状态,“这也太慢了”,我心想。用鼠标点了点,出现了程序挂起的特征,标题栏上显示无法响应,程序界面变白。经验告诉我挂起的问题70%都还是比较容易调的,好吧,就让我看看这是怎么回事。
打开Windbg,Attach到Foxmail上,习惯做的第一件事情就是保存Full dump
.dump /ma /u e:\foxmail.dmp |
由于程序的主界面出现了挂起的现象,而一般情况下主线程就是程序的界面线程,所以此时根本没必要去查看所有线程的情况,直接看看主线程的栈信息吧。
0:000> k |
可以看到界面线程调用GlobalAlloc的时候在等HeapLock被释放。
0:000> kb L5 |
那么014d0138比如是默认堆的Critical Section了。来看看这个cs的数据
0:000> !cs 014d0138 |
嗯,ntdll的符号文件的结构体信息没有了!这个问题发生在2015年7月份的,安装KB3071756和KB3060716补丁后产生的。详情可以查看
http://www.osronline.com/showthread.cfm?link=269221
既然新的符号不让用,那就只有用老的了
0:000> .reload /f /i E:\WorkSpace\MySymbols\ntdll.dll\49900AFA96000\ntdll.dll=77ffe000 |
好了,这样就够用了。可以看到TID=1730的线程正在占用这个cs,马上去看看这个线程在干什么。
|
从栈的信息看来,这个线程是OpenSSL的一个线程,正在做随机数处理,而枚举Heap的信息应该也是随机数的一个组成部分。在枚举Heap的时候也处于等待一个HeapLock的情况,来具体看看
0:041> kb L5 |
看来正在等一个基地址是04560000的Heap的cs。顺藤摸瓜看看这个cs又被谁占用了。
0:041> dt ntdll_77ffe000!_RTL_CRITICAL_SECTION 04560138 |
继续看看0x00001994这个线程在做什么事情
0:002> kb |
这个线程正在为04560000这个heap创建LowFragHeap,但是在获取014d0138的cs的时候被卡住了。这里就发现问题所在了!014d0138不正是我们主线程在等待的cs么,这个线程的10f0和1994都在等待1730的cs=014d0138,而1730却在等待1994的cs=04560138。
反汇编RtlpQueryExtendedHeapInformation就能知道这里造成死锁的真正原因。实际上Heap32Next函数枚举所有Heap信息的时候,先统一锁住进程里面所有的Heap,然后做枚举工作,最后再统一释放锁。所以它在正在锁住所有HeapLock过程中的时候,例如锁住了一半,正在尝试锁住另一半,另外一个线程正好拥有他没锁住的HeapLock,但是不巧的时候他在给这个Heap创建LowFragHeap,而创建LowFragHeap需要初始化HeapLock,其中HeapLock的DebugInfo又是用默认Heap去分配内存的,默认堆的锁正好在第一个线程被锁住的那一半里,这就是事情的真相!
那么微软为什么要提供一个会造成死锁的API,而且不去修复呢?按照Raymond的说法,这个系列的函数目的只是诊断,性能很低,不应该用于普通程序中。
http://blogs.msdn.com/b/oldnewthing/archive/2012/03/23/10286665.aspx
但是,我觉得既然是文档化的接口,而且文档里面没有提到会造成死锁,那么它就应该是安全的。
####PickIconDlg
相信给快捷方式指定过图标的朋友肯定看过一个这样的对话框吧,如果你看到过,你肯定已经知道了这个API是怎么一回事。这个API会弹出一个选择图标的窗口给你选择,确定后返回图标在资源中的索引值。这样你可以通过这个索引值和ExtractIcon函数获得这个图标的句柄。
示例代码如下:
int Index = 2; |
####WNetConnectionDialog和WNetConnectionDialog1
这两个函数是帮助我们在程序中显示映射网络驱动器对话框的,虽然用的不多,但是也应该见到过它。这两个函数区别不大,只不过WNetConnectionDialog1比WNetConnectionDialog提供了更多的参数去设置。
示例代码如下:
|
####SHOpenWithDialog
这个API所显示的对话框我们应该是最多见的,它显示了一个打开方式的对话框。不过有点可惜的是,XP并不支持这个API,我们只能将它用在Vista开始的系统上。
示例代码如下:
OPENASINFO Info = { 0 }; |