欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

打造属于自己的“查壳”工具

程序员文章站 2024-01-16 22:47:46
文/图 冀云壳是一种保护软件的工具,壳有加密壳和压缩壳,有的壳两种功能都有。壳这东西,我也不怎么了解,说多了怕说错,影响我在大家面前的形象。废话不多说了,只说说今天我的目的。此次想跟大家探讨一...

文/图 冀云
壳是一种保护软件的工具,壳有加密壳和压缩壳,有的壳两种功能都有。壳这东西,我也不怎么了解,说多了怕说错,影响我在大家面前的形象。废话不多说了,只说说今天我的目的。此次想跟大家探讨一些编写“查壳”工具的实现方法,我只把我的方法说给大家,有不足的地方欢迎指正和批评。
PEiD这款工具想必很多人都用过吧?尤其是我们《黑客防线》的朋友,对它就更是了如指掌了。PEiD为什么能查壳呢?这是因为PEiD有一个文本类型的数据库,我的PEiD数据库的名称是“userdb.txt”,复制下来一段给大家看看!
 
[ASPack v2.12]
signature = 60 E8 03 00 00 00 E9 EB 04 5D 45 55 C3 E8 01
ep_only = true
 
第一行是加壳软件的名称版本,第二行是壳的特征码,第三行我就不知道了,但我们不用管它,因为跟我们的文章没有关系嘛。比较关键的是加壳软件的名字和特征码,因为我们查壳是查什么?就是查这个加壳软件的名字嘛!怎么查?靠特征码查呗!事实上我发现,PEiD使用的特征码都是连续的,而且大多都是从程序入口处开始取的特征码。这样的方法也算正常,黑防的有些高手,用OD打开要调试的软件,一看前面几行就知道这个软件加的是什么壳(汗!黑防的高手就是厉害)。大家看看图1,就知道特征码确实是从文件的入口点取出的,并且是连续的。

打造属于自己的“查壳”工具

图1
有了这个方法,我们就知道该怎么办了。怎么办?先找到文件的入口点呗!文件的入口点其实就在PE文件的某个结构里保存的,大家可以自己查阅MSDN看一下,这里就不多说关于PE的内容了。入口点一般在IMAGE_OPTIONAL_HEADER结构中的AddressOfEntryPoint成员中保存,只要把这个成员的值读出来,入口点就找到了。记得曾经有人说过“源代码面前没有秘密”,那么我们就直接看代码吧。
 
IMAGE_DOS_HEADER *pDosHeader=(IMAGE_DOS_HEADER *)lpBase;
pNtHeader=(IMAGE_NT_HEADERS *)((char *)lpBase+pDosHeader->e_lfanew);
dwOEP=pNtHeader->OptionalHeader.AddressOfEntryPoint;
 
其中的dwOEP中保存的就是入口点的值了。很简单吧?不过在这之前我们应该先判断一下打开的文件是不是PE文件,然后再读取入口点。不过我还是省略了一些打开文件、映射文件视图等的操作,不过也很简单,代码也就两三行。
 
       hFile=CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
hMap=CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,0);
lpBase=MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);
 
这其中没有一句错误处理,我们就假设都能保证正常返回吧。前面的处理都一切顺利,但是怎样才能读出入口点处的N个特征码呢?又用什么读呢?ReadFile()?ReadProcessMemory()?说真的,这里当初确实把我给难住了,用ReadFile读的话,文件的句柄用哪个?怎么定位?想不出来。用ReadProcessMemory()读的话,读哪个进程的?也不知道。怎么办呢?结果我发现PEiD上还有一个“文件偏移”按钮。文件偏移是干什么的?用UltraEdit32打开在PEiD里打开的那个文件看看“00001001”这个偏移的内容,如图2所示,没想到在文件偏移处也能找到传说中的特征码。好,就从这里下手了。但是“文件偏移”又怎么找呢?我们继续往下看。

打造属于自己的“查壳”工具

图2
其实我们找到的入口点是一个相对偏移地址(RVA),顾名思义,它是一个“相对”地址,也就是一个“偏移量”。那么RVA怎么才能转换成文件偏移呢?好像微软没有提供这方面的API函数吧?因此只能我们自己解决了。我的方法比较笨,分三步:首先循环扫描每个节表,得到每个节在内存中的RVA(在IMAGE_SECTION_HEADERD的VirtualAddress成员中保存),还有该节的大小(在IMAGE_SECTION_HEADERD的SizeOfRawData中保存);判断我们的入口点的RVA是否在某个节的范围内;最后,在某个节中用我们的RVA减去该节的起始RVA再加上该节在文件中所处的文件偏移即可得到。方法看起来复杂,其实实现起来并不怎么复杂,里面涉及到的还是PE文件头的那些个结构。我们依然先看代码实现。
 
for(i=0;i<pNtHeader->FileHeader.NumberOfSections;i++)
{
if((dwOEP>=(pSecHeader[i].VirtualAddress))&&(dwOEP<(pSecHeader[i].VirtualAddress+pSecHeader[i].SizeOfRawData)))
{
FileOffSet=dwOEP-pSecHeader[i].VirtualAddress+pSecHeader[i].PointerToRawData;
break;
}
}

代码中的FileOffSet就保存了我们的文件偏移地址。这样的话,我们就从得到的文件偏移地址处开始读取我们的特征码吧。我选用的读取特征码的程序,还是我使用的那个例子程序。读取特征码的方法就不多说了,更简单了,直接看代码。
 
hFile=CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
SetFilePointer(hFile,FileOffSet,NULL,FILE_BEGIN);
ReadFile(hFile,buffer,15,(unsigned long *)a,NULL);
 
因为ASPack v2.12的特征码一共15位,所以ReadFile()函数读15位就可以了。读出特征码后,最后一步就是比较了。
 
for(int i=0;i<16;i++)
{
if(buffer[i]!=code[i])
{
lstrcpy(types,"No found");
break;
}
else if(i==15)
{
lstrcpy(types,"Aspack v2.12");
}
}

我给出的代码比较简陋,比较特征码的实现也不是很好,但用strcmp之类的函数比较的话恐怕实现不了,因为特征码有“00”这种字符。
好了,程序的整体就是这样了,测试结果如图3所示。是不是觉得我们的入口点、文件偏移跟PEiD的不一样呢?这是因为PEiD用的是 16进制,而我们的是10进制。值的大小其实没有什么不同,只是表示的方法不一样而已了。

打造属于自己的“查壳”工具

图3
最后再补充一点,作为大家扩展性的知识来了解一下吧,毕竟我们这里也提到了特征码这个概念。我们还是先从“userdb.txt”中复制一段特征码出来看看。
 
[ASPack v1.07b]
signature = 90 75 ?? E9
ep_only = true
 
这个很显然是ASPack1.07的特征码了,但里面的“??”是什么意思呢?其实“??”是通配符。不管特征码是什么字符,只要跟“??”比较的话,都可以忽略不计。特征码中除了会出现“??”以外,还有“%”,是重复的意思,举个例子,比如“%3 45”的意思就是在接下来的3个位置中是“45”。这两个通配符可是杀毒软件中都用得到的哦