标 题: 【原创】(高手勿进)为pe文件添加CRC32自效验的小程序(vc)
作 者: sdzbyy
时 间: 2007-05-23,00:49
链 接: http://bbs.pediy.com/showthread.php?t=45074
学习CRC32的一个习作,老实说,对CRC的原理还是不太明白,我是菜鸟,请大家不吝赐教。
关于CRC32知识,这里有很好的解释http://www.pediy.com/tutorial/chap6/Chap6-2-4.htm
步骤:
pe文件通过比较自己的CRC32效验值,来判断自己是否被改写。
(1)为原pe文件添加一个新的节,将自效验部分(Loader)写入该新节。
(2)将原pe文件的ep改为新节的开始。
(3)计算原pe文件部分(在程序里计算的是从pe文件的开头到+0xe5)的CRC32效验值,并将该效验值保存在Loader中。
(4)修改后的pe文件开始运行时,先计算相应部分的(在程序里计算的是从pe文件的开头到+0xe5 )CRC32效验值。与原pe文件的CRC32效验值比较,若相等则跳转到原pe的ep,否则直接调用ExitProcess退出。
测试:
(1)打开程序CRC32为记事本添加自效验。
(3)修改从记事本开头到0xe5范围内的任意一个字节(不要修改关键数据,如“MZ“),再运行,记事本直接ExitProcess,退出。
说明:
(1)源码在vc7+xp_xp2下编译通过。
(2)卡巴斯基会将已添加自效验的记事本误报为病毒。
(3)因为图简单所以将自效验的范围定为"MZ"到0XE5。(添加了新的节以后,记事本的头部会改变,但0xe6之前的不会变)
(4)在调用ExitProcess时使用了硬编码:mov eax,07c81caa2h(Kernel32.ExitProcess),call eax
源码:
CAddCRC32::CAddCRC32()
{
}
CAddCRC32::~CAddCRC32()
{
}
CAddCRC32::InitCRC32()
{
init_crc32table();
}
CAddCRC32::ArrayCRC32()
{
DWORD tmp1=0xe5;//baseofdata-baseofcode
DWORD tmp2=(DWORD)(pImageBase);//
__asm
{
mov ebx,tmp2
mov ecx,tmp1
}
OriginalCRC32=arraycrc32();//计算crc32值
}
CAddCRC32::AddSection(DWORD AddrToAdd)
{
//pSection指向新添加的节表
PIMAGE_SECTION_HEADER pSection=(PIMAGE_SECTION_HEADER)AddrToAdd;
//
//添加节表名
BYTE Name[8]=".crc32";
PBYTE ptmp;
ptmp=pSection->Name;
for(int i=0;i<8;i++)
{
*(ptmp)=Name[i];
ptmp++;
}
//
//将VirtualSize(内存块大小)的大小设置为LenOfNewSection
DWORD LenOfNewSection=0x789;
pSection->Misc.VirtualSize=LenOfNewSection;
//
//计算相关的数据:
//(1)新节的virtual offset
//(2)新节的对齐文件大小
//(1)
DWORD SectionAlignment,FileAlignment;
SectionAlignment=pImageOptionalHeader->SectionAlignment;
FileAlignment=pImageOptionalHeader->FileAlignment;
PIMAGE_SECTION_HEADER ForwardSection=(PIMAGE_SECTION_HEADER)(AddrToAdd-0x28);
DWORD ForwardVA,ForwardVS;
ForwardVA=ForwardSection->VirtualAddress;
ForwardVS=ForwardSection->Misc.VirtualSize;
UINT residue,quotient,tmp1,sec_NewVA;
residue=(UINT)ForwardVS%SectionAlignment;
quotient=(UINT)ForwardVS/SectionAlignment;
if(residue!=0x0)quotient++;
tmp1=(UINT)quotient*SectionAlignment;
sec_NewVA=tmp1+ForwardVA;
//(1)
//(2)
UINT tmp2,tmp3,tmp4,sec_NewSR;
tmp2=(UINT)LenOfNewSection%FileAlignment;
tmp3=(UINT)LenOfNewSection/FileAlignment;
if(tmp2!=0x0)tmp3++;
tmp4=(UINT)tmp3*FileAlignment;
sec_NewSR=tmp4;
//(2)
//内存中块的rva值
pSection->VirtualAddress=sec_NewVA;
NewOEP=sec_NewVA+pImageOptionalHeader->ImageBase;;
cNewOEP=sec_NewVA;
//
//文件中的块大小
pSection->SizeOfRawData=sec_NewSR;
//
//文件中的块偏移
pSection->PointerToRawData=ForwardSection->PointerToRawData+ForwardSection->SizeOfRawData;
InjectAddr=pSection->PointerToRawData;//文件偏移量
//
//obj文件使用
pSection->NumberOfLinenumbers=0x0;
pSection->NumberOfRelocations=0x0;
pSection->PointerToLinenumbers=0x0;
pSection->PointerToRelocations=0x0;
//
//修改该节的属性(可读写)
pSection->Characteristics=0x0C00000E0;
//
//
//修改节的数目
UINT NewNum=NumberOfSection+1;
CopyMemory(&pImageFileHeader->NumberOfSections,&NewNum,2);
//
//更新imagesize
UINT tmp3_1,tmp3_2,tmp3_3,tmp3_4;
tmp3_1=LenOfNewSection+pImageOptionalHeader->SizeOfImage;
tmp3_2=tmp3_1/SectionAlignment;
tmp3_3=tmp3_1%SectionAlignment;
if(tmp3_3!=0)tmp3_2++;
tmp3_4=(UINT)tmp3_2*SectionAlignment;
pImageOptionalHeader->SizeOfImage=tmp3_4;
//将bound import设置成零
pImageOptionalHeader->DataDirectory[11].VirtualAddress=0x0;
pImageOptionalHeader->DataDirectory[11].Size=0x0;
//修改文件的大小
::SetFilePointer(hFile,pSection->PointerToRawData+pSection->SizeOfRawData,0,FILE_BEGIN);
::SetEndOfFile(hFile);
}
void CAddCRC32::InjectLoader()//InjectAddr注入的位置
{
//保存老的ep
OldOEP=pImageOptionalHeader->AddressOfEntryPoint+pImageOptionalHeader->ImageBase;
//
BYTE OpNOPCode=0x90;
DWORD Pointer=InjectAddr;
PBYTE ptmp=(PBYTE)Pointer;//指向待Inject的位置
DWORD FuncRVA=GetFunctionSize(Loader);//Loader的大小
DWORD dwRVA=GetFunctionRVA(Loader);//Loader的RVA
PBYTE pFuncBody=(PBYTE)dwRVA;
while(1)
{
if(NumOfNOP(pFuncBody)==3)
{
pFuncBody=pFuncBody+3;
break;
}
else
{
pFuncBody++;
}
}
//定位到1024个nop上
//将loader的Opcode写入PE文件的.crc32节中
for(;ptmp<(PBYTE)(Pointer+FuncRVA);)
{
if(*pFuncBody==OpNOPCode && NumOfNOP(pFuncBody)>256)//若为NOP,则开始Inject
{
PDWORD CRC=CRC32Table;
for(int i=0;i<256;i++)//将crc32table写入.crc节
{
DWORD num=4;
::SetFilePointer(hFile,(LONG)ptmp,0,FILE_BEGIN);
::WriteFile(hFile,CRC,num,&num,NULL);
ptmp=ptmp+4;
CRC++;
}
pFuncBody=pFuncBody+1024;
}
if(*pFuncBody==OpNOPCode && NumOfNOP(pFuncBody)==18 )
{
//
BYTE OpMovEbx=0xBB;
DWORD tmp=(DWORD)(pImageOptionalHeader->ImageBase);//效验计算开始位置:imagebase
BYTE MovEbx[5];
CopyMemory(MovEbx,&OpMovEbx,1);
CopyMemory(&MovEbx[1],&tmp,4);//mov eax,ep
DWORD num=5;
::SetFilePointer(hFile,(LONG)ptmp,0,FILE_BEGIN);
::WriteFile(hFile,MovEbx,num,&num,NULL);
ptmp=ptmp+5;
pFuncBody=pFuncBody+5;
//
BYTE OpMovEcx=0xB9;
tmp=0xe5;//效验计算的结束位置:新的入口点的前一个字节
BYTE MovEcx[5];
CopyMemory(MovEcx,&OpMovEcx,1);
CopyMemory(&MovEcx[1],&tmp,4);//mov eax,ep
num=5;
::SetFilePointer(hFile,(LONG)ptmp,0,FILE_BEGIN);
::WriteFile(hFile,MovEcx,num,&num,NULL);
ptmp=ptmp+5;
pFuncBody=pFuncBody+5;
//
BYTE OpMovEdi=0xBF;
tmp=OriginalCRC32;//效验计算的结束位置:新的入口点的前一个字节
BYTE MovEdi[5];
CopyMemory(MovEdi,&OpMovEdi,1);
CopyMemory(&MovEdi[1],&tmp,4);//mov eax,ep
num=5;
::SetFilePointer(hFile,(LONG)ptmp,0,FILE_BEGIN);
::WriteFile(hFile,MovEdi,num,&num,NULL);
ptmp=ptmp+5;
pFuncBody=pFuncBody+5;
}
if(*pFuncBody==OpNOPCode && NumOfNOP(pFuncBody)==6 )
{
BYTE OpMovEsi=0xBE;
DWORD tmp=NewOEP+5;//jmp xxxx
BYTE MovEsi[5];
CopyMemory(MovEsi,&OpMovEsi,1);
CopyMemory(&MovEsi[1],&tmp,4);//mov eax,ep
DWORD num=5;
::SetFilePointer(hFile,(LONG)ptmp,0,FILE_BEGIN);
::WriteFile(hFile,MovEsi,num,&num,NULL);
ptmp=ptmp+5;
pFuncBody=pFuncBody+5;
}
if(*pFuncBody==OpNOPCode && NumOfNOP(pFuncBody)==5 )
{
BYTE OpMovEax=0xB8;
BYTE OEP_Jmp[7];
CopyMemory(OEP_Jmp,&OpMovEax,1);
CopyMemory(&OEP_Jmp[1],&OldOEP,4);//mov eax,ep
OEP_Jmp[5]=0xFF;
OEP_Jmp[6]=0xE0;//jmp eax
DWORD num=7;
::SetFilePointer(hFile,(LONG)ptmp,0,FILE_BEGIN);
::WriteFile(hFile,OEP_Jmp,num,&num,NULL);
goto exit;
}
//处理不是nop的部分,直接拷贝
DWORD num=1;
::SetFilePointer(hFile,(LONG)ptmp,0,FILE_BEGIN);
::WriteFile(hFile,pFuncBody,num,&num,NULL);
pFuncBody++;
ptmp++;
}
//修改程序的oe
exit:
pImageOptionalHeader->AddressOfEntryPoint=cNewOEP;
}
本文转自:http://blog.csdn.net/rainertop/archive/2008/08/09/2789491.aspx