VC6写的一个简单的压缩壳,我把我的心得体会分享给大家.
因为本人的水平有限,很多东西都是靠自己的理解来实现的,其中错误难免,拍砖、喷我,我都能Hold住,
但是一定要指出来,我会及时修正,因为我不想让文中的错误传遍互联网,祸害大家.
大致思路:
把被加壳程序的所有区段,放到压缩后放到第一个区段,资源和TLS段我选择不压缩,就直接拷贝到后面的
段中,添加一个shell段来做引导,解压压缩过的所有区段
结构为:
.oldDat
.shell
.rsrc(如果有的话)
.tls(如果有的话)
.info(一些shell用到的必要信息)
实现起来有几点要注意的地方:
1.为什么那么多壳都是选择把被加壳程序的区段放到第一个区段?,因为不放入第一个区段,变相的相当
于更改了ImageBase,导入被加壳程序用到的全局数据偏移都要修正,并且EXE基本上没有重定位表,这时
解决方法,需要特征码扫描被加壳程序需要重定位的数据偏移,在shell引导程序中修正偏移,
首先构造重定位表已经很累很难了,再者就是增加shell代码, 不过写loader应该是必须要做的(当然不
排除你能申请到你想要的ImageBase),第二种解决方法是修正EXE的ImageBase, 我就直接放到第一区段
了,何必麻烦呢
2.资源段的处理:资源段我选择全部不压缩,当然也可以压缩运行后才需要到的资源数据,但是感觉太麻
烦了(自己太菜-_-), 把资源段放到后面需要注意,要修正数据目录的RVA,并且资源的结构是三层目录结
构,要修正最后一层数据RVA
3.TLS的处理:TLS需要拷贝到后面区段,同时修正数据目录的RVA, 当TLS有回调函数时, 则shell解压数
据后,首先调用TLS回调函数,然后再跳到OEP,TLS还有个要处理的是.TLS区段有可能包含全局数据,所以
shell需要把TLS区段再拷贝到原始地方,以便被加壳程序能正确引用
4.被加壳程序的导入表处理:我的做法把他的导入表清空,区段也清除,当运行加壳后的程序使用shell
来填写原始IAT即可, 而应用程序加载后,即使没有导入表也是默认加载ntdll和kernel32(有了这两个,
飞机都能造了),当然最后一个面临问题就是shell用的Api的怎么填写, 就是我们写的程序,事先把
dll的IAT写入shell要用的api即可,这样就事先了,加壳后的程序没有导入表也可正常运行
5.shell用什么来写:可以考虑用exe,但是首选是dll,因为dll默认会有重定位表,上面说到生成后的程序
的基址与被加壳程序的基址一致是最方便的做法, 而dll又有重定位表, 只需要修正dll的重定位地址即
可
具体实现:
注:
文中的m_TargetPeTag,m_ShellPeTag,m_PressPeTag
分别是被加壳程序,dll写的shell,要生成的加壳程序
我用结构体成员管理了内存映射文件的句柄和地址,方便最后释放资源
首先用个数组保存被加壳程序区段信息,以及区段内容.以便后面查找和压缩用
判断被加壳程序是否有资源段和tls段,有的话,把区段信息设置为不压缩
//保存被压缩PE的区段信息
StorePressSecInfo();
//被压缩程序的TLS和资源区段要保持不变 首先判断是否有TLS表和资源表
DWORD dwResRVA = m_TargetPeTag.m_lpNtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
DWORD dwTLSRVA = m_TargetPeTag.m_lpNtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress;
MySecInfo* lpResSecInfo = NULL;
if (dwResRVA != 0)
{
lpResSecInfo = GetSecInfoByRVA(dwResRVA, m_TargetPeTag.m_lpNtHeader-
>OptionalHeader.SectionAlignment);
//资源段不压缩
lpResSecInfo->m_isNeedPress = false;
}
MySecInfo* lpTLSSecInfo = NULL;
if (dwTLSRVA != 0)
{
lpTLSSecInfo = GetSecInfoByRVA(dwTLSRVA, m_TargetPeTag.m_lpNtHeader-
>OptionalHeader.SectionAlignment);
//TLS段不压缩
lpTLSSecInfo->m_isNeedPress = false;
}
//获得被压缩PE的所有区段文件大小和(去除不能压缩的资源,TLS)
DWORD dwPressSize = GetPressSize();
//申请要压缩数据的空间
char* lpSrcData = new char[dwPressSize];
if (lpSrcData == NULL)
{
AfxMessageBox("new Error");
return false;
}
RtlZeroMemory(lpSrcData, dwPressSize);
//拷贝要压缩的数据
CopyPressData(lpSrcData);
//aplib压缩数据
DWORD dwAfterPressSize = MyCompress(lpSrcData, &m_lpPressData, dwPressSize);
if (dwAfterPressSize == APLIB_ERROR)
{
if (lpSrcData != NULL)
{
delete[] lpSrcData;
}
return false;
}
DWORD dwFileAlign = m_TargetPeTag.m_lpNtHeader->OptionalHeader.FileAlignment;
DWORD dwResFileSize = 0;
if (lpResSecInfo != NULL)
{
dwResFileSize = Align(dwFileAlign, lpResSecInfo-
>m_SecHeader.SizeOfRawData);
}
DWORD dwTlsFileSize = 0;
if (lpTLSSecInfo != NULL)
{
dwTlsFileSize = Align(dwFileAlign, lpTLSSecInfo-
>m_SecHeader.SizeOfRawData);
}
DWORD dwFileSize = m_TargetPeTag.m_lpNtHeader->OptionalHeader.SizeOfHeaders +
Align(dwFileAlign, dwAfterPressSize) +
Align(dwFileAlign, m_ShellPeTag.m_lpNtHeader->OptionalHeader.SizeOfCode) +
dwResFileSize + dwTlsFileSize +
dwFileAlign;
//拷贝PE头
memcpy(m_PressPeTag.m_FileMapTag.m_lpFileData,
m_TargetPeTag.m_FileMapTag.m_lpFileData,
m_TargetPeTag.m_lpNtHeader->OptionalHeader.SizeOfHeaders);
m_PressPeTag.m_lpDosHeader =(IMAGE_DOS_HEADER*)
m_PressPeTag.m_FileMapTag.m_lpFileData;
m_PressPeTag.m_lpNtHeader = (IMAGE_NT_HEADERS*)
(m_PressPeTag.m_FileMapTag.m_lpFileData + m_PressPeTag.m_lpDosHeader->e_lfanew);
//生成程序的映像大小 = pe头 + 被压缩PE镜像大小 + .shell + rsrc + tls + info
DWORD dwSecAlign = m_PressPeTag.m_lpNtHeader->OptionalHeader.SectionAlignment;
DWORD dwResSize = 0;
if (lpResSecInfo != NULL)
{
dwResSize = Align(dwSecAlign, lpResSecInfo->m_SecHeader.SizeOfRawData);
}
DWORD dwTlsSize = 0;
if (lpTLSSecInfo != NULL)
{
dwTlsSize = Align(dwSecAlign, lpTLSSecInfo->m_SecHeader.SizeOfRawData);
}
DWORD dwImageSize = Align(dwSecAlign, m_PressPeTag.m_lpNtHeader-
>OptionalHeader.SizeOfHeaders) +
dwTargetImageSize + Align(dwSecAlign, m_ShellPeTag.m_lpNtHeader-
>OptionalHeader.SizeOfCode) +
dwResSize + dwTlsSize + Align(dwSecAlign, dwFileAlign);
m_PressPeTag.m_lpNtHeader->OptionalHeader.SizeOfImage = dwImageSize;
//添加区段表
m_PressPeTag.m_lpNtHeader->FileHeader.NumberOfSections = 0;
AddSec(".OldDat", dwAfterPressSize, m_PressPeTag.m_FileMapTag.m_lpFileData);
AddSec(".Shell", m_ShellPeTag.m_lpNtHeader->OptionalHeader.SizeOfCode,
m_PressPeTag.m_FileMapTag.m_lpFileData);
if (lpResSecInfo != NULL)
{
AddSec(".rsrc", lpResSecInfo->m_SecHeader.SizeOfRawData,
m_PressPeTag.m_FileMapTag.m_lpFileData);
}
if (lpTLSSecInfo != NULL)
{
AddSec(".Tls", lpTLSSecInfo->m_SecHeader.SizeOfRawData,
m_PressPeTag.m_FileMapTag.m_lpFileData);
}
AddSec(".Info", dwFileAlign, m_PressPeTag.m_FileMapTag.m_lpFileData);
//拷贝.OldDat区段
DWORD dwCurPos = m_PressPeTag.m_lpNtHeader->OptionalHeader.SizeOfHeaders;
memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos),
m_lpPressData,
dwAfterPressSize);
//拷贝.Shell区段
dwCurPos += Align(dwFileAlign, dwAfterPressSize);
memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos),
(LPVOID)((DWORD)hRes + RVA2FA((char*)hRes,
m_ShellPeTag.m_lpNtHeader->OptionalHeader.BaseOfCode)),
m_ShellPeTag.m_lpNtHeader->OptionalHeader.SizeOfCode);
dwCurPos += Align(dwFileAlign, m_ShellPeTag.m_lpNtHeader-
>OptionalHeader.SizeOfCode);
//拷贝.rsrc区段
if (lpResSecInfo != NULL)
{
memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos),
(m_TargetPeTag.m_FileMapTag.m_lpFileData + lpResSecInfo-
>m_SecHeader.PointerToRawData),
lpResSecInfo->m_SecHeader.SizeOfRawData);
dwCurPos += Align(dwFileAlign, lpResSecInfo->m_SecHeader.SizeOfRawData);
}
//拷贝.tls区段
if (lpTLSSecInfo != NULL)
{
memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos),
(m_TargetPeTag.m_FileMapTag.m_lpFileData + lpTLSSecInfo-
>m_SecHeader.PointerToRawData),
lpTLSSecInfo->m_SecHeader.SizeOfRawData);
}
//设置数据目录
//记录被压缩程序的导入表RVA
DWORD dwOldImpAddr = m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
//把所有数据目录项都清空
ClearDataDir(m_PressPeTag.m_FileMapTag.m_lpFileData);
//资源表
DWORD dwRVA = 0;
DWORD dwSize = 0;
GetResRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwRVA, dwSize);
m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = dwRVA;
m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = dwSize;
//TLS
//某些时候 TLS和别的区段连在一起 则要加上一个相差偏移
DWORD dwPressTLSRVA = 0;
if (lpTLSSecInfo != NULL)
{
DWORD dwTLSSize = 0;
GetTLSRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwPressTLSRVA,
dwTLSSize);
dwPressTLSRVA += dwTLSRVA - lpTLSSecInfo->m_SecHeader.VirtualAddress;
m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress = dwPressTLSRVA;
m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_TLS].Size = dwTLSSize;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)