0CCh Blog

proc_study —— 仿PsList的进程查看工具

proc_study 是我通过逆向PsList而写出来的小工具,如果在本地查看进程,这个工具和pslist没有任何区别。因为实现查看进程的方式也是和pslist一模一样的。另一方面,他缺乏pslist的查看远程计算机的进程的功能。没有实现这个并不是不知道怎么实现,是我半天也没搭建出这样的一个远程环境,真够郁闷的。这个应该是年前的最后一个study系列的工具了。期待蛇年有时间山寨更多工具,嘿嘿~~~

20130204204101

这个工具的使用方法和命令行参数可以直接参看PsList的。因为整个Usage我都是直接山寨过来的。

Usage: proc_study [-d][-m][-x][-t][name|pid]
-d      Show thread detail.
-m     Show memory detail.
-x      Show processes, memory information and threads.
-t       Show process tree.
name Show information about processes that begin with the name
specified.
-e      Exact match the process name.
pid Show information about specified process.

All memory values are displayed in KB.
Abbreviation key:
Pri Priority
Thd Number of Threads
Hnd Number of Handles
VM Virtual Memory
WS Working Set
Priv Private Virtual Memory
Priv Pk Private Virtual Memory Peak
Faults Page Faults
NonP Non-Paged Pool
Page Paged Pool
Cswtch Context Switches

下载proc_study

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

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

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

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

分享自己打造的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:

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

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

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,也是目前该工具具有的功能。

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

设置线程名

给线程命名的作用主要还是为了调试方便。其他的好处也没有了,至少我没想出来。这里说一下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


#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)
{
}
}

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):


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);就能卸载钩子了。

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

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

这里举个例子:

// case 1
long i = -1;
long long q = i;

// case 2
long i = -1;
unsigned long long q = i;

// case 3
unsigned long i = 0xffffffff;
long long q = i;

// 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的高位。

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函数(后续可能会慢慢添加更多,看需求了)

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)

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

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

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

// 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流程,所以查询时间大幅下降,但是操作略微繁琐,导致他不是最好的选择。
3. GetFileAttributes 和GetFileAttributesEx 也设置了QueryOnly标志,不需要获得真正的句柄,并且能够走fastio流程,也没有文件夹查询等工作,所以速度最快。

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

google就是这样做的:


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);
}