注:本文已发表在<黑客防线>2008年第1期,转载请注明出处.
在第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_MACHINE\SOFTWARE\grayfox,选中它后在右边的空白区域点右键,“新建”——“字符串值”——“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]="\0";
DWORD type = REG_SZ;
DWORD size = sizeof(buffer);
if(RegQueryValueEx(hKey,"test",NULL,&type,(LPBYTE)buffer,&size)==ERROR_SUCCESS)
{ //先读取原内容保存到buffer中
printf("%s\n",buffer);
char *lstr;
lstr = strstr((char *)buffer,"test3"); //检查是否有test3这个值
if(lstr != NULL) //如果有则进入处理
{
printf("Find!\n");
if(strchr((char *)buffer,',') == NULL) //检查是否有逗号
{ //无逗号说明只有这一个值,可直接删除
RegDeleteValue(hKey,"test");
}
else
{ //有逗号不能直接删除,需重新设置值
int result = (int)lstr - (int)buffer; //找到病毒名字符串所在位置
buffer[result - 1] = '\0'; //从这里将后面截断,因为病毒通常都是将自己加入后在最后面
printf("New Buffer:%s\n",buffer);
sprintf((char *)buffer,"%s",buffer);
RegSetValueEx(hKey,"test",0,REG_SZ,buffer,sizeof(buffer));
}
}
}
}
else
{
printf("Can't Open The Key!\n");
}
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;
浙公网安备 33010602011771号