-
-
[原创]C语言改造一个壳程序
-
发表于:
2013-6-27 15:25
20196
-
最近在逆向一个游戏的时候,碰到了加壳的。壳,“知其然,不知其所以然”。先找个简单的来瞧瞧吧,刚好加密解密(第三版)里有一个。
古老的标签:
/*Code by Hying 2001.1
/*Modified by kanxue 2005.3
好好认识了一下(压缩壳)加壳的流程
//读取文件内容
(1)打开源文件读取DOS头,读取PE头到局部变量空间
(2)文件对和节对齐大小等,根据节对齐大小修正映像大小并申请空间装映像
(3)读取文件头(DOS + NT Header)到新内存空间,修正文件头大小
(4)定位节表头,按照文件和节对齐大小修正节的大小,并检查最后一个节大小
(5)读取各节的内容到新的内存空间,修正可选头中的映像大小
(6)提取额外数据
//处理文件内容
(1)保存重定位表
(2)按照一定的文件格式处理输入表(所需空间大小通过MoveImpTable函数跑一遍得到)
(3)提取运行时需要的资源
(4)合并区段(貌似只是把节表头给抹了,抹掉的节的大小加到第一个节里面)
(5)压缩各个节
1、清除IAT目录信息
2、重新计算文件头大小(最后一个节表地址(考虑增加一个节) - 映像基址),用文件对齐大小修正,并修正第一个节的RAW地址
3、写回各种修正后的文件头到源文件中
4、写回各个节数据;重新计算节大小(扣除数据尾部的零数据);判断当前节是否可以压缩,执行压缩,写回压缩数据(紧接着文件头),填充对齐零数据,按文件对齐大小修正节大小,保存压缩前节的信息;对于可压缩资源区段,先获取资源目录大小,写入资源目录,压缩资源,写回压缩后的数据,修正资源节大小并填充零数据,记录压缩前节的信息;对于不可区段,直接保存数据,修正下一个节的起始偏移地址,循环执行
//写回文件内容
(1)拼装外壳段,外壳分两段,对于第二段进行压缩;在外壳段中保存各种信息,同时修正文件头的导入表和TLS表,使其指向外壳段对应位置,修两表的各种参数,
(2)添加一个新节,设置节表相关变量的值,按文件对齐大小修正节大小,按节对齐大小修正虚拟大小,修改文件头
(3)若是DLL,则把重定位表指向外壳段的虚构重定位表
(4)写入外壳段,末尾填充零数据
(5)定位源文件末尾,写入额外数据
//壳运行流程
(1)外壳第一段代码
1、从导入表获取GetModuleHandleA、GetProcAddress、VirtualAlloc函数的函数地址
2、申请一块内存,用于解压外壳第二段代码
3、解压外壳第二段代码,构造jmp xxxx跳到第二段代码执行
(2)外壳第二段代码
1、获取LoadLibraryA、aP_Depack函数地址
2、解压缩各节,解压后写到源文件对应位置
3、回复原文件的输入表
4、若有重定位数据,则修正重定位数据
5、准备返回OEP
代码Copy完一遍,第一个感觉就是各种大小修正,各种计算公式(部分):
//映像大小修正
ImageSize = AlignSize(nImage,nSectionAlign);
//映像大小计算(psececHeader 最后一个节表指针)
ImageSize = psecHeader->VirtualAddress + psecHeader->Misc.VirtualSize;
//定位NTHeader
pNtHeader = ImageBase + DosHeader.e_lfanew;
//定位NtHeader.OptionalHeader数据目录的数据
pTarget = m_pImageBase + pDataDir->VirtualAddress;
//定位节表
pSecHeader = m_pNtHeader + NtHeader.FileHeader.SizeOfOptionalHeader;
//节表数据起始地址
psecHeader[1].PointerToRawData = psecHeader->PointerToRawData + psecHeader->SizeOfRawData;
//额外数据大小计算
nMapOfSDataSize = nFileSize - (psecHeader->PointerToRawData + psecHeader->SizeOfRawData);
//NtHeader大小计算
nNtHeaderSize = sizeof(NtHeader.FileHeader) + sizeof(NtHeader.Signature) + NtHeader.FileHeader.SizeOfOptionalHeader;
//节表相关大小修正
psecHeader->SizeOfRawData = AlignSize(nRawDataSize,nFileAlign);
psecHeader->Misc.VirtualSize= AlignSize(nVirtualSize,nSectionAlign);
//节表大小修正后,检测最后一个节大小的处理
if(nIndex == nSectionNum - 1 && psecHeader->VirtualAddress + psecHeader->SizeOfRawData > m_nImageSize){
psecHeader->SizeOfRawData = m_nImageSize - psecHeader->VirtualAddress;
}
//关于代码长度计算
void FirstShellFunction0(PDWORD StartAddr,PULONG length_code)
{
if(0){
_asm{
ShellStart0:
……
ShellEnd0:
nop
}
}
if(StartAddr && length_code){
_asm{
lea eax,ShellEnd0
lea ebx,ShellStart0
mov ecx,StartAddr
mov [ecx],ebx
sub eax,ebx
mov ecx,length_code
mov [ecx],eax
}
}
}
//关于两段代码使用同一局部变量
void FirstShellFunction1(PDWORD StartAddr,PULONG length_code)
{
函数一.变量1;
函数一.变量2;
……
函数二.变量1;
函数二.变量2;
if(0){
_asm{
jmp secondFunctionCode
}
}
}
void SecondShellFunction(PDWORD StartAddr,PULONG length_code)
{
函数一.变量1;
函数一.变量2;
……
函数二.变量1;
函数二.变量2;
……
if(0){
_asm{
push OEP
retn
}
}
}
//实际的变量定义
volatile PDEFINE_IMPORT_TABLE pImportTable = NULL;
volatile PDEFINE_RELOCBASE_TABLE pRelocTable = NULL;
PDEFINE_SHELLDATA_0 pData0 = NULL;
PDEFINE_SHELLDATA_1 pData1 = NULL;
//临时变量
ULONG Temp1 = NULL;
ULONG Temp2 = NULL;
ULONG Temp3 = NULL;
ULONG Temp4 = NULL;
ULONG Temp5 = NULL;
//循环变量
ULONG i = NULL;
//函数指针
PGETMODULEHANDLEA D_GetModuleHandleA = NULL;
PGETPROCADDRESS D_GetProcAddress = NULL;
PLOADLIBRARYA D_LoadLibraryA = NULL;
PVIRTUALALLOC D_VirtualAlloc = NULL;
PAPDEPACK D_aP_Depack = NULL;
PVIRTUALFREE D_VirtualFree = NULL;
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课