GUID TOOL —— 一个转换GUID格式的小工具

周末闲来无事,逆向点有趣的功能的时候遇到这样一个问题。有些16进制的数貌似就是GUID,但是需要转换为注册表形式,才方便在注册表里面搜索。所以就写了个小工具转换16进制,C语言格式以及注册表格式的GUID。

usage: guid.exe <<-r|-c|-h> guid_string> | <-g>
-r Format registry guid string.
-c Format C code guid string.
-x Format HEX guid string.
-g Create new guid.

20140223220404

20140223220314

下载:guid

Tips

SSD TRIM功能的一些记录

如今SSD越来越普及了,本来就想了解下关于SSD的一些情况,正好工作中有机会接触这一块的东西,很幸运。这里先记录一些已知知识,方便以后自己查阅。

说到SSD,第一个想到的就是读写速度快。那个就要归功于其存储原理,关于SSD存储原理的文章很多,我这里简单通俗的描述一下:

  1. SSD存储不同于机械硬盘,他没有机械硬盘所谓的扇区、柱面,磁头。查询逻辑地址上的数据没有机械上的寻址(没有马达)。SSD的存储介质是闪存。
  2. SSD存储数据被覆盖的时候不会马上覆盖原有数据,而是继续往之前没有写的闪存上写。因为闪存擦除次数非常有限,同一个地方小范围反复擦除会导致整个SSD的寿命缩短。
  3. SSD内部是有自己的GC(垃圾回收器),这个GC擦除不需要的数据以及调整需要的数据的位置,能帮助SSD进行擦除平衡。

而这里重点要说到的TRIM功能就是辅助GC更好工作的一环。从Windows 7开始,文件系统上已经集成了自动TRIM功能。但是早些时候的系统,例如Windows XP,就没有这样的功能了。所以手动TRIM工具就出现了。比如Intel的固态硬盘工具集,其中就包含了手动TRIM功能。也许,有人回想,SSD的高端用户团体怎么会还在使用XP呢?不幸的是还真有这样一群坚守XP的SSD用户。下面就记录一些关于执行TRIM要做的事情。

先决条件:

  1. 要执行TRIM,首先要确保自己的硬盘模式是AHCI的。
  2. 然后,系统需要时XP SP2 RC2以上(我想就算是XP的用户,现在也应该都是SP3了吧)。

上面的第一条非常重要,因为目前世面上的大部分盗版盘和所有正版安装盘,都是没有带AHCI驱动的。也就是说,如果你的BIOS上把硬盘模式调整为AHCI,那你回没有任何意外看到一个蓝屏。少部分盗版系统盘会说明自己是支持AHCI的,否则,就需要在IDE/ATA模式下,安装系统,然后去网上找到你的BIOS所指定AHCI驱动,安装后在把BIOS调整回AHCI。

最后,来看看执行TRIM的一种思路:

  1. 判断系统版本,硬盘的控制器,SSD是否支持TRIM。
  2. 创建多个1G的文件,直到占满所有磁盘空间。
  3. 获得这些文件的基于卷的簇。
  4. 将簇转换成基于卷的逻辑地址。
  5. 将基于卷的逻辑地址转换成基于硬盘的逻辑地址。
  6. 按照ATA文档,发送TRIM指令。
  7. 删除所有创建的1G文件。

上面有几个和ATA相关的简单介绍下。首先是SSD是否支持TRIM的问题,需要发送DEVICE IDENTIFY指令,获得硬盘数据,其中WORD 169表示是否支持TRIM,如果是1就是支持了。其次,发生TRIM指令,实际上发送的是DATA SET MANAGEMENT指令,其中Features register设置为1,即为TRIM指令了。至于如何在没有驱动的情况下发生这些指令(这也是我要求系统版本要是XP SP2 RC2以上的原因),可以利用DeviceIOControl函数发送IOCTL_ATA_PASS_THROUGH来完成。

这些记录已经很详细了,那几百行的代码就没必要贴出来了。

另外TRIM还有一个思路,实现起来可以麻烦一些,简单说说吧:

  1. 首先获得NTFS的Bitmap,获得空闲的簇。
  2. 转换空闲的簇到基于硬盘的逻辑地址。
  3. 发送TRIM指令

之所以说这个比较麻烦,是因为他需要保证在进行TRIM的时候,NTFS不发生写操作。方法就是LOCK VOLUME,但不幸的是,系统盘是没法LOCK的,所以这就不得不写一个Native Application,放在开机的时候运行,也就是Check Disk运行的时机。这个思路的优点是:TRIM全面,精确,速度快。

差不多就是这些了,希望这个记录对自己和他人都有所帮助。

Tips

一点有关Ntdll中提供的bitmap系列函数

我们都知道STL中提供了一个bitset类,但是在我真正操作有关文件系统的时候,发现这个类提供的功能并不能满足我的需求。幸运的是Ntdll中提供了一套操作bitmap的API。于是我抽了点时间把这几个API总结了一下,写成了一个类。这个类只是简单的对Ntdll的bitmap相关API做很浅的封装,没啥好说的。要说的是这套bitmap的API用起来确实很方便。
这些API包括:
RtlInitializeBitMap
RtlFindClearBits
RtlFindClearBitsAndSet
RtlFindClearRuns
RtlFindLastBackwardRunClear
RtlFindLongestRunClear
RtlFindNextForwardRunClear
RtlFindSetBits
RtlFindSetBitsAndClear
RtlSetAllBits
RtlSetBits
RtlClearAllBits
RtlClearBits
RtlNumberOfClearBits
RtlNumberOfSetBits
RtlAreBitsClear
RtlAreBitsSet
以上这些,在MSDN上都能查到API的详细文档介绍。唯一不方便的就是使用的时候需要GetProcAddress一下。所以我为了自己以后使用方便才写了一个类。

在项目(https://github.com/0cch/bitmap)中,bitmap_class是封装类,整个工程是一个使用这套API,获得文件系统的bitmap,并且查找空闲簇的一个例子。

2014-01-18_101801

Tips

总结和展望:质量比较大点,就不容易被风吹动

又到了一年一度的总结和展望。时间过得真快,2012年定计划的那会好想就发生在昨天。还能清晰的记得当时的计划,当然也是因为计划定的足够的简单。当然还是把个人的计划放在后面,先来总结下2013年的工作。这年的工作真是富有戏剧性,工作中做了一些网页前端的工作,这确实让我措手不及,不过既然组里有这样的需求,也只能硬着头皮上了,还好是这也并不算忙,不影响个人计划的进展。另一方面,公司拿我们部门和其他公司合并了,
换句话说,我们部门被裁了,只不过以一种漂亮的方式。就如同所有的接纳新员工的老大一样,新公司的leader会给你谈未来画大饼。只不过,对不起,我真的不看好这种抱团取暖的合并,所以,我选择离开。由于平时有一定的积累,所以找份靠谱的工作也并不是特别难的事情。能预感到新的工作会比较忙,不过我想,应该还是能hold住的。

当然,要说最放不下的,要数公司的健身房。掐指一算,已经坚持锻炼了16个月了!依稀记得12年是拼命跑25分钟能跑到4km多点,而13年已经能跑过5km了。一年的时间,让我在25分钟里能超越过去的自己两圈,“想想还有点小激动呢!”。另外肱二头肌和肱三头肌已经比较明显了,上臂粗了好多,穿短袖看起来MAN了好多,不过腹肌虽然能看出来,但还是不算特别明显。这里不得不提醒一下sysdbg的博主,“我督促你健身了16个月,你是不是应该请我吃金钱豹啊?!别客气,跪谢就免了,嗯!”。噢!说到这货,我还想到了一个事情,就是练字。现在字终于写的有点人样了,虽然写急了还是很丑,但是应该比以前好点了吧……大概……是这样。

我记得,13年的个人计划只有一个就是山寨sysinternals的工具。这次算是完成的比较好吧,工具集中大部分小工具都山寨了,只有个别界面特别复杂,功能特别强大的工具没有山寨。实在是没有动力写界面。另外,又一次把minikernel重写了一次,也围绕这个发布了不少的blog,但是这个minikernel还是缺少挺多的东西,比如很核心的多进程和多线程。一方面是突然遇到公司方面的事情打断了进度,另一个方面也是自己懒。

至于2014年的个人计划,还真不好说,还不知道新工作是个什么情况,但是我个人还是比较想写一个脚本语言以及完善这个minikernel的。另外继续健身,练字也是必须的。最近因为灌篮高手高清重置版播出了,也导致我又想没事抽出点时间打打球了。另外在13年,花了很多时间看dota2的视频,这个也是被sysdbg的博主吐槽了好久,最近也已经开始戒dota了,时间真是越来越不够用。
希望15年写总结的时候,能看到一个质量更大的自己吧,希望写的这些计划能完成75%以上。

最后,还是祝福家人,朋友,自己在新的一年里幸福安康,合家欢乐!!!

XPerfHelper —— XPerf的Windows命令行脚本生成工具

使用过XPerf的应该都知道,写一个XPerf的命令行是多么的麻烦,如果不太熟悉,需要反复的查看帮助里的参数。所以一般情况下,大家会把命令写到一个cmd或者bat的脚本中,这样就可以双击来使用XPerf,只需要第一次费点心思写脚本罢了。但是我还是觉得,即使是只用写一次脚本,也还是挺麻烦的,于是写了这个小工具,生成cmd脚本文件。妈妈再也不用担心我的XPerf命令写错了。

20131201221223

如上图所示,我们可以选择kernel flag和stackwalk,然后选择providers,点击OK,生成cmd文件即可。下面是一个生成的cmd的内容:

20131201222315

下载XPerfHelper

Debugging

使用ETW对程序进行监控和分析

ETW(Event Tracing for Windows)是Windows提供的对程序进行事件记录,跟踪,使用的机制。我们可以利用这个机制对程序进行调试和性能分析。从Windows Vista开始,ETW已经非常好的融合在Windows内核之中了,在Windows 7开始,这一个机制更加完善,几乎记录了Windows运行的每一个细节。我个人猜测,从Windows Vista开始到Windows 7直到现在的Windows 8,性能都在不断提高,ETW机制应该是功不可没的。

ETW是Windows提供的机制,我们要使用他还需要工具,这些工具我们可以自己开发,因为Windows提供了调用接口。当然,更惬意的选择就是直接使用Windows提供的工具集WPT(Windows Performance Toolkit)。利用WPT和SDK,我们可以将ETW融入到我们自己的程序中,帮助我们调试程序和提升性能。

以下是我们需要的工具:
SDK:

  1. ECMangen.exe
  2. mc.exe

Windows:
WEVTUtil.exe

WPT:

  1. XPerf.exe
  2. XPerfView.exe

首先我们需要用SDK中的工具ECMangen,生成一个manifest文件,这个文件用来描述记录的事件,如图。
20131117114336
这里,我们首先创建一个Provider,接下来创建一个Event,参数可以随便填下,就像上图所示。作为演示,这里一个Event就够了(FirstEvent)。
然后保存为0CChProvider.man。

接下来我们MC来生成一个头文件,一个资源文件和两个二进制文件。命令行如:
mc -um C:\etw\0CChProvider.man
20131117114801

然后我们可以创建一个工程,加入这个头文件和资源文件。并且在代码中插入写事件的代码,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "stdafx.h"
#include
#include "../../0CChProvider.h"
int _tmain(int argc, _TCHAR* argv[])
{
EventRegisterMy0CChProvider();
EventWriteFirstEvent(L"Hello ETW World!");
Sleep(1000);
EventWriteFirstEvent(L"Bye ETW World!");
EventUnregisterMy0CChProvider();
return 0;
}

在这里,我们利用EventRegisterMy0CChProvider先注册自己的Provider,接下写事件才会发挥作用。记录事件后,我们需要反注册Provider。
好了,演示代码就这么一点,然后编译即可。

接下来我们需要注册这个Provider给系统,需要使用到系统自带的工具WEVTUtil.exe。
wevtutil im C:\etw\0CChProvider.man

注册成功后就可以利用XPerf开启ETW,然后运行程序,查看记录的事件了。

  1. xperf -start 0cch -on My0CChProvider:::’stack’
  2. xperf -on base
  3. Run 0CChProvider.exe
  4. xperf -stop 0cch -stop -d d:\0cch.etl
  5. xperf d:\0cch.etl

XPerfView会生成分析数据如图:
20131117115853

20131117120007

整体来说,想简单的使用ETW也就是这么简单,当然你也可以把他弄得很复杂,这里就不介绍了。话说sysdbg早就让我写点XPerf的东西,但是因为各种懒没写,刚好最近终于有空了,就先写了这么个简单的介绍,就当作一个开篇吧,话说某人的Blog好久没更新了呀。。。

DebuggingNTInternals

Windbg script中获得调试环境的基本信息

今天继续来玩Windbg script。在写复杂的脚本的时候,可能需要根据调试的环境,指定不同的脚本代码来运行。而Windbg貌似没有提供很好的方式,让脚本得知调试环境。还好,我们可以用一些其他的方式获得这些信息,例如:写一个扩展程序来设置这些信息到Aliase上,0cchext就实现了这个功能。另外一个方式就是使用脚本自身来获得一些简单的信息,算是个windbg script中的小把戏吧。脚本如下:

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
$$ Initialize script environment
$$ Author: nighxie
$$ Blog: 0cch.net
$$ @#NtMajorVersion @#NtMinorVersion - System version number.
$$ @#DebugMode - 0:kd 1:lkd 2:user
ad /q ${/v:$sharedata}
.catch {
.foreach /pS 2 (${/v:$addr} {!kuser}) {
aS ${/v:$sharedata} ${$addr};
.leave;
}
}
.block {
r @$t0=${$sharedata};
aS /x ${/v:@#NtMajorVersion} @@C++(((nt!_KUSER_SHARED_DATA *)@$t0)->NtMajorVersion);
aS /x ${/v:@#NtMinorVersion} @@C++(((nt!_KUSER_SHARED_DATA *)@$t0)->NtMinorVersion);
}
ad /q ${/v:$sharedata}
.catch {
r @$t0 = 0;
.foreach (${/v:$addr} {lm1m m nt}) {
r @$t0 = ${$addr};
.leave;
}
}
.if ($vvalid(@$t0, 1)) {
aS ${/v:@#DebugMode} 0;
.foreach (${/v:$val} {.catch{? @eax}}) {
.if ($scmp("${$val}", "\'@eax\'")==0) {
aS ${/v:@#DebugMode} 1;
}
}
}
.else {
aS ${/v:@#DebugMode} 2;
}

DebuggingTips

Windbg script的notepad++语法高亮配置文件

经常写复杂的windbg脚本的程序员肯定知道,windbg脚本的宏替换的执行方式,让人非常的不舒服。另外windbg的脚本也没有一个好用的语法高亮编辑器,所以让脚本写起来更加痛苦。前者看来是已成定局,很难解决了。不过后者还是有机会改善的,闲暇之余,写了一个notepad++上的windbg脚本的语法高亮配置文件。以上一篇文章中的windbg脚本为例,高亮效果如下图:

20131015164715

导入方式也非常简单,点击[语言]菜单下的define your language,在弹出的对话框中点击导入按钮,导入配置文件即可。

20131015164704

下载脚本wds

Debugging

Windbg内核调试查看窗口句柄信息的脚本

十一长假瞬间就结束了,整一周都在玩,也没有研究什么好玩的东西,这里就分享一个以前写的windbg脚本吧。通途是内核调试查看窗口句柄信息。用法很简单,例如 $$>a<hwnd.wds 000207B8。运行结果如下图:

20131007195716

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
75
76
77
78
79
80
81
82
83
84
$$ Convert HWND to tagWnd
$$ Author: nighxie
$$ Blog: 0cch.net
.if (${/d:$arg1}) {
.if (${/d:$arg2}) {
.if (${$arg2} == 1) {
r $t0 = nt!PsActiveProcessHead
.for (r $t1 = poi(@$t0);(@$t1 != 0) & (@$t1 != @$t0);r $t1 = poi(@$t1)) {
r? $t2 = #CONTAINING_RECORD(@$t1, nt!_EPROCESS, ActiveProcessLinks);
as /x ${/v:$ProcAddr} @$t2;
as /ma ${/v:$ImageName} @@c++(&@$t2->ImageFileName[0]);
.block {
$$ .echo ${$ImageName}
.if ($sicmp("${$ImageName}", "explorer.exe") == 0) {
.echo Found the process at ${$ProcAddr};
.process /p /r ${$ProcAddr};
ad ${/v:$ImageName};
ad ${/v:$ProcAddr};
.break;
}
}
ad ${/v:$ImageName};
ad ${/v:$ProcAddr};
}
}
}
r @$t1 = ${$arg1};
r @$t0 = win32k!gSharedInfo;
.if ((@$t1&0xffff) < @@C++(((win32k!tagSHAREDINFO *)@$t0)->psi->cHandleEntries)) {
r @$t0 = @@C++(((win32k!tagSHAREDINFO *)@$t0)->aheList);
r @$t0 = @@C++(@$t0+(@$t1&0xffff)*sizeof(win32k!_HANDLEENTRY));
r @$t0 = poi(@$t0);
.printf "HWND: %p\n", @@C++(((win32k!tagWnd *)@$t0)->head.h);
.printf /D "tagWnd * @ %p\n", @$t0;
.if (@@C++(((win32k!tagWnd *)@$t0)->strName.Buffer) != 0) {
.printf "Window Name: %mu\n", @@C++(((win32k!tagWnd *)@$t0)->strName.Buffer);
}
.printf /D "tagCLS * @ pcls) win32k!tagCLS\">%p\n", @@C++(((win32k!tagWnd *)@$t0)->pcls);
.if (@@C++(((win32k!tagWnd *)@$t0)->pcls->lpszAnsiClassName) != 0) {
.printf "Window Class Name: %ma\n", @@C++(((win32k!tagWnd *)@$t0)->pcls->lpszAnsiClassName);
}
.if (@@C++(((win32k!tagWnd *)@$t0)->spwndNext) != 0) {
.printf "Next Wnd: %p\n", @@C++(((win32k!tagWnd *)@$t0)->spwndNext->head.h);
}
.if (@@C++(((win32k!tagWnd *)@$t0)->spwndPrev) != 0) {
.printf "Previous Wnd: %p\n", @@C++(((win32k!tagWnd *)@$t0)->spwndPrev->head.h);
}
.if (@@C++(((win32k!tagWnd *)@$t0)->spwndParent) != 0) {
.printf "Parent Wnd: %p\n", @@C++(((win32k!tagWnd *)@$t0)->spwndParent->head.h);
}
.if (@@C++(((win32k!tagWnd *)@$t0)->spwndChild) != 0) {
.printf "Child Wnd: %p\n", @@C++(((win32k!tagWnd *)@$t0)->spwndChild->head.h);
}
.if (@@C++(((win32k!tagWnd *)@$t0)->spwndOwner) != 0) {
.printf "Own Wnd: %p\n", @@C++(((win32k!tagWnd *)@$t0)->spwndOwner->head.h);
}
.if (@@C++(((win32k!tagWnd *)@$t0)->lpfnWndProc) != 0) {
.printf /D "pfnWndProc: head.pti->pEThread)->Tcb.Process);u @@C++(((win32k!tagWnd *)@$t0)->lpfnWndProc)\">%p\n", @@C++(((win32k!tagWnd *)@$t0)->lpfnWndProc);
}
.printf "Visiable: %d\n", @@C++((((win32k!tagWnd *)@$t0)->style & (1<<28)) != 0);
.printf "Child: %d\n", @@C++((((win32k!tagWnd *)@$t0)->style & (1<<30)) != 0);
.printf "Minimized:%d\n", @@C++((((win32k!tagWnd *)@$t0)->style & (1<<29)) != 0);
.printf "Disabled: %d\n", @@C++((((win32k!tagWnd *)@$t0)->style & (1<<27)) != 0);
.printf "Window Rect { %d, %d, %d, %d}\n", @@C++(((win32k!tagWnd *)@$t0)->rcWindow.left), @@C++(((win32k!tagWnd *)@$t0)->rcWindow.top), @@C++(((win32k!tagWnd *)@$t0)->rcWindow.right), @@C++(((win32k!tagWnd *)@$t0)->rcWindow.bottom);
.printf "Clent Rect { %d, %d, %d, %d}\n", @@C++(((win32k!tagWnd *)@$t0)->rcClient.left), @@C++(((win32k!tagWnd *)@$t0)->rcClient.top), @@C++(((win32k!tagWnd *)@$t0)->rcClient.right), @@C++(((win32k!tagWnd *)@$t0)->rcClient.bottom);
}
.else {
.printf "HWND is out of range.\n";
}
}
.else {
.echo "Usage $$>a<${$arg0} HWND(HEX)"
.echo "e.g. $$>a<${$arg0} 0x60962"
}

DebuggingTips