首页
社区
课程
招聘
[原创]注册表文件解析完整解决方案
发表于: 2010-6-26 23:46 34480

[原创]注册表文件解析完整解决方案

2010-6-26 23:46
34480
UESTC 鹿剑
开始是看大米的那个增删改方案,不过,由于好多GUI处理干扰,头很大,最后到网上找到了下面这个库,大米那个其实就是基于这个改写的,最近要用到这个,所以好好看了看,做了良好的封装,支持几乎所有操作:
枚举子键、枚举键值,修改、删除、增加键值、子键,关键是接口良好,移植使用相当方便,就算不懂注册表格式的程序员移植也很简单。
注册表格式虽然已经可以找到相对完整资料,但是自己来解析的话难度还是有难度,现今比较完整的资料都是英文,这个对于英文不太好的同学来说是个不小的挑战,另外要读懂那些资料然后自己写出完整代码来也不是一件容易的事,最简单的做法就是在前人的代码上来进行二次开发。网上比较完整的资料就是这个了:
NTREG - Window registry file reader / writer library
作者是 Petter Nordahl-Hagen.
下载地址可以使用谷歌搜索引擎来获取,输入关键字Ntreg.h之后前几页都是,请不要使用百度,然后找不到时问我要下载地址,相信外事问谷歌,内事问百度这句话你还是知道的。下载回来之后一般直接编译成静态库就可以了,附件里有我编译好的静态库。原作者的代码是使用运行时C写的,所以在使用时请使用extern “C” 包含头文件。废话不多说,接下来正式编程,首先遇到的问题是访问被系统独占的文件,XP下可以枚举系统句柄表,DuplicateHandle句柄过来,原因就是xp下可以用复制句柄权限打开system进程,到了Windows7下就完全不行了,用复制句柄权限无法打开system进程(我很意外,为什么资源监视器还是能看到system进程打开的文件呢?自己逆向水平太差了,没本事去分析资源监视器,还望高手指点一二^_^),所以为了通用,我写了一个驱动来实现句柄权限修改,都到了驱动,可以访问注册表文件的方法很多了,直接使用驱动读写文件等等,我之所以选择修改句柄权限的方法是因为方法稳定,比较简单,首先获取进程句柄表,遍历句柄表,找到目标句柄,修改权限,为了通用(其实是我也真的不知道到底那个权限域和SDK里面的权限对应关系),用户态传入两个句柄,目标句柄会获取到源句柄相同的访问权限,这样就不需要关心在各个系统下SDK里面的权限和句柄对象权限的对应关系了,我也没有能力像别人一样实现句柄表的手工解析,资料倒是很多,读起来头大,我又比较懒,所以使用系统提供的ExEnumHandleTable函数了,一句足以搞定,手动解析还要分层次,麻烦。驱动代码大致如下:
bool EnumHandleCallBack(PHANDLE_TABLE_ENTRY Entry,HANDLE handle,ENUMHANDLE_PARAMETER* Param)
{
	KdPrint(("函数被调用! 句柄:%08x\n",handle));
	if(Param->AccessMask==0)
	{
		if(handle==Param->SorHadnle)
		{
			KdPrint(("获取源权限成功!"));
			Param->AccessMask=Entry->GrantedAccess;
			return true;

		}
	}
	else
	{
		if(handle==Param->DesHandle)
		{
			KdPrint(("修改成功!\n"));
			Entry->GrantedAccess=Param->AccessMask;
			return true;
		}
	}
	return false;
}

NTSTATUS ModifyRight(FILE_RIGHT_MODIFY* Data)
{
	
	NTSTATUS  Status=STATUS_UNSUCCESSFUL;
	KdPrint(("输入句柄:Des %08x---Sour %08x",Data->DesHandle,Data->SourceHandle));
	ENUMHANDLE_PARAMETER * Param=(ENUMHANDLE_PARAMETER *)ExAllocatePool(NonPagedPool,sizeof(ENUMHANDLE_PARAMETER));
	if(Param>0)
	{
		Param->AccessMask=0;
		Param->DesHandle=Data->DesHandle;
		Param->SorHadnle=Data->SourceHandle;
		KdPrint(("%08x\n",PsGetCurrentProcess()));	
		PVOID TableAddr=(PVOID)GetHandleTableFromProcessXp(PsGetCurrentProcess());
		if(ExEnumHandleTable(TableAddr,
			(EX_ENUMERATE_HANDLE_ROUTINE)EnumHandleCallBack,Param,NULL))
		{
			KdPrint(("Enum Success!\n"));
			if(ExEnumHandleTable((PVOID)GetHandleTableFromProcessXp(PsGetCurrentProcess()),
				(EX_ENUMERATE_HANDLE_ROUTINE)EnumHandleCallBack,Param,NULL))
			{
				Status=STATUS_SUCCESS;
			}

		}
		ExFreePool(Param);
	}

	return Status;
}

NTSTATUS DisModifyRight(PDEVICE_OBJECT pDevice,PIRP pIrp)
{
	PIO_STACK_LOCATION  Stack=IoGetCurrentIrpStackLocation(pIrp);
	pIrp->IoStatus.Information=0;
	NTSTATUS Status;
	if(Stack->Parameters.DeviceIoControl.InputBufferLength<4)
	{
		Status=STATUS_INFO_LENGTH_MISMATCH;
	}
	else
	{
		Status=ModifyRight((FILE_RIGHT_MODIFY*)pIrp->AssociatedIrp.SystemBuffer);

	}
	pIrp->IoStatus.Status=Status;

	IofCompleteRequest(pIrp,IO_NO_INCREMENT);

	return Status;
}


移植时,只需要注意一个地方GetHandleTableFromProcessXp 这个宏我是这样定义的,这个只是在Xp下使用
#define GetHandleTableFromProcessXp(a)  (*(PULONG)((char*)a+0xc4))
当然了,上面这小段代码可以用来更改任何句柄的权限。只需要在自己的驱动力加进去就好了,连分发函数都写好了^-^.
好了,现在任务完成一大半了,我对注册表函数的操作封装为一个类。首相使用传统注册表操作函数读取SYSTEM\\CurrentControlSet\\Control\\hivelist下面的数据,将键名作为我这个注册表的根。

以后所有的操作都是基于键名的完整路径来说的。
比如你要操作HKEY_LOCAL_MACHINE\SOFTWARE\Adobe这个路径,在我这个类里面,你应该传进来的完整路径是:\\REGISTRY\\MACHINE\\SOFTWARE\\Adobe,注意这里的\\REGISTRY\\MACHINE\\SOFTWARE就是SYSTEM\\CurrentControlSet\\Control\\hivelist里面的SOFTWARE对应的键名(上图中划线部分),我说了,这个会作为根键,之后各个函数介绍如下,封装的函数如下:
1.枚举子键
REGSTATUS HiveAnysBase::EnumSubKey(char *FullPath, PVOID Buffer, ULONG BufferSize);
FullPath---eg: \\REGISTRY\\MACHINE\\SOFTWARE\\Adobe
Buffer –返回数据的缓冲区,
BufferSize—缓冲区大小
返回值 REG_NOT_FIND 给的路径没找到
       REG_BUFFER_TOO_SMALL 缓冲区太小
       REG_SUCCESS  查询成功
Buffer里面的数据结构SUB_KEY_INFO,Count指明了PSUBKEY_ENTRY的数目
typedef struct tagSUBKEY_BUFFER
{
        char Name[MAX_PATH];
}SUB_KEY_ENTRY,*PSUBKEY_ENTRY;

typedef struct tag_SUBKEY_INFO/*EnumSubKey返回的数据结构*/
{
        ULONG Count;
        SUB_KEY_ENTRY Entrys[1];
}SUB_KEY_INFO,*PSUB_KEY_INFO;

Eg:
PSUB_KEY_INFO pInfo =malloc(0x1000);
If(EnumSubKey(“\\REGISTRY\\MACHINE\\SOFTWARE\\Adobe”,pInfo,0x1000)==REG_SUCCESS)
{
For(ULONG i=0;i<pInfo->Count’i++)
{
Ptinf(“%s\n”,pInfo->Entrys[i].Name);
}
}

2.枚举键值
REGSTATUS HiveAnysBase::EnumKeyValue(char *FullPath, PVOID Buffer, ULONG BufferSize);
参数同上面是一样的,返回值和1一样。
缓冲区数据结构如下
typedef struct tagKeyValue
{
        ULONG Type;// reg type
        ULONG DataLen; //if REG_DWORD is value key else length of DataBuffer
        char  Name[MAX_PATH];//value name
        unsigned char * DataBuffer;//key value data
}KEY_VALUE_ENTRY,*PKEY_VALUE_ENTRY;

typedef struct tag_KEY_VALUE_INFO/*EnumValue返回的数据结构*/
{
        ULONG Count;Entrys数据的大小
        KEY_VALUE_ENTRY Entrys[1];
}KEY_VALUE_INFO,*PKEY_VALUE_INFO;
特别请注意,如果数据类型是REG_DWORD,则其值放在DataLen域传回,否则数据在DataBuffer域,在使用这个域之后需要你自己释放这个DataBuffer占用的内存,请使用
void FreeBuffer(void *p)
{
        VirtualFree(p,0,MEM_RELEASE);
}函数来释放。

3.修改键值名字
REGSTATUS HiveAnysBase::SetKeyValueName(char *FullPath, char *OldName, char *NewName);
FuPath参数同上,OldName是现在键值的名字,NewName就是要修改成的新名字
4.修改键值
REGSTATUS HiveAnysBase::SetKeyValueData(char *FullPath, char *Name, BYTE *Data, ULONG DataSize);
FullPath参数同上,
Name是键值的名字,
Data是数据缓冲区,
DataSize 缓冲区大小
5. 删除子键
REGSTATUS HiveAnysBase::DeleteSubKey(char *FullPath);
子键的完整路径,比如要删除HKEY_LOCAL_MACHINE\SOFTWARE\Adobe\Photoshop子键就传入\\REGISTRY\\MACHINE\\SOFTWARE\\Adobe\\Photoshop就好了
6.删除键值
REGSTATUS HiveAnysBase::DeleteValue(char *FullPath, char *Name);
FullPath—子键路径
Name ---键值名字

7.增加子键
REGSTATUS HiveAnysBase::AddSubKey(char *FullPath, char *Name);
FullPath—要增加子键的路径
Name—子键名字
Eg:在HKEY_LOCAL_MACHINE\SOFTWARE\Adobe\Photoshop增加一个test的子键
调用为
AddSubKey(“\\REGISTRY\\MACHINE\\SOFTWARE\\Adobe\\Photoshop”,“test”);
8.增加键值
REGSTATUS HiveAnysBase::AddValue(char *FullPath, char *Name, BYTE *Data, ULONG DataSize, ULONG Type);
Fullpath –子键路径
Name—键值名字
Data—键值数据
DataSize—数据长度
Type—键值类型
例:在HKEY_LOCAL_MACHINE\SOFTWARE\Adobe\Photoshop下增加一个键值,名字为
Lujian 的DWORD 值为1的数据
ULONG Data=1;
AddValue(”\\REGISTRY\\MACHINE\\SOFTWARE\\Adobe\\Photoshop”,”lujian”,&Data,sizeof(DWORD),REG_DWORD);

运用上面这个类,就算是你对注册表文件格式一无所知,也可以实现离线解析注册表了,我在这个类的基础上,又派生了一个类,CRegEditEx,这类在上面那个类的基础之上特别提供了对CTreeCtrl控件支持,可参看源代码。

效果图:

  移植指南:

1.        首先在驱动里面加入上面修改句柄权限的代码,修改RegSuport.cpp里面的
BOOL GetRightToAccessFile(HANDLE hSor,HANDLE hDes)
{
        /*
        hSor 源句柄
        hDes 目标句柄,
        目标句柄会获得和源句柄相同的权限
        */
        ASSERT(hSor!=0);
        ASSERT(hDes!=0);
        FILE_RIGHT_MODIFY Data={0};
        Data.SourceHandle=hSor;
        Data.DesHandle=hDes;
        return theApp.m_hDriver.ControDevice(IOCTL_MODIFY_FILE_RIGHT,&Data,sizeof(FILE_RIGHT_MODIFY),0,0);

}
将最后这行换成给你的驱动发送控制码来修改权限的实现代码

2.        我这个代码是在大工程下的一小部分代码 ,代码使用UNICODE编码,如果要在Ascii下使用,修改有关字符处理的部分。
3.        两个内存处理函数:
void * AllocateBuffer(DWORD Size)
{
        void *p=VirtualAlloc(NULL,Size,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        if(p!=NULL)
                RtlZeroMemory(p,Size);
        return p;

}
void FreeBuffer(void *p)
{
        VirtualFree(p,0,MEM_RELEASE);
}

4.        在Windows7下和vista下,驱动里面修改获取程序句柄表的那个宏

5.没有解决的问题,中文键名乱码(中文是Unicde码,但是英文是char,所以没找到好的处理)
2010 7-13,最终解决了中文乱码问题,可可以参考我博客上的解决方法(http://hi.baidu.com/%D0%A1%C2%B9%BD%A3/blog/item/504f3763ea75ec49ebf8f8a7.html),就不放代码了,另外vista以后打不开System进程是因为System是Protected Process,只有同是Protected Process进程才能打开另外的Protected Process,可以已经有人注意到了声音管理那个进程也是Protected Process,用户态也是打不开的,对于Protected Process进程微软现在开放的就只有有限的查选信息权限,除此之外的任何权限打开都会失败……要变成Protected Process进程可以修改EPROCESS的ProtectedFlags位……

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 8
支持
分享
最新回复 (45)
雪    币: 364
活跃值: (152)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
2
支持vista不?
2010-6-27 01:05
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
支持,多谢分享
2010-6-27 02:33
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
4
解析部分完全支持Windows 7 vista 等等,只是那个修改权限的驱动,获取进程句柄表的那行代码需要修改而已,自己已经在Windows7下测试过了,完全没有问题,因为注册表解析库使用的是最新版本……
2010-6-27 11:52
1
雪    币: 7651
活跃值: (523)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
5
这个必须支持~
2010-6-27 16:52
0
雪    币: 466
活跃值: (165)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
6
打个标签备用
2010-6-27 17:35
0
雪    币: 93908
活跃值: (200199)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
7
Support.
2010-6-27 17:59
0
雪    币: 254
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
最近论坛很少进货,支持个
2010-6-27 18:32
0
雪    币: 363
活跃值: (338)
能力值: ( LV15,RANK:310 )
在线值:
发帖
回帖
粉丝
9
不错!支持下!
2010-6-27 21:40
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
嗯。支持,学习中。
2010-6-28 11:55
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
11
支持,顺便提供我找到的资源:
http://pogostick.net/~pnh/ntpasswd/
上传的附件:
2010-6-28 12:04
0
雪    币: 146
活跃值: (182)
能力值: ( LV13,RANK:220 )
在线值:
发帖
回帖
粉丝
12
学习下这个改句柄权限的
2010-6-28 13:47
0
雪    币: 255
活跃值: (49)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
13
感谢分享 ^_^
2010-7-1 13:02
0
雪    币: 324
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
UESTC.....DING
2010-7-1 21:04
0
雪    币: 88
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
谢谢楼主,学习一下。
2010-7-2 15:01
0
雪    币: 88
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
最后一个问题,就是中文乱码,提一个解决办法(试试)

我最近在用VS2005的VC++编程,默认情况下,是支持Unicode。

能不能在这相环境下编程,但楼主提供的源码就要进行unicode方面的修改。

如果有一天我也要用到这方面的编程,我会利用楼主提供的代码进行修改。
2010-7-2 15:06
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
17
没有这么简单,我的开发环境就已经是vs2008了,并且整个工程都是UNICODE,很奇怪,所有键值名字,子健名字,如果是英文就是ascii的char,如果是中文却是 UNICODE,如果中英混合,就更混乱了……
2010-7-2 15:33
0
雪    币: 387
活跃值: (25)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
18
strong, powerful
2010-7-2 16:28
0
雪    币: 234
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
想当年我第一次到实验室的项目就是HIVE文件解析..怀念啊...
2010-7-2 18:48
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
windows7的system进程是有点奇怪
2010-7-5 11:21
0
雪    币: 234
活跃值: (83)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
21
好东西,学习加收藏。。。
2010-7-5 13:14
0
雪    币: 320
活跃值: (183)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
22
mark之 顶楼主
2010-7-5 14:40
0
雪    币: 287
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
23
RegEdit.rar大家都编译通过了吗?怎么没人提出疑问
2010-7-6 16:33
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
看一下源码,看看是怎么实现的
2010-7-10 21:25
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
访问被系统独占的文件在用户级可以绕过句柄保护的
2010-7-10 23:36
0
游客
登录 | 注册 方可回帖
返回
//