我在对一些PE文件加壳的时候,需要插入一个段。最初我把这个段放到文件的最后,经常莫名奇妙地被杀毒软件(尤其是那个小红伞,还有金山毒霸2009年10月份的版本)误报为病毒。偶然尝试的时侯,发现把自己的段插入到资源段的前面,或者和资源段前面的段合并,然后把资源段向后移动一点距离,非常有效果。 说到免杀的话题,真是个龙争虎斗的战场,我的意见是,要对自己的壳的逻辑百分百地清楚,百分百地可控,尽量不引用其它号称很成熟的代码,做到这点,即使被杀的时候也心中有数。防破解或者隐藏原程序的理想状态是:原程序作为外壳程序的一部分,而不是外壳程序作为原程序的一部分,虚拟机即是如此。 书归正传,怎样把资源段移位呢。我把程序中的一个函数贴出来,m_FileBuf是PE文件读到内存中的位置,GetResRpos()是资源段到PE头的偏移(RawOffset)。这个函数没有考虑名称表示的资源,也就是注释掉的判断语句,因为我处理的程序没有,所以就偷工减料了。如果您的原程序有,您还需要把那个名称字符串也移位一下。
void CPeManager::ModifyResourceOffset(int move) { IMAGE_RESOURCE_DIRECTORY *dir1, *dir2, *dir3; //三级目录 IMAGE_RESOURCE_DIRECTORY_ENTRY *entry1, *entry2, *entry3; //每个目录项 IMAGE_RESOURCE_DATA_ENTRY *data4; //字符串资源块
int entry_num1, entry_num2, entry_num3; //每级目录有多少项 BYTE* res_buf = m_FileBuf + GetResRpos(); //指向资源段
int i, k, n; //循环用 DWORD offset; //在资源段中的偏移
//根目录 dir1 = (IMAGE_RESOURCE_DIRECTORY*)res_buf; entry_num1 = dir1->NumberOfIdEntries + dir1->NumberOfNamedEntries; entry1 = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dir1 + 1);
//循环资源的类型 for(i = 0; i < entry_num1; i++) { //if(((entry1[i].Name) & 0x80000000)) // == 0 //{ // break; //} offset = (entry1[i].OffsetToData) & 0x7FFFFFFF; dir2 = (IMAGE_RESOURCE_DIRECTORY*)(res_buf + offset); entry_num2 = dir2->NumberOfIdEntries + dir2->NumberOfNamedEntries; entry2 = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dir2 + 1);
//循环资源的ID for(k = 0; k < entry_num2; k++) { //if(((entry1[i].Name) & 0x80000000)) //{ // break; //} offset = (entry2[k].OffsetToData) & 0x7FFFFFFF; dir3 = (IMAGE_RESOURCE_DIRECTORY*)(res_buf + offset); entry_num3 = dir3->NumberOfIdEntries + dir3->NumberOfNamedEntries; entry3 = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dir3 + 1);
//循环语言代码页 for(n = 0; n < entry_num3; n++) { //if(((entry1[i].Name) & 0x80000000)) //{ // break; //} //找到数据区 offset = (entry3[n].OffsetToData) & 0x7FFFFFFF; data4 = (IMAGE_RESOURCE_DATA_ENTRY*)(res_buf + offset); data4->OffsetToData += move; } } } } |