hive_study —— 注册表文件格式解析工具

这几天又写了个小工具,刚下班就在这发了算了。

hive_study 是一个注册表文件格式解析工具。功能也比较的简单,主要是dump出注册表里面的数据信息。例如显示子项,子键,显示键值以及他们的安全描述符。当然了,还是那样,注册表的hive文件实际上是不可读的,因为内核独占他了。想要获得可以读的hive文件,可以用ntfsstudy把hive文件dump出来,然后去读这个新的hive文件。以下是他的Usage。

1
2
3
4
5
6
7
8
9
Usage : hive_study.exe -f hive_path [-options]
Options:
[-k key_path] Specifies key path (e.g.: -k "ControlSet001\Control\Session Manager")
[-v value_name] Specifies value name (e.g.: -v BootExecute)
[-l] List subkeys information
[-e] List values information
[-s] Show key security description information
[-w output_file] Dump attribute to a file

20130123181723

下载hive_study

NTInternals

分享自己打造的BugReporter

这份代码应该是将近一年前写的吧,应该是去年年初了。不像ntfsstudy和dia_study,这个工具应该比较的实用吧,实用场合可以比较多。所以也把源代码发出来。看工具名称基本上已经知道了是报告bug用的。其实这种工具倒是挺多的。不过我个人感觉自己写一个用起来顺手,而且代码量小,容易修改自定义。

工具代码有三个工程,分别是minidump_generator,dump_minidump,BugReporter。
minidump_generator是一个dll,当然也可以编译成静态库,他需要加入到你想使用BugReporter的工程里面。
dump_minidump是一个dump文件的解析库,他是BugReporter工程的依赖库。
BugReporter是一个窗口程序,在你融合了minidump_generator的程序崩溃的时候会弹出窗口。就像图中那样。

20130120193015

勾选send bug report并且点击ok,窗口里的完整log和dump文件会打包为zip文件储存,储存的地方可以由程序指定,也可以写注册表键值,reporter会去都键值然后存储到相应的位置。具体什么键值,看源代码吧。不完整和遗憾的地方也有,就是还没有http上传到提交dump收集服务器的代码。因为确实不会写php啥的。另外这个工程也用了不少开源代码,这里也感谢一下,其中包括反汇编引擎和ZIP代码。

下载源代码:BugReporter

以下是完整的dump log:

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
SYSTEM INFORMATIONS
---------------
ProcessorArchitecture : x86
Number Of Processors : 16
OS Version : 6.1
BuildNumber : 7600
CSDVersion :
Platform : Windows Server 2003, Windows XP, or Windows 2000
EXCEPTION INFORMATIONS
---------------
Thread id : 5744
ExceptionCode : c00000fd
ExceptionFlags : 00000000
ExceptionRecord : 0000000000000000
ExceptionAddress : 0000000000dea637
ExceptionInformation0 = 0000000000000000
ExceptionInformation1 = 0000000000102000
Thread Context :
EAX = 00102000 ECX = 00102050 EDX = 5eea2408 EBX = 7efde000
ESP = 00103120 EBP = 00103128 ESI = 001ff9c8 EDI = 00104210
EFLAGS = 00010206
MODULE INFORMATIONS
---------------
Base Size Name FileVersion ProductVersion
0000000000de0000 0001a000 D:\crashme\Debug\crashme.exe 0 0
00000000772b0000 00180000 C:\Windows\SysWOW64\ntdll.dll 600011db040af 600011db040af
0000000075ef0000 00100000 C:\Windows\SysWOW64\kernel32.dll 600011db04001 600011db04001
00000000750f0000 00046000 C:\Windows\SysWOW64\KERNELBASE.dll 600011db04001 600011db04001
000000005ed90000 00124000 C:\Windows\winsxs\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_2a4cbfc25558bcd3\msvcr90d.dll 9000078091034 9000078091034
0000000074330000 000eb000 C:\Windows\System32\dbghelp.dll 600011db04001 600011db04001
0000000076e00000 000ac000 C:\Windows\SysWOW64\msvcrt.dll 700001db04001 6000121be4001
0000000076d10000 000f0000 C:\Windows\SysWOW64\rpcrt4.dll 600011db04001 600011db04001
0000000074e20000 00060000 C:\Windows\SysWOW64\sspicli.dll 600011db04064 600011db04064
0000000074e10000 0000c000 C:\Windows\SysWOW64\CRYPTBASE.dll 600011db04001 600011db04001
00000000761e0000 00019000 C:\Windows\SysWOW64\sechost.dll 600011db04001 600011db04001
00000000763e0000 000a0000 C:\Windows\SysWOW64\advapi32.dll 600011db04001 600011db04001
000000006e810000 00025000 C:\Windows\System32\powrprof.dll 600011db04001 600011db04001
00000000768e0000 0019d000 C:\Windows\SysWOW64\setupapi.dll 600011db04001 600011db04001
0000000074f70000 00027000 C:\Windows\SysWOW64\cfgmgr32.dll 600011db04001 600011db04001
0000000076c80000 00090000 C:\Windows\SysWOW64\gdi32.dll 600011db04001 600011db04001
0000000076a80000 00100000 C:\Windows\SysWOW64\user32.dll 600011db04001 600011db04001
0000000077280000 0000a000 C:\Windows\SysWOW64\lpk.dll 600011db04001 600011db04001
0000000075140000 0009d000 C:\Windows\SysWOW64\usp10.dll 102721db04001 102721db04001
0000000076150000 0008f000 C:\Windows\SysWOW64\oleaut32.dll 600011db04001 600011db04001
0000000075ff0000 0015c000 C:\Windows\SysWOW64\ole32.dll 600011db04001 600011db04001
0000000074fa0000 00012000 C:\Windows\SysWOW64\devobj.dll 600011db04001 600011db04001
0000000074f10000 00060000 C:\Windows\System32\imm32.dll 600011db04001 600011db04001
0000000075020000 000cc000 C:\Windows\SysWOW64\msctf.dll 600011db04001 600011db04001
000000006fa00000 0003e000 C:\Program Files (x86)\Sophos\Sophos Anti-Virus\sophos_detoured.dll 900000000235f 9000000000000
00000000763d0000 00005000 C:\Windows\SysWOW64\psapi.dll 600011db04001 600011db04001
Crash IP Disasm :
test dword ptr [eax], eax

Debugging

Dia_study —— PDB查看工具

没事在家翻代码,发现大半年前的一份代码,写的是一个调用DIA SDK查看PDB文件的小工具。仔细想想我觉得还有点用处,而且使用方式简单,所以现在就发到blog上来吧。

简单介绍一下这个小工具。它可以dump出pdb的函数和数据结构的信息。下面两张图分别dump的是数据结构和函数的信息。

图一中,命令行为 dia_study.exe -p xxx.pdb -n processor -t 其实 -p是指pdb路径,-n是要获得的符号(支持通配符),-t说明要看的是数据结构而不是函数。然后所有带有processor的数据结构就会dump出来了。

20130120165712

图二中,命令行为 dia_study.exe -p xxx.pdb -n processor -f 唯一的区别就是-t变成了-f。指明要dump的是函数而非数据结构。

20130120165737

ok,使用方式就是如此简单。注意一点,请自备vs2010的c runtime 以及 msdia100.dll(需要注册),否则程序无法运行。

下载 dia_study

Debugging

NtfsStudy —— ntfs磁盘格式学习工具

经过将近一个月业余时间的开发,终于完成了NtfsStudy这个小工具的第一版。

简单介绍一下这个工具,NtfsStudy这个工具是我在学习Ntfs文件系统磁盘格式的时候,为了自己更加方便快捷的查看磁盘格式而开发的工具。可以说这个工具从开始写到现在发布,实际上也是一个学习ntfs的过程。我一边研究理解这个格式,一边把理解的东西写成代码,加入这个工具,然后再用这些功能去理解新的内容。反复这样做,这个工具就也不知不觉成型了。

NtfsStudy这个工具的主要功能是:枚举目录文件,查看和dump文件属性。这些功能都没用调用windows 文件操作类的API完成,而是依靠直接读取磁盘信息,并且解析磁盘信息来完成的。例如,如果尝试去复制注册表的系统hive文件,那是一定会被系统拒绝的,这个文件是系统读写独占的,但是通过这个工具就能绕过“ntfsstudy.exe -f c:\ -r e0a2 -w 3 d:\system.hiv”, 这个命令行的意思是把volume C上的引用数为0xe0a2文件中的3号属性的内容写到d:\system.hiv文件中。其实id为3的属性正好就是data属性,也就是文件本身的内容。这样就可以dump不能读的注册表hive文件了。下面是“ntfsstudy.exe -f c:\ -r e0a2 -d 8”的结果:

ntfs_hive

更多的详细用法和例子等我有空会在blog里面写一些。

下面就是他的Usage,也是目前该工具具有的功能。

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
NtfsStudy v1.0 - Ntfs format study tool.
Copyright (C) 2012-2013 nightxie
0CCh - www.0cch.net
Usage : NtfsStudy.exe [options] -f file_path_name
-f file_path_name Specifies the target file path to parse.
options:
[-r file_reference]   Specifies the target file reference.
NtfsStudy will parse the REFERENCE rather than the path which
Specifies by -f. NtfsStudy will just use the path root.
[-a] Show the file record information of the target file.
[-l]   List the files in the directory.
[-w attribute_id output_file_path] Write target attribute to a file.
(The attribute size must less than 128mb)
[-v attribute_type] Show detail attribute information specified by attribute_type.
[-d attribute_type [start_offset range]] Show binary information specified by attribute_type.
[-s secure_id] Show the security descriptor specified by secure_id.
[-c] Show the attributes definition columns.
About attribute type:
$STANDARD_INFORMATION         = 1
$ATTRIBUTE_LIST               = 2
$FILE_NAME                    = 3
$OBJECT_ID                    = 4
$SECURITY_DESCRIPTOR          = 5
$VOLUME_NAME                  = 6
$VOLUME_INFORMATION           = 7
$DATA                         = 8
$INDEX_ROOT                   = 9
$INDEX_ALLOCATION             = 10
$BITMAP                       = 11
$REPARSE_POINT                = 12
$EA_INFORMATION               = 13
$EA                           = 14
$LOGGED_UTILITY_STREAM        = 16
About secure id:
To get the secure id of target file.
Use '-v 1' command, secure id will displayed in STANDARD_INFORMATION.

另外我还会继续完善这个工具。如果发现bug请与我联系。

下载NtfsStudy

NTInternals

设置线程名

给线程命名的作用主要还是为了调试方便。其他的好处也没有了,至少我没想出来。这里说一下MS_VC_EXCEPTION这个异常,调试器(vs,windbg)默认情况下应该会在收到这个异常的时候,他会自动处理这个异常,具体操作应该是记录下线程对应的名称,然后将异常设置为Handle状态。什么意思呢?就是说,即使下面这段代码中的RaiseException不在try-except中,在调试器attach的情况下也能顺畅执行,调试器不会因为异常把执行中断下了,而是默默设置了线程名之后继续后面的代码。而下面的代码之所以要放在try-except中,是因为希望没有调试器的情况下,也能顺利执行不被中断。另外一点,windbg可以设置让这个异常中断下来(命令 sxe vcpp),而vs貌似没有这样的方法,可能是我vs调试器用的比较少,没找到吧。对于托管代码,设置这个就更简单了,详见 http://msdn.microsoft.com/en-us/library/581hfskb(v=vs.100).aspx.aspx)

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
#include
const DWORD MS_VC_EXCEPTION=0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetThreadName( DWORD dwThreadID, char* threadName)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
{
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info; );
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
}

Tips

R3卸载任意消息钩子

这是工作中遇到的一个问题。一个程序每次起来后会去挂鼠标键盘低级钩子,这类钩子恶心的地方是如果你用调试工具attach上去,鼠标键盘就会急剧的延时,就跟挂起差不多了,根本无法使用键盘鼠标。MSDN上面指明,设置LowLevelHooksTimeout可以帮助解决这个问题。但是无奈的是,似乎没起什么作用,渣英语,不知道是不是我的理解有误。

其他比较好的解决方案也有。比较好的一个就是用远程调试的方式,这种方式可以在hook存在的情况下调试程序,甚至调试hook的函数。另一个办法就是attach之前,卸载低级钩子。对于调试和钩子无关地方的时候,第二个选择也还是不错的。所以,Xuetr的卸载消息钩子的功能派上了用场。每次调试此程序之前,都先卸载钩子。但是还是有问题,我们都有这样的经验,在进行调试的时候经常需要restart程序,并且重新开始调试。这样可就恶心了,每次都要卸载一次钩子。于是,我就写了一个程序,循环查询低级鼠标键盘钩子,发现后立刻卸载,这样调试这个程序就会比较轻松了。

虽然说用驱动写这个功能看起来比较轻松,实际上R3实现也很简单。这里用到的关键之时是Desktop Heap会在GUI进程中映射到用户态内存上,这也就给了我们可乘之机。简单介绍一些Desktop Heap是什么。我们都知道一个桌面都有个Desktop Object的对象,而实际上美国Desktop Object都会有一个对应他的Desktop Heap。Desktop Heap主要存储用户交互对象(user interface objects),这其中就包括Window,Menu,Hook等等。既然Hook存储在Desktop heap,而且Desktop heap又刚好映射到R3内存,那么我们就可以顺利的读取他了。这里,可能会有一个疑问,怎么知道Hook存储在Desktop Heap,而不是Share Heap或者其他。实际上Windows的Win32k中有一个Handle Information Table,指明了每种Object的存储类型。

现在是已经知道了可以去读Hook对象,但是上哪去读就是要解决的问题了。这里就要提到老生常谈的Sharedinfo了。用户态的Sharedinfo获取方法很多,顺手就行。与上面问题相关的就是Sharedinfo里面会有一个Handle Entry Table,里面存储的就是包括Hook在内的User Object。Entry的结构如下(来自reactos):

1
2
3
4
5
6
7
8
9
10
11
typedef struct _HANDLEENTRY
{
PHEAD pHead;
PVOID pOwner;
BYTE bType;
BYTE bFlags;
WORD wUniq;
} HANDLEENTRY, *PHE, *PHANDLEENTRY;

其中pHead指向Object,bType表示object类型。HOOK的bType是5。所以这里我们只需要在bType为5的时候继续下面的操作。

pHead肯定是指向的内核内存,所以我们无法直接访问HOOK的内部情况。我们需要找到这个Object映射到R3的内存地址才行。幸运的是这种关系也比较简单明了。在Teb->Win32ClientInfo.ulClientDelta就存放了对应的关系Delta值。计算方法如下

ObjectInR3 = HANDLEENTRY.pHead - Teb->Win32ClientInfo.ulClientDelta。

20121226220202

这样也就得到了HOOK Object。接下来的事情就好办了,HOOK Object的第一项就是HHOOK。只需要UnhookWindowsHookEx((HHOOK)Hook->head.h);就能卸载钩子了。

NTInternals

关于整型数符号位扩展的一点心得

最近写的程序中遇到了整型数符号位扩展的小问题。稍稍看了下,写在这里备忘。

这里举个例子:

1
2
3
4
// case 1
long i = -1;
long long q = i;
1
2
3
4
// case 2
long i = -1;
unsigned long long q = i;
1
2
3
4
// case 3
unsigned long i = 0xffffffff;
long long q = i;
1
2
3
4
// case 4
unsigned long i = 0xffffffff;
unsigned long long q = i;

那么这4肿情况中q都是多少呢?
实验结果是case 1 和 2,他们的q的值(这里都表示为无符号)0xffffffffffffffff,而case 3 和 4 中q的值为0x00000000ffffffff。
看到这里,大概就能推测c++的转换策略。即以源操作数的类型为依据,对其进行扩展,然后赋值到目标操作数,他并不在乎目标的类型有无符号。

看了相关编译完成后的汇编代码可以确认这一点,case 1和2的汇编代码完全相同,而3和4也是一样。更具体一点来说。有符号的情况下调用了cdq对符号位进行扩展,然后将edx赋值到q的高位,而无符号的情况下,简单xor了寄存器,然后赋值到q的高位。

Tips

dbgLua,让lua脚本也能控制windbg进行调试(更新1.0.1.1)

关于dbgLua:
这是一个让windbg支持lua脚步的扩展程序。写这个程序的主要目的是希望能简单的取代windbg本身的脚本。因为我确实不喜欢windbg那种形式的脚本。

使用方法:将dbgLua.dll拷贝到windbg的winext下,编写lua脚本。调试的时候,在输入框中输入“!dbgLua.run d:\sample.lua”其中“d:\sample.lua”是你的脚步路径。

以下是1.0.0.1版本所支持的lua函数(后续可能会慢慢添加更多,看需求了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dbgLua 1.0.1.1 API
dprint 输出信息到windbg command窗口,例如dprint("hello")
exec 执行一条windbg命令,例如exec("bp kernel32!CreateFileW")
getreg 获得当前被调试对象的寄存器数据,例如eax_val = getreg("eax")
setreg 设置当前被调试对象的寄存器数据,例如setreg("eax", 123456)
readbyte 读取当前被调试对象的内存器数据,大小1字节
readword 同上,大小为2字节
readdword 同上,大小为4字节,例如mem_val = readxxxx(0x410000)
writebyte 写入当前被调试对象的内存器数据,大小1自己
writeword 同上,大小为2字节
writedword 同上,大小为4字节,例如writexxxx(0x410000, 654321)
readunicode 读取一个unicode字符串,例如str = readunicode(0x410000)
readascii 读取一个ascii字符串,例如str = readascii(0x410000)
wait 等待事件,例如exec("bp kernel32!CreateFileW;g");wait();
evalmasm masm表达式求值,例如val = evalmasm("11+2*3")
evalcpp cpp表达式求值,例如val = evalcpp("sizeof(char)")
getmoduleinfo 通过模块名获得模块基址和大学,例如base,size = getmoduleinfo("kernel32")
search 二进制查找,例如found = search(base, size, "cc 89 75 fc eb ")</blockquote>

具体的结合这些函数进行调试的例子还没有准备好,等有机会了,我会准备好调试案例放到这里来。

另外这是一个初始版本,不保证没有bug,如果你在使用中发现了bug,或者有好的想法,例如添加什么函数功能,不妨联系我。

下载:

dbgLua(v1.0.1.1)

dbgLua(v1.0.0.1)

Debugging

关于判断文件是否存在最高效的函数

判断文件存在方法有很多,例如CreateFile,FindFirstFile,GetFileAttributes,PathFileExists等等。但是哪一种更加高效呢?其实作为常识,可能都能判断出GetFileAttributes和PathFileExists会比较快(而实际上PathFileExists就是调用的GetFileAttributes)。

下面是google一份开源代码中提到的统计结果

1
2
3
4
5
// NOTE: This is the fastest implementation I found. The results were:
// CreateFile 1783739 avg ticks/call
// FindFirstFile 634148 avg ticks/call
// GetFileAttributes 428714 avg ticks/call
// GetFileAttributesEx 396324 avg ticks/call</blockquote>

为什么会这样呢?大概了看了下,原因应该是这样的。

1.CreateFile会创建句柄,需要一个完整IO流程,所以需要的时间比如非常长。
2.FindFirstFile回去查询文件夹的文件,虽然不会真正的打开文件句柄,并且在文件已经被缓存的情况下,走的是fastio流程,所以查询时间大幅下降,但是操作略微繁琐,导致他不是最好的选择。

  1. GetFileAttributes 和GetFileAttributesEx 也设置了QueryOnly标志,不需要获得真正的句柄,并且能够走fastio流程,也没有文件夹查询等工作,所以速度最快。

那么为什么GetFileAttributesEx 会快那么一点点呢?因为这个函数少了一个获取BasicInformation,也就是少了一个fastio流程。所以速度更快。这样看来,自己实现一个PathFileExistsEx效率可以高过PathFileExists了。(其实没多大实际意义)

google就是这样做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool File::Exists(const TCHAR* file_name) {
ASSERT1(file_name && *file_name);
ASSERT1(lstrlen(file_name) > 0);
// NOTE: This is the fastest implementation I found. The results were:
// CreateFile 1783739 avg ticks/call
// FindFirstFile 634148 avg ticks/call
// GetFileAttributes 428714 avg ticks/call
// GetFileAttributesEx 396324 avg ticks/call
WIN32_FILE_ATTRIBUTE_DATA attrs = {0};
return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs);
}

NTInternals

PIO读IDE

经过各种代码的东拼西凑、改来改去,总算是把PIO读取硬盘信息的代码“写”好了,上图是读取硬盘的前512字节的效果图。目前看来还是很挫,原因有两点:

1.只支持LBA48的读取方法,不支持CHS,LBA28,虽然这两个方法的读取范围很有限,但是感觉至少要把LBA28给支持了才行。
2.很郁闷的一点,这个读取代码读取成功了,但是IO后返回的状态值是错误的。不知道哪里出了问题,会不会是虚拟硬盘太小而不能用LBA48的问题呢?没有头绪。


补充1.通过IDENTIFY DEVICE命令发现,可能由于设置的虚拟硬盘比较小的原因,虚拟硬盘不支持48bit的地址。IDENTIFY DEVICE会通过PIO方式返回一个256字(512字节)的数据。其中第83个字的第10位表示是否支持48bit的地址。如下图(来自ATA官方手册AT Attachment with Packet Interface - 6)。

补充2.由于不支持LBA48,我还是实现了LBA28。不过这个只能访问128G的硬盘了。至于CHS目前还是不考虑实现。
补充3.PIO写的方式大概也是差不多的。准备慢慢实现,还有DMA读写硬盘也需要了解下。不过好消息是现在基本能看懂ATA的手册了。
补充4.MiniKernel的代码依然写得很挫,暂时不准备共享出来,因为共享出来也没啥用,想学写系统的也看不懂那种烂代码。
补充5.感觉读写硬盘是一个挺有意思地方,完全可以单独拿出来写一个系列的blog。只是有没有时间和懒不懒的问题。
补充6.补充5的最后一句是P话,时间肯定是有的,就是懒而已。。。

一个月一篇文章。。。多一点都没有。。。我果然是个需要被监督的人。。。

MiniKernel