gotcha sdk 全盘文件名搜索开发库

想必大家都知道著名的全盘搜索工具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的用法。

20151124233627

gotcha sdk 代码SVN:
http://code.taobao.org/svn/gotcha_sdk/

NTInternals

程序初始化失败DUMP分析

拿到程序初始化失败的DUMP,一般情况下我们看到的栈是这个样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:000> kb
ChildEBP RetAddr Args to Child
0012fc7c 7c92d9ca 7c972b53 c0000145 00000001 ntdll!KiFastSystemCallRet
0012fc80 7c972b53 c0000145 00000001 00000000 ntdll!NtRaiseHardError+0xc
0012fca4 7c960f9f c0000005 0012fd30 00370034 ntdll!LdrpInitializationFailure+0x2d
0012fd1c 7c92e457 0012fd30 7c920000 00000000 ntdll!_LdrpInitialize+0x1f9
00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7
0:000> !error c0000145
Error code: (NTSTATUS) 0xc0000145 (3221225797) - {Application Error} The application was unable to start correctly (0x%lx). Click OK to close the application.
0:000> !error c0000005
Error code: (NTSTATUS) 0xc0000005 (3221225477) - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

可以看到最后报错是c0000145,应用程序无法运行。而引起出错的是LdrpInitializationFailure,出错原因内存访问异常。但是具体是哪出错还不无法从此刻的栈看到,我们需要进一步分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
0:000> dds esp-1000 esp
...
0012f3c0 7c92e920 ntdll!_except_handler3
0012f3c4 00000001
0012f3c8 0012f470
0012f3cc 0012fd0c
0012f3d0 7c953fdc ntdll!RtlDispatchException+0xb1
0012f3d4 0012f470
0012f3d8 0012fd0c
0012f3dc 0012f48c
0012f3e0 0012f444
0012f3e4 7c92e920 ntdll!_except_handler3
0012f3e8 003d3810 someapp!PostMsg+0x27aa0
0012f3ec 0012f470
0012f3f0 b36caf32
0012f3f4 00153960
0012f3f8 7c93e584 ntdll!DbgPrint+0x1c
0012f3fc 00150178
0012f400 000000e8
0012f404 00000668
0012f408 00150000
0012f40c 0012f204
0012f410 7c940571 ntdll!RtlCreateActivationContext+0x2c
0012f414 c0000000
0012f418 00153960
0012f41c 003f0000
0012f420 00000000
0012f424 0012f444
0012f428 7c940610 ntdll!RtlCreateActivationContext+0xed
0012f42c 001539b4
0012f430 00000002
0012f434 00000008
0012f438 00000000
0012f43c 00000000
0012f440 00000000
0012f444 0012f750
0012f448 7c814880 kernel32!CreateActCtxW+0x75c
0012f44c 00130000
0012f450 0012d000
0012f454 00000000
0012f458 0012f76c
0012f45c 7c92e48a ntdll!KiUserExceptionDispatcher+0xe
0012f460 00000000
0012f464 0012f48c
0012f468 0012f470
0012f46c 0012f48c
0012f470 c0000005
0012f474 00000000
0012f478 00000000
0012f47c 7c93ccf2 ntdll!LdrpHandleOneOldFormatImportDescriptor+0x21
0012f480 00000002
0012f484 00000000
0012f488 d16cca32
0012f48c 0001003f
...

这里我们就可以看到异常发生的栈了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
0:000> .exr 0012f470
ExceptionAddress: 7c93ccf2 (ntdll!LdrpHandleOneOldFormatImportDescriptor+0x00000021)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: d16cca32
Attempt to read from address d16cca32
0:000> .cxr 0012f48c
eax=003a0000 ebx=00253010 ecx=d132ca32 edx=00033810 esi=b36caf32 edi=003d3810
eip=7c93ccf2 esp=0012f758 ebp=0012f76c iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010282
ntdll!LdrpHandleOneOldFormatImportDescriptor+0x21:
7c93ccf2 833c0800 cmp dword ptr [eax+ecx],0 ds:0023:d16cca32=????????
0:000> kb
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr Args to Child
0012f76c 7c93ccc4 7ffd9000 00020498 00253010 ntdll!LdrpHandleOneOldFormatImportDescriptor+0x21
0012f784 7c93bc1e 7ffd9000 00020498 00253010 ntdll!LdrpHandleOldFormatImportDescriptors+0x1f
0012f800 7c93d216 00020498 00253010 00434398 ntdll!LdrpWalkImportDescriptor+0x19e
0012fa50 7c93cd1d 00020498 004396ca 00400000 ntdll!LdrpLoadImportModule+0x1c8
0012fa80 7c93ccc4 7ffd9000 00020498 00251ec0 ntdll!LdrpHandleOneOldFormatImportDescriptor+0x5e
0012fa98 7c93bc1e 7ffd9000 00020498 00251ec0 ntdll!LdrpHandleOldFormatImportDescriptors+0x1f
0012fb14 7c9418b5 00020498 00251ec0 7ffdf000 ntdll!LdrpWalkImportDescriptor+0x19e
0012fc94 00000000 0012fca0 00000000 0012fd1c ntdll!LdrpInitializeProcess+0xe02
0:000> dt ntdll!_LDR_DATA_TABLE_ENTRY 00253010
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x251e9c - 0x252ee0 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x251ea4 - 0x252ee8 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x018 DllBase : 0x003a0000 Void
+0x01c EntryPoint : 0x003c1ae4 Void
+0x020 SizeOfImage : 0x43000
+0x024 FullDllName : _UNICODE_STRING "C:\Program Files\S-dir\Some-dir\someapp.dll"
+0x02c BaseDllName : _UNICODE_STRING "someapp.dll"
+0x034 Flags : 0x200006
+0x038 LoadCount : 0
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x7c99e2f0 - 0x252a5c ]
+0x03c SectionPointer : 0x7c99e2f0 Void
+0x040 CheckSum : 0x252a5c
+0x044 TimeDateStamp : 0x5618c3dc
+0x044 LoadedImports : 0x5618c3dc Void
+0x048 EntryPointActivationContext : 0x00153960 Void
+0x04c PatchInformation : (null)

可以看到正在加载someapp.dll,并且处理导入表的时候出了错。来看看这个模块的导入表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
0:000> !dh someapp -f
File Type: DLL
FILE HEADER VALUES
14C machine (i386)
6 number of sections
5618C3DC time date stamp Sat Oct 10 15:53:00 2015
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2102 characteristics
Executable
32 bit word machine
DLL
OPTIONAL HEADER VALUES
10B magic #
10.00 linker version
27A00 size of code
15C00 size of initialized data
0 size of uninitialized data
21AE4 address of entry point
1000 base of code
----- new -----
10000000 image base
1000 section alignment
200 file alignment
2 subsystem (Windows GUI)
5.01 operating system version
0.00 image version
5.01 subsystem version
43000 size of image
400 size of headers
4943E checksum
00100000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
140 DLL characteristics
Dynamic base
NX compatible
34BE0 [ 1E0] address [size] of Export Directory
33810 [ B4] address [size] of Import Directory
3A000 [ 4CC] address [size] of Resource Directory
0 [ 0] address [size] of Exception Directory
0 [ 0] address [size] of Security Directory
3B000 [ 3954] address [size] of Base Relocation Directory
29340 [ 1C] address [size] of Debug Directory
0 [ 0] address [size] of Description Directory
0 [ 0] address [size] of Special Directory
2DD80 [ 18] address [size] of Thread Storage Directory
2DD38 [ 40] address [size] of Load Configuration Directory
0 [ 0] address [size] of Bound Import Directory
29000 [ 2CC] address [size] of Import Address Table Directory
0 [ 0] address [size] of Delay Import Directory
0 [ 0] address [size] of COR20 Header Directory
0 [ 0] address [size] of Reserved Directory
0:000> dc someapp+33810 someapp+33810+B4
003d3810 60325c32 68326432 90326c32 b332af32 2\2`2d2h2l2.2.2.
003d3820 d132ca32 df32db32 0032fc32 30332033 2.2.2.2.2.2.3 30
003d3830 40333833 4c334833 54335033 5c335833 [email protected]\
003d3840 64336033 6c336833 74337033 7c337833 3`3d3h3l3p3t3x3|
003d3850 84338033 8c338833 94339033 ce33b433 3.3.3.3.3.3.3.3.
003d3860 e933d233 f733f333 b134a633 0434e134 3.3.3.3.3.4.4.4.
003d3870 44353935 94357135 d435c935 57361a35 595D5q5.5.5.5.6W
003d3880 be366736 0c36d036 4b374037 b3377b37 [email protected]{7.
003d3890 ea37cf37 45380e37 81385e38 a0388a38 7.7.7.8E8^8.8.8.
003d38a0 fb38d338 4b392438 a4399939 0039dd39 8.8.8$9K9.9.9.9.
003d38b0 403a353a c33a863a 2a3ad33a 6b3b3c3b :5:@:.:.:.:*;<;k
003d38c0 cf3b933b 1d3bea3b ;.;.;.;.

所以这样就清楚了,someapp.dll的输入表被破坏了,导致加载他的程序无法运行起来。

debugging

Windbg插件0cchext

0cchext.dll是我一直在开发和维护的一个Windbg扩展程序。扩展程序中包含了一些或者有趣,或者实用,或者纯个人偏好的功能。这篇文章就来介绍一些主要的功能:


!a

!a - Assembles instruction mnemonics and puts the resulting
instruction codes into memory.

这个指令是写入汇编代码的扩展,虽然Windbg有自己的汇编命令a,但是这个命令无法配合脚本使用。你一旦输入命令a,Windbg就会进入汇编模式,此时你就无法让脚本继续进行了。所以我开发了!a,这个命令只会对一条命令进行汇编,并且将下一条汇编的地址存储在@#LastAsmAddr中,然后马上执行下面的命令,对脚本而已再好不过了。
例如下面这个脚本,他可以注入dll到debuggee

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ad /q ${/v:alloc_addr}
ad /q ${/v:@#LastAsmAddr}
x kernel32!LoadlibraryA
.foreach /pS 5 (alloc_addr {.dvalloc 0x200}) {r $.u0 = alloc_addr}
.block {aS ${/v:@#LastAsmAddr} 0; !a $u0 pushfd};
.block {!a ${@#LastAsmAddr} pushad}
.block {!a ${@#LastAsmAddr} push 0x$u0+0x100}
.block {!a ${@#LastAsmAddr} call kernel32!LoadLibraryA}
.block {!a ${@#LastAsmAddr} popad}
.block {!a ${@#LastAsmAddr} popfd}
.block { eza 0x$u0+0x100 "${$arg1}"}
r @eip=$u0
.block {g ${@#LastAsmAddr}}
.dvfree 0x$u0 0


!autocmd

!autocmd - Execute the debugger commands.(The config file is
autocmd.ini)

自动执行特定指令。有的时候我希望调试器附加到进程或者运行程序的时候能够自动运行一连串的命令,这个功能虽然可以由脚本完成,但是对我而言还是不够简洁,所以就有了这个命令。我可以在0cchext.dll的目录下,创建autocmd.ini文件,然后输入以下内容:

1
2
3
4
5
6
7
[notepad.exe]
.sympath+ c:\notepad_pdb
~*k
[calc.exe]
.sympath+ c:\calc_pdb
~*k

这样,在调试不同程序的时候输入!autocmd会执行不同的命令。


!bing & !google

!bing - Use bing to search.
!google - Use google to search.

这个命令非常简单,就是用bing和google去搜索指定的字符串。


!favcmd

!favcmd - Display the favorite debugger commands.(The config file is
favcmd.ini)

这个命令也非常简单,只需要把自己喜欢的命令一行一行的写在favcmd.ini文件里就行了,当然这个文件也需要和0cchext.dll在同一个目录。然后运行这个命令后,你所喜欢的命令就会打印到Windbg上,你可以用鼠标选择执行这些命令。

例如在文件中分别写入:

~*k
!address
!heap

20151005162754


!hwnd

!hwnd - Show window information by handle.

这个命令很简单,可以输入窗口句柄为参数,查看窗口相关信息。主要作用是在内核调试的时候,用调试器看到窗口信息会比较方便。


!url

!url - Open a URL in a default browser.

这个命令会打开一个url,实际上他就是一个ShellExecute。Windbg本来就有.shell功能了,这个似乎是多余了一点。


!init_script_env

!init_script_env - Initialize script environment.

这个命令是我给脚本准备的,他方便了脚本判断系统环境。如下图所示

20151005163744


!import_vs_bps

!import_vs_bps - Import visual studio breakpoints.

这个命令可以将VS存储在suo文件的断点导入到Windbg中。我有的时候会碰到这样的情况,VS里设置了一堆断点,但是调试环境里只有Windbg,那么我需要把这些断点转移到Windbg,有了这个命令,我只需要将VS解决方案的suo文件拷贝到调试环境中,然后运行这条命令即可。

例如

!import_vs_bps c:\proj\xxx.suo


!setvprot

!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 & !pe_import

!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

!wql - Query system information with WMI.

这也是我比较喜欢的一个功能,他可以在调试的时候通过WQL来查询系统的一些信息,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0:000> !0cchext.wql select * from win32_process where name="explorer.exe"
-------------------------------------------------------------
Caption local CIM_STRING explorer.exe
CommandLine local CIM_STRING C:\Windows\Explorer.EXE
CreationClassName local CIM_STRING Win32_Process
CreationDate local CIM_DATETIME 2015-09-17 09:41:53.959
CSCreationClassName local CIM_STRING Win32_ComputerSystem
...
...
ThreadCount local CIM_UINT32 40
UserModeTime local CIM_UINT64 605439881
VirtualSize local CIM_UINT64 435580928
WindowsVersion local CIM_STRING 6.1.7601
WorkingSetSize local CIM_UINT64 109813760
WriteOperationCount local CIM_UINT64 399
WriteTransferCount local CIM_UINT64 1545945
-------------------------------------------------------------

!logcmd

!logcmd - Log command line to log file

这个命令是一个开关,打开后,他会记录调试的命令到文件中,这样下次调试相同的程序的时候就不需要在此去输入这些命令了,只需要读取这个命令文件,就可以用鼠标点击执行命令了。

20151005170422


!dpx

!dpx - Display the contents of memory in the given range.

这个命令是集dps dpa dpu大成者。他的会对目标指针做一个简单的判断,判断是符号,字符串,还是宽字符串。这样在我们查看栈信息的时候就不会漏掉一些有用的线索了。

1
2
3
4
5
6
7
8
9
10
11
0:000> !dpx esp 100
00c3f28c 7605cb33 [S] USER32!GetMessageA+0x53 (7605cb33)
...
00c3f2b4 012b6ca9 [S] usbview!WinMain+0xe3 (012b6ca9)
...
00c3f2f4 012ce723 [S] usbview!WinMainCRTStartup+0x151 (012ce723)
00c3f2f8 01260000 [S] usbview!__guard_check_icall_fptr <PERF> (usbview+0x0)
...
00c3f320 01025618 [A] "Winsta0\Default"
00c3f324 01025640 [A] "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\usbview.exe"
00c3f328 00000000 [D] ....

!dtx

!dtx - Displays information about structures. (The config file is
struct.ini)

这个命令主要用在逆向工程的时候。因为逆向工程的时候,我们往往没有符号文件,就不可能直接知道内存数据的结构是什么样子的,我们需要自己通过代码推断出来。在IDA中,我们可以自己设置结构体帮助分析。但是在Windbg中,并没有一个功能能方便的帮助我们用这推断的结构体去显示内存。不可否认我们其实可以用其他的办法来完成这个目的,但操作很繁琐。那么这个命令就解决了这些问题。我们可以在struct.ini文件中写入我们推断的结构体,然后通过这个命令去打印内存数据。当然,这个文件也必须在0cchext.dll的同目录下。

20151005172455

到目前位置脚本解析器支持的基本类型有BYTE WORD DWORD CHAR WCHAR,支持数组和指针,支持结构体嵌套,有了这些,对于基本的逆向就能够满足需求了。


现在0cchext.dll就是这些命令了,我也会根据自己的需求继续添加命令,如果你有什么有趣或者实用的想法,可以通过邮件或者留言告诉我。

debugging

Foxmail无法响应

Foxmail是一款不错的邮件客户端软件,小巧实用,我在公司就是用的它。早上一如既往的先打开Foxmail,然后去倒杯水,回来发现Foxmail还在收取邮件的状态,“这也太慢了”,我心想。用鼠标点了点,出现了程序挂起的特征,标题栏上显示无法响应,程序界面变白。经验告诉我挂起的问题70%都还是比较容易调的,好吧,就让我看看这是怎么回事。

打开Windbg,Attach到Foxmail上,习惯做的第一件事情就是保存Full dump

1
.dump /ma /u e:\foxmail.dmp

由于程序的主界面出现了挂起的现象,而一般情况下主线程就是程序的界面线程,所以此时根本没必要去查看所有线程的情况,直接看看主线程的栈信息吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
0:000> k
ChildEBP RetAddr
0018f454 770fd993 ntdll!ZwWaitForSingleObject+0x15
0018f4b8 770fd877 ntdll!RtlpWaitOnCriticalSection+0x13e
0018f4e0 770f84ca ntdll!RtlEnterCriticalSection+0x150
0018f514 76734e8c ntdll!RtlLockHeap+0x3d
0018f558 004091af KERNELBASE!GlobalAlloc+0x92
WARNING: Stack unwind information not available. Following frames may be wrong.
0018f578 0042bf97 Foxmail+0x91af
0018f588 0042c115 Foxmail+0x2bf97
0018f5a4 007c5f25 Foxmail+0x2c115
0018f9fc 007c50ce Foxmail+0x3c5f25
0018fa34 007c516a Foxmail+0x3c50ce
0018fa44 007c9e57 Foxmail+0x3c516a
0018faac 007ca28b Foxmail+0x3c9e57
0018fae0 007c9370 Foxmail+0x3ca28b
0018fb10 007ca3fb Foxmail+0x3c9370
0018fb1c 00dd8a96 Foxmail+0x3ca3fb
0018fb44 007c93bb Foxmail+0x9d8a96
0018fb64 007c9479 Foxmail+0x3c93bb
0018fba8 00dd0492 Foxmail+0x3c9479
0018fcd4 00dd019d Foxmail+0x9d0492
0018fd00 00dd454a Foxmail+0x9d019d
0018fd44 00dcf20d Foxmail+0x9d454a
0018fd60 007ca59d Foxmail+0x9cf20d
0018fd84 0044e9d6 Foxmail+0x3ca59d
0018fd94 0044e844 Foxmail+0x4e9d6
0018fdbc 00434a96 Foxmail+0x4e844
0018fdd4 74aa62fa Foxmail+0x34a96
0018fe00 74aa6d3a user32!InternalCallWinProc+0x23
0018fe78 74aa77c4 user32!UserCallWinProcCheckWow+0x109
0018fed8 74aa7bca user32!DispatchMessageWorker+0x3bc
0018fee8 00dc5e7a user32!DispatchMessageA+0xf
0018ff04 00dc5ee4 Foxmail+0x9c5e7a
0018ff2c 00dc61bf Foxmail+0x9c5ee4
0018ff5c 00ded3a0 Foxmail+0x9c61bf
0018ff88 75b9336a Foxmail+0x9ed3a0
0018ff94 770f9882 kernel32!BaseThreadInitThunk+0xe
0018ffd4 770f9855 ntdll!__RtlUserThreadStart+0x70
0018ffec 00000000 ntdll!_RtlUserThreadStart+0x1b

可以看到界面线程调用GlobalAlloc的时候在等HeapLock被释放。

1
2
3
4
5
6
7
0:000> kb L5
ChildEBP RetAddr Args to Child
0018f454 770fd993 00000698 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15
0018f4b8 770fd877 00000000 00000000 00000000 ntdll!RtlpWaitOnCriticalSection+0x13e
0018f4e0 770f84ca 014d0138 0044e9b8 0018f4ac ntdll!RtlEnterCriticalSection+0x150
0018f514 76734e8c 014d0000 4d5402d1 00000000 ntdll!RtlLockHeap+0x3d
0018f558 004091af 00000002 00002000 0042c07c KERNELBASE!GlobalAlloc+0x92

那么014d0138比如是默认堆的Critical Section了。来看看这个cs的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
0:000> !cs 014d0138
*************************************************************************
*** ***
*** ***
*** Either you specified an unqualified symbol, or your debugger ***
*** doesn't have full symbol information. Unqualified symbol ***
*** resolution is turned off by default. Please either specify a ***
*** fully qualified symbol module!symbolname, or enable resolution ***
*** of unqualified symbols by typing ".symopt- 100". Note that ***
*** enabling unqualified symbol resolution with network symbol ***
*** server shares in the symbol path may cause the debugger to ***
*** appear to hang for long periods of time when an incorrect ***
*** symbol name is typed or the network symbol server is down. ***
*** ***
*** For some commands to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: ntdll!_RTL_CRITICAL_SECTION ***
*** ***
*************************************************************************
Bad symbols for NTDLL (error 3). Aborting.

嗯,ntdll的符号文件的结构体信息没有了!这个问题发生在2015年7月份的,安装KB3071756和KB3060716补丁后产生的。详情可以查看
http://www.osronline.com/showthread.cfm?link=269221

既然新的符号不让用,那就只有用老的了

1
2
3
4
5
6
7
8
9
10
11
12
13
0:000> .reload /f /i E:\WorkSpace\MySymbols\ntdll.dll\49900AFA96000\ntdll.dll=77ffe000
0:000> lm m ntdll*
start end module name
770c0000 77240000 ntdll (pdb symbols) e:\workspace\mysymbols\wntdll.pdb\FA9C48F9C11D4E0894B8970DECD92C972\wntdll.pdb
77ffe000 78094000 ntdll_77ffe000 (pdb symbols) e:\workspace\mysymbols\ntdll.pdb\6992F4DAF4B144068D78669D6CB5D2072\ntdll.pdb
0:000> dt ntdll_77ffe000!_RTL_CRITICAL_SECTION 014d0138
+0x000 DebugInfo : 0x771c4960 _RTL_CRITICAL_SECTION_DEBUG
+0x004 LockCount : 0n-30
+0x008 RecursionCount : 0n1
+0x00c OwningThread : 0x00001730 Void
+0x010 LockSemaphore : 0x00000698 Void
+0x014 SpinCount : 0xfa0

好了,这样就够用了。可以看到TID=1730的线程正在占用这个cs,马上去看看这个线程在干什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0:041> k
ChildEBP RetAddr
06e1f214 770fd993 ntdll!ZwWaitForSingleObject+0x15
06e1f278 770fd877 ntdll!RtlpWaitOnCriticalSection+0x13e
06e1f2a0 770f84ca ntdll!RtlEnterCriticalSection+0x150
06e1f2d4 7717fd7d ntdll!RtlLockHeap+0x3d
06e1f3bc 7714de8e ntdll!RtlpQueryExtendedHeapInformation+0xbd
06e1f3fc 7716476b ntdll!RtlQueryHeapInformation+0x4a
06e1f4a0 77143320 ntdll!RtlQueryProcessHeapInformation+0x288
06e1f51c 75c15f4b ntdll!RtlQueryProcessDebugInformation+0x28a
Unable to load image D:\Program Files\Foxmail 7.2\libeay32.dll, Win32 error 0n2
*** ERROR: Symbol file could not be found. Defaulted to export symbols for libeay32.dll -
06e1f54c 0606ad6a kernel32!Heap32Next+0x4d
WARNING: Stack unwind information not available. Following frames may be wrong.
06e1fab0 06069c77 libeay32!RAND_poll+0x5fa
06e1facc 06023db7 libeay32!RAND_SSLeay+0x447
06e1fb00 06069f71 libeay32!CRYPTO_set_ex_data_implementation+0x387
00000000 00000000 libeay32!RAND_SSLeay+0x741

从栈的信息看来,这个线程是OpenSSL的一个线程,正在做随机数处理,而枚举Heap的信息应该也是随机数的一个组成部分。在枚举Heap的时候也处于等待一个HeapLock的情况,来具体看看

1
2
3
4
5
6
7
0:041> kb L5
ChildEBP RetAddr Args to Child
06e1f214 770fd993 00000b10 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15
06e1f278 770fd877 00000000 00000000 06e1f428 ntdll!RtlpWaitOnCriticalSection+0x13e
06e1f2a0 770f84ca 04560138 00000000 00000000 ntdll!RtlEnterCriticalSection+0x150
06e1f2d4 7717fd7d 04560000 71edefe5 06e1f468 ntdll!RtlLockHeap+0x3d
06e1f3bc 7714de8e 06e1f428 771640d3 00000000 ntdll!RtlpQueryExtendedHeapInformation+0xbd

看来正在等一个基地址是04560000的Heap的cs。顺藤摸瓜看看这个cs又被谁占用了。

1
2
3
4
5
6
7
0:041> dt ntdll_77ffe000!_RTL_CRITICAL_SECTION 04560138
+0x000 DebugInfo : 0x0151df40 _RTL_CRITICAL_SECTION_DEBUG
+0x004 LockCount : 0n-6
+0x008 RecursionCount : 0n1
+0x00c OwningThread : 0x00001994 Void
+0x010 LockSemaphore : 0x00000b10 Void
+0x014 SpinCount : 0xfa0

继续看看0x00001994这个线程在做什么事情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
0:002> kb
ChildEBP RetAddr Args to Child
0438d2c4 770fd993 00000698 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15
0438d328 770fd877 00000000 00000000 014d0000 ntdll!RtlpWaitOnCriticalSection+0x13e
0438d350 770fdaf8 014d0138 7334c875 00078000 ntdll!RtlEnterCriticalSection+0x150
0438d42c 770f2fe3 000001f8 00000200 00000000 ntdll!RtlpAllocateHeap+0x159
0438d4b0 770f7bfb 014d0000 00800000 000001f8 ntdll!RtlAllocateHeap+0x23a
0438d4fc 770f70f1 00000028 7334c9d5 07240048 ntdll!RtlpAllocateUserBlock+0xae
0438d58c 770ee0e2 07240048 07240048 00000000 ntdll!RtlpLowFragHeapAllocFromContext+0x802
0438d600 770f8129 014d0000 00000000 00000020 ntdll!RtlAllocateHeap+0x206
0438d610 770f3f21 04560000 07240048 00000000 ntdll!RtlpAllocateDebugInfo+0x28
0438d64c 770f4262 07240048 00000000 00000000 ntdll!RtlInitializeCriticalSectionEx+0x93
0438d660 771031e2 07240048 07240048 04560000 ntdll!RtlInitializeCriticalSection+0x12
0438d674 771031b0 00000000 00000800 04560000 ntdll!RtlpInitializeLowFragHeap+0x28
0438d684 77102f5a 7334cae5 045600cc 04560000 ntdll!RtlpCreateLowFragHeap+0x28
0438d6bc 77103002 04560000 04560194 0438d7a8 ntdll!RtlpActivateLowFragmentationHeap+0xc9
0438d6cc 77102fce 04560000 7334cbf1 00000000 ntdll!RtlpPerformHeapMaintenance+0x2a
0438d7a8 770f2fe3 00000008 00000010 04560194 ntdll!RtlpAllocateHeap+0x172
0438d82c 6fef1e62 04560000 00000008 00000008 ntdll!RtlAllocateHeap+0x23a
0438d844 6fef20f9 0438d858 04560984 00000007 nlaapi!NlapAllocNlaDataSet+0x18
0438d85c 6fef2096 00000007 00000003 045608f0 nlaapi!QueryCtxtDeserializeTuplesToTrieMap+0x20
0438d888 6fef23a7 045608f0 000003c8 04560958 nlaapi!QueryCtxtDeserializeEntityToTrieMap+0x38
0438d924 6fef58e0 7972514e 00000001 00000000 nlaapi!NLA_QUERY_HANDLE_completion_internal+0x21f
0438d944 6fef5cd7 00000000 00000000 0c7507d0 nlaapi!NlaRegisterQuery+0x7d
0438d99c 6fef5bac 044907d0 4e3f415a 014587b0 nlaapi!GetNlaV2Handle+0x7e
0438d9d0 769d59b1 01435244 0438db88 044907d0 nlaapi!WSM_NSPLookupServiceBegin_v2+0xc9
0438d9ec 769d598d 01435208 0438db88 00000000 ws2_32!NSPROVIDER::NSPLookupServiceBegin+0x1b
0438da08 769d591c 01458960 0438db88 00000000 ws2_32!NSPROVIDERSTATE::LookupServiceBegin+0x1d
0438da6c 769d57cc 014587b0 0438db88 00000ff0 ws2_32!NSQUERY::LookupServiceBegin+0x18d
0438dabc 0fd7fca2 0438db88 00000ff0 0438dbd0 ws2_32!WSALookupServiceBeginW+0x7f
WARNING: Stack unwind information not available. Following frames may be wrong.
0438dd18 0fd0c6a1 0fa9df49 03ac9c20 03b5da80 libcef!cef_time_delta+0x3ddc22
0438e2f0 0fa9ec71 0438e698 03a91e00 0438e310 libcef!cef_time_delta+0x36a621
0438e37c 0fa8d58f 0438e698 03ac9c00 03b5da80 libcef!cef_time_delta+0xfcbf1
0438eb88 0fa9915f 0438ebc8 fffffffe ffffffff libcef!cef_time_delta+0xeb50f
0438ebe8 0fb3605d 03b5da80 fffffffe 03ac9d20 libcef!cef_time_delta+0xf70df
0438ec04 0fb3744d 03b5da80 fffffffe fffffffe libcef!cef_time_delta+0x193fdd
0438ecd8 0fb37ad2 00000003 fffffffe 00000000 libcef!cef_time_delta+0x1953cd
0438edc0 0fb37f5d 03ac9400 11c9bee1 03ac9400 libcef!cef_time_delta+0x195a52
0438edfc 0fa9ab19 03ac9400 03ac9c38 03ac9c00 libcef!cef_time_delta+0x195edd
0438f20c 0fa9b466 03ac9400 00000000 03ac9c38 libcef!cef_time_delta+0xf8a99
0438f220 0fb3c815 00000000 03ac9400 0fb3aee5 libcef!cef_time_delta+0xf93e6
0438f2e8 0fb3d2e3 00000000 03b53384 03ac9c38 libcef!cef_time_delta+0x19a795
0438f690 0fb3ad7b 0438f600 03b53384 03b2ddc0 libcef!cef_time_delta+0x19b263
0438f8ac 0f985613 0438fb64 03b53384 00000001 libcef!cef_time_delta+0x198cfb
0438fbec 0f98016a ffffffff ffffffff 03b53368 libcef!cef_string_multimap_free+0x18b63
0438fc28 0fa208a4 03b53340 03ac0dd0 03a87b00 libcef!cef_string_multimap_free+0x136ba
0438fd70 0fa223ba 0438fdd0 03a878c0 03ac0d00 libcef!cef_time_delta+0x7e824
0438fe0c 0fa4edfc 00000000 03a878c0 03ac0dd0 libcef!cef_time_delta+0x8033a
0438fe3c 0fa4ec2e 00000000 03ac0dd0 03ac0dd0 libcef!cef_time_delta+0xacd7c
0438fe5c 0fa2177f 03ac0dd0 0438ff34 03ac0dd0 libcef!cef_time_delta+0xacbae
0438ff24 0fa3a323 03a8d7d0 0fa1fc56 03ac0dd0 libcef!cef_time_delta+0x7f6ff
0438ff50 0fa39b9b 0438ff74 0fa39c6a 03ac0dd0 libcef!cef_time_delta+0x982a3
0438ff58 0fa39c6a 03ac0dd0 03a80f40 03a8d7d0 libcef!cef_time_delta+0x97b1b
0438ff74 0fa22ad8 00000000 00000000 03a80f40 libcef!cef_time_delta+0x97bea
0438ff88 75b9336a 000001a8 0438ffd4 770f9882 libcef!cef_time_delta+0x80a58
0438ff94 770f9882 03a80f40 7334e38d 00000000 kernel32!BaseThreadInitThunk+0xe
0438ffd4 770f9855 0fa22a80 03a80f40 00000000 ntdll!__RtlUserThreadStart+0x70
0438ffec 00000000 0fa22a80 03a80f40 00000000 ntdll!_RtlUserThreadStart+0x1b

这个线程正在为04560000这个heap创建LowFragHeap,但是在获取014d0138的cs的时候被卡住了。这里就发现问题所在了!014d0138不正是我们主线程在等待的cs么,这个线程的10f0和1994都在等待1730的cs=014d0138,而1730却在等待1994的cs=04560138。

20150908002438

反汇编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
但是,我觉得既然是文档化的接口,而且文档里面没有提到会造成死锁,那么它就应该是安全的。

debugging

介绍三个有趣的API

####PickIconDlg

相信给快捷方式指定过图标的朋友肯定看过一个这样的对话框吧,如果你看到过,你肯定已经知道了这个API是怎么一回事。这个API会弹出一个选择图标的窗口给你选择,确定后返回图标在资源中的索引值。这样你可以通过这个索引值和ExtractIcon函数获得这个图标的句柄。

20150827113011

示例代码如下:

1
2
3
4
5
6
7
8
9
10
int Index = 2;
const DWORD BuffSize = MAX_PATH;
TCHAR Path[BuffSize] = _T("c:\\windows\\system32\\shell32.dll");
const int Sel = PickIconDlg(NULL, Path, BuffSize, &Index);
if(Sel)
{
HMODULE hMod = ::LoadLibrary(Path);
HICON hIcon = ExtractIcon(hMod, Path, Index);
FreeLibrary(hMod);
}

####WNetConnectionDialog和WNetConnectionDialog1

这两个函数是帮助我们在程序中显示映射网络驱动器对话框的,虽然用的不多,但是也应该见到过它。这两个函数区别不大,只不过WNetConnectionDialog1比WNetConnectionDialog提供了更多的参数去设置。

20150827114516

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma comment(lib, "Mpr.lib")
CONNECTDLGSTRUCT condlg = { 0 };
condlg.cbStructure = sizeof(condlg);
condlg.hwndOwner = GetConsoleWindow();
condlg.dwFlags = CONNDLG_USE_MRU;
NETRESOURCE nr = { 0 };
nr.dwScope = RESOURCE_GLOBALNET;
nr.dwType = RESOURCETYPE_DISK;
nr.lpRemoteName = NULL;
nr.dwDisplayType = RESOURCEDISPLAYTYPE_DOMAIN;
condlg.lpConnRes = &nr;
const int RetVal = WNetConnectionDialog1(&condlg);

####SHOpenWithDialog

这个API所显示的对话框我们应该是最多见的,它显示了一个打开方式的对话框。不过有点可惜的是,XP并不支持这个API,我们只能将它用在Vista开始的系统上。

20150827115225

示例代码如下:

1
2
3
4
OPENASINFO Info = { 0 };
Info.pcszFile = _T("C:\\Windows\\win.ini");
Info.oaifInFlags = OAIF_EXEC | OAIF_ALLOW_REGISTRATION;
SHOpenWithDialog(NULL, &Info);

Tips

OleFileView 查看Ole文件数据结构的工具

OleFileView是一个查看结构化存储文件的工具,我们熟悉的Ole存储的文件就是这种格式,虽然很老了,但是依旧被广泛使用,例如老版本Office的文件doc,xls等,包括msi,jumplist等,都是采用的这种格式。
所以我也就抽了点时间研究了一下这个数据结构,可以说这就是个小型的文件系统,虽然比不上NTFS,但是对于一般的存储可以说是小菜一碟。

20150803003316

下载:OleFileView.zip

Tips

使用WMI监控进程创建和结束

Windows Management Instrumentation (WMI) 是微软实现的一套可以通过网页管理计算机的系统,我们可以通过WMI查询计算机的方方面面。从Vista开始,这个机制增加了Instance Event的提醒机制,这个机制可以帮助我们监控各种Instance的创建、删除和修改。所以,我们可以想到的是进程也是在WMI里的Win32_Process有Instance的记录,这样我们就可以跟踪到进程的创建和结束了。当然,我们还可能监控到文件等等WMI里的各种Instance。下面是一个监控进程的例子:

20150712232158

下载:MonitorProcessWithWMI.zip

Tips

用Windbg script将内存中的PE文件dump出来

最近看到有些恶意程序,从网络上下载PE文件后,直接放在内存里重定位和初始化,为了能将其dump出来,所以写了这个Windbg脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.foreach( place { !address /f:VAR,MEM_PRIVATE,MEM_COMMIT /c:"s -[1]a %1 %2 \"MZ\"" } )
{
ad *
.catch {
r @$t2 = place;
r @$t0 = place;
r @$t1 = @@C++(((ntdll!_IMAGE_DOS_HEADER *)@$t0)->e_lfanew);
r @$t0 = @$t0 + @$t1;
r @$t1 = $vvalid(@$t0, 4);
.if (@@C++(@$t1 && @@C++(((ntdll!_IMAGE_NT_HEADERS *)@$t0)->Signature) == 0x00004550))
{
r @$t1 = @@C++(((ntdll!_IMAGE_NT_HEADERS *)@$t0)->OptionalHeader.SizeOfImage);
.printf "%08x %08x\n", @$t2, @$t1;
aS /x start_addr @$t2
aS /x dump_size @$t1
.block {
aS target_file e:\\${start_addr}.dll
}
.block {
.printf "${target_file}"
.writemem "${target_file}" ${start_addr} L?${dump_size}
}
}
}
}

Tips

将blog迁移到了jekyll

上周终于下定决心把blog从wordpress转到jekyll,不是因为wordpress臃肿,也不是因为jekyll的更加Geek,纯粹是因为穷。我一直都觉得wordpress是一个非常伟大的blog程序,虽然臃肿了点,但确实功能强大操作简单,对于我这种懒人和对前端代码完全不熟的程序员来说,wordpress确实是一个非常好的选择。但是问题就出在了webhost上,我使用的webhost刚刚买的时候是50多刀一年,之后每年涨价,今年续费看了下需要100刀左右,这个确实让我心中无数的羊驼奔腾了起来。于是就决定把blog搬离这个地方。

刚开始我只是想找便宜的地方转移wordpress的blog。网上也有很多这类的webhost,第一年进去都很便宜,甚至有1刀一个月的。但是一朝被蛇咬啊,为了防止以后又被迫搬家,于是打消了这个念头。想要便宜和稳定的blog空间,看来是只有伟大的Github。而Github只支持静态程序,那么我也只能放弃wordpress的方便,自己折腾点静态博客程序了。摆在眼前的选择其实很多最基础jekyll,加强版的octopress以及hexo。第一个程序的优点就是简单基础,缺点就是太基础了,而octopress在jekyll的基础之上加上了一些插件,让blog默认的功能变得丰富起来。之后hexo,也是一个自带很多基础功能的程序而且还带了很多非常漂亮的主题,主题控的bloger不妨选择这个,我就特别喜欢他其中的一个默认主题,但是折腾样式的时候jekyll的基本结构都搭建好了,所以就没有更换hexo程序,于是极度痛苦的折腾了一周的css和ruby插件才把现在的blog折腾的和之前的差不多。

简单说下用jekyll在Github上搭建blog的步骤,其实网上很多很多教程,这里记录下就是防止自己忘了把。

  1. 首先在http://rubyinstaller.org/downloads/下载ruby和DevKit,安装分别安装他们,然后运行Devkit,分别执行:
    1) dk.rb init
    2) dk.rb review
    3) dk.rb install
  2. 接下来就是安装jekyll了,安装之前推荐更换Gem的源到https://ruby.taobao.org/ 这样下载程序比较快。具体方式是:
    1) gem source -r (url)
    2) gem source -a (new url)
    3) gem source -u
  3. 然后就可以开始下载jekyll和他的代码高亮程序rouge了,gem install (app name)
  4. 最后记得要设置_config.yml文件,尤其是高亮highlighter: rouge

这样,最基础功能的blog就搭建好了,接下来就是把blog从wordpress转移到jekyll了。方法是使用exitwp这个python脚本。

  1. 先导出wordpress的数据到一个xml里,这个功能wordpress是自带的。
  2. 然后同个这个脚本把数据转换成markdown文件,放在jekyll生产的_post里面。并且把里面的图片和下载的url替换了。
  3. 最后把wordpress的upload目录下载下来,放到jekyll里面即可。

这样我们看到的就是一个最简单的jekyll的blog,要想改变主题,自己去折腾吧。我能做的就是推荐两个jekyll的插件,分别是按日期和分类生成归档网页的,可以在我的Github上看到。

最后要说的是rouge语法高亮有个bug,在使用显示行号linenos参数的时候会出现嵌套错误的问题,解决方法倒是有,不过有了行号之后高亮的显示极其丑陋,所以还是我还是没用这个参数。如果有需求可以使用代码rouge_linenos_patch.rb覆盖”\lib\ruby\gems\2.2.0\gems\jekyll-2.5.3\lib\jekyll\tags\highlight.rb”里对应的函数即可。

Tips

关于Zone.Identifier的一点记录

自从Windows XP SP2开始,微软对文件加入了Zone.Identifier的数据流,所以这个也不算什么新东西了,最近偶然有机会研究了下所以就记录了下来。
说起Zone.Identifier,我们最常见的应用就是在我们从Internet上下载了可执行文件后,运行的时候会弹出如下图的警告窗口:

20150615150628

弹出这个窗口就是因为Explorer在运行这个文件的时候先检查了Zone.Identifier的数据,发现了如下文本
[ZoneTransfer]
ZoneId=3

这个ZoneId=3,就是指明这个文件是由Internet上下载的。根据MSDN,这个id有以下几种:

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef enum tagURLZONE {
URLZONE_INVALID = -1,
URLZONE_PREDEFINED_MIN = 0,
URLZONE_LOCAL_MACHINE = 0,
URLZONE_INTRANET,
URLZONE_TRUSTED,
URLZONE_INTERNET,
URLZONE_UNTRUSTED,
URLZONE_PREDEFINED_MAX = 999,
URLZONE_USER_MIN = 1000,
URLZONE_USER_MAX = 10000
} URLZONE;

查看这个数据流的方法也很简单,用notepad就行了。

20150615153805

另外如果想给添加或者去除这个数据流,我们这里有两种方法:
1.直接读写数据流,其实这个跟普通文件读写没什么两样。
2.调用微软提供的com接口,这个比较是规范的。

对于第一种方法,没什么可说的,无非就是文件操作的那些API。第二种方法我们需要用到以下两个接口:
IPersistFile
IZoneIdentifier

我们先创建IZoneIdentifier接口,然后query出IPersistFile打开文件,最后读取或者写入文件。
代码详见:http://blogs.msdn.com/b/oldnewthing/archive/2013/11/04/10463035.aspx

最后说一下,之所以能有Zone.Identifier这种功能,完全依赖于NTFS文件系统,它允许多个数据流的存在,对它而言,每个数据流无非就是一个属性而已,只不过Zone.Identifier是一个名字为Zone.Identifier的数据流,而文件本身的数据是一个没有命名的数据流而已。用ntfs_study查看,如下图,第一个Data数据没有名字是文件本身的数据,第二个就是Zone.Identifier的数据了。
20150615160110

NTInternalsTips