当前位置:编程学习 > 网站相关 >>

枚举注册表搜索病毒痕迹的实现思路

文/图 灰狐(D.S.T&&E.S.T)
在第11期的黑客防线中,我那篇关于algsrvs病毒专杀的文章中有点不太完美的地方,就是没有把清理注册表那一部分写完整,一个原因是当时我已经把病毒清理干净了,没有记录下每一个相应键值的位置;还有一个原因就是就算我记录下来了,也是一件相当麻烦的事情,因为病毒在注册表中写入的信息相当多,大概有几十处,这样一个一个删还不把人累死?当然,也不是没有办法,下面就用一种比较简单的思路来进行演示。首先完整枚举所有项,检查其键值是否被病毒修改过,然后进入子项中递归调用此函数完成遍历注册表的功能。
为了程序的通用性,今天我就不用VCL库中的Registry类了,改用Windows操作系统提供的正宗API,这样大家就可以把它完美地嵌入到自己的程序中了。
首先来讲一点基础知识,让没有编程操作过注册表的菜友们也能顺利上手。注册表有五大子键,这我就不废话了,这次我们只拿其中比较重要的一个HKEY_LOCAL_MACHINE来演示,需要遍历注册表的话只需要把函数简单修改下调用五次就没问题了。我们在注册表中某个地方点击右键,选择“新建”,可以看到有以下几种数据类型:字符串值、二进制值、DWORD值、多字符串值、可扩充字符串值。最常用的也是注册表中子项的默认值就是字符串值,它的内部名称为REG_SZ。
关于注册表编程的资料通常都很零散,所以我们最好的参考书就是MSDN了。在MSDN的索引里输入Reg就可以显示出所有以Reg开头的函数,前面那些基本就是跟操作注册表有关的,RegOpenKeyEx、RegCloseKey、RegDeleteValue、RegEnumKeyEx、RegEnumValue、RegQueryValueEx和RegSetValueEx这几个是我们重点需要的。
首先我们来看一下删除病毒键值的方法,为避免误删除,我们先自己建立一个子项:HKEY_LOCAL_MACHINESOFTWAREgrayfox,选中它后在右边的空白区域点右键,“新建->字符串值->test”,双击“test”,在“数值数据”里输入“test1,test2,test3”。我们假设test是启动项,test1、test2是本来的启动程序,病毒又向其中添加了一个test3,我们的目的就是使其还原为test1、test2。这里要注意的是,我们是不能直接使用RegDeleteKey将其删除的,如果里面只有test3这个值的话是可以的,但如果还有其他值的话,需要使用RegSetValueEx重新设置。
我们使用VC 6.0建立一个控制台程序测试效果,代码如下。

#include <stdio.h>
#include <windows.h>

void main()
{
HKEY hKey = NULL;
DWORD rc;
rc=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,"software\grayfox",0,
KEY_ALL_ACCESS,&hKey);
if( rc == ERROR_SUCCESS )
{
unsigned char buffer[MAX_PATH]="";
DWORD type = REG_SZ;
DWORD size = sizeof(buffer);
if(RegQueryValueEx(hKey,"test",NULL,&type,(LPBYTE)buffer,&size)==ERROR_SUCCESS)
{//先读取原内容保存到buffer中
printf("%s ",buffer);
char *lstr;
lstr = strstr((char *)buffer,"test3");//检查是否有test3这个值
if(lstr != NULL)//如果有则进入处理
{
printf("Find! ");
if(strchr((char *)buffer,,) == NULL)//检查是否有逗号
{//无逗号说明只有这一个值,可直接删除
RegDeleteValue(hKey,"test");
}
else
{//有逗号不能直接删除,需重新设置值
int result = (int)lstr - (int)buffer;//找到病毒名字符串所在位置
buffer[result - 1] = ;//从这里将后面截断,因为病毒通常都是将自己加在最后面
printf("New Buffer:%s ",buffer);
sprintf((char *)buffer,"%s",buffer);
RegSetValueEx(hKey,"test",0,REG_SZ,buffer,sizeof(buffer));
}
}
}
}
else
{
printf("Cant Open The Key! ");
}
RegCloseKey(hKey);
}
代码其实不长,函数也不多,很容易看懂,太多的注释反而会影响整体思路。下面的代码都侧重于搜索,而对搜索到符合条件的值如何处理没有给出实际代码,所以上面这段代码请务必领会。
我们运行下看看,果然被修改成了test1、test2,我们再把这两个值改成test3,重新运行程序,可以看到由于只有一个值,这个键值被直接删除了,hoho~~命中目标!
当然,这段代码并不完善,因为假如里面刚好有个值为abctest3de的话,不是被直接KILL就是该被改成abc了,完全不符合我们原来的设想了,这个问题就留给大家发挥自己的聪明才智来解决了。
下面我们来演示枚举子项和枚举键值,代码如下。

#include <windows.h>
#include <stdio.h>
#define SUBKEYS 1//决定是枚举子项还是键值

void main()
{
HKEY hKey = NULL,h = NULL;
char str[MAX_PATH];
DWORD num = sizeof(str),index = 0,rc;

#if SUBKEYS
rc=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE",0,KEY_ALL_ACCESS,&hKey);
#else
rc=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\Microsoft\Windows\CurrentVersion",0,KEY_ALL_ACCESS,&hKey);
#endif

if(rc == ERROR_SUCCESS)
{
#if SUBKEYS
while( RegEnumKeyEx(hKey,index,str,&num,NULL,NULL,NULL,NULL)==0 )
#else
while( RegEnumValue(hKey,index,str,&num,NULL,NULL,NULL,NULL)==0 )
#endif
{
printf("%s ",str);
index++;
num = MAX_PATH;
}
printf(" NumberOfindex = %d ",index);
}
else
{
printf("Cant Open The Key! ");
}
RegCloseKey(h);
RegCloseKey(hKey);
}

在上面这段代码中,我使用了条件编译指令,分别演示用来枚举子项还是键值,默认是枚举HKEY_LOCAL_MACHINESOFTWARE下所有的子项,如果改为“#define SUBKEYS 0”就是枚举HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersion下所有的键值,测试结果如图1和图2所示。

图1
[img]/kf/201011/20101125140035126.jpg[/img
图2
嘿嘿,非常完美地实现了我们的功能哦。当然,这只是演示,要想应用到实际中其实也不难,如果你还记得那段经典的遍历文件的代码的话,这个就应该不在话下了。呵呵,就是利用递归来实现的。我写了一段演示代码,献丑了。

#include <windows.h>
#include <stdio.h>

#define MAINKEY HKEY_LOCAL_MACHINE//主键
int ResultCount = 0;//全局变量用来记录符合条件的值数
//下面这段代码是处理键值的,第一个参数是使用API从注册表中读出来的值;第二个参数是你要查找的值,比如病毒名,由于目的主要是演示,写的很简单
bool StealReg(char KeyValue[MAX_PATH],char Virus[MAX_PATH])
{//这里面要实现的具体功能请大家根据实际情况自行扩充
if(strcmp(KeyValue,Virus) == 0)
{
return true;
}
return false;
}
//这个函数是关键,唯一的参数是SubKey,比如“software\Microsoft”
void EnumReg(char SubKey[MAX_PATH])
{
char temp[MAX_PATH];
HKEY hKey = NULL;
char str[MAX_PATH];//存放读取出来的值
DWORD num = sizeof(str),index = 0,rc;
rc = ::RegOpenKeyEx(MAINKEY,SubKey,0,KEY_ALL_ACCESS,&hKey);
if(rc == ERROR_SUCCESS)
{
while( RegEnumValue(hKey,index,str,&num,NULL,NULL,NULL,NULL)==0 )
{//首先遍历值,进行处理
printf(" %s ",str);
if(StealReg(str,"F:\BCB\Program Files\Common Files\Borland Shared\BDE\"))
{//上面第二个参数就是你要查找的值,注意目录之间要用两个“\”
ResultCount++;//记录数加一
}
index++;
num = MAX_PATH;
}
index = 0;
while( RegEnumKeyEx(hKey,index,str,&num,NULL,NULL,NULL,NULL)==0 )
{//遍历子项后进行递归
printf("%s ",str);
strcpy(temp,SubKey);//这样做是必须的,不然通常情况下都会出现异常
strcat(temp,"\");
strcat(temp,str);
EnumReg(temp);//递归

index++;
num = MAX_PATH;
}
}
else
{
printf("Cant Open The Key! ");
}

RegCloseKey(hKey);
}

void main()
{
EnumReg("software");
printf(" 符合条件的值共有:%d 条! ",ResultCount);
}

好了,上面的代码都是可以直接保存为文本文件编译运行的,但由于注册表实在太庞大了,所以要在里面找到全部的东西耗时实在有点长,如果要更加完善的话,就需要大家的一起努力了,欢迎大家与我一起讨论、进步
补充:综合编程 , 安全编程 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,