学完科锐第三阶段壳的课程内容之后,我发现,实现压缩壳,必须对PE格式十分熟悉,其次,解压缩代码需要编写shellcode,也是十分麻烦的环节。有了两者的结合,我们才能写好一个真正的压缩壳。
首先上一张图,让大家直观地感受到一个壳程序是如何运行起来的。
左边是壳PE,壳程序有一个PE头,节表1是空节,用来存放解压缩后的原程序PE,节表2此时存储的是压缩后的原PE。节表3则是壳代码节,壳PE运行起来后,首先就是进入入口点,运行节表3的代码,解压缩节表2,然后将结果覆盖PE头+节表1的位置,修复完导入表、重定位表,jmp到原程序的入口点处即可。
原理不变,我这里加了点“料”,新增了节4和节5,存储了相关的信息,让压缩壳的脱壳过程变难,往后看就知道了。
1.为了生成一个新的壳PE,我们一步步来,首先是PE头,俗话说靠山吃山,靠水吃水,这个PE头我就直接拿原程序的PE头来代替了,只不过需要改一些数据:
2.新PE头里一些需要修改的数据,比如SizeOfImage,我们目前还没有,需要等我们构造出节表1、2、3、4、5之后,才知道。接下来,先构造节表1的表头,节表1是个空节,它的大小只要够存放原程序的节表即可,多给一点也没关系,我这里直接给了SizeOfImage。(由于是空节,所以这里并不需要考虑节表1的数据内容)
3.接着是节表2,这个节要存放原PE的压缩数据,先设计表头(使用了PointerToRelocations和PointerToLinenumbers这两个没啥用的字段,存放压缩大小信息,留着给后面shellcode用)
然后将PE压缩,压缩前我把节表、导入表、重定位表保存并清空,到时候由shellcode进行还原。
4.接着是节表3,该节表存放的是解压缩PE、还原导入表、重定位表,运行原程序的至关重要的shellcode,先设计表头
至于节表3的数据内容,也就是shellcode,等我把节表、PE设计完,我再说
5.接着是节表4,节表4是我额外增加的一个,用来存储步骤3中保存的原节表表头、原导入表、原重定位表,首先设计节表4表头
然后把刚才保存的原节表表头、原导入表、原重定位表信息,按顺序写入到缓冲区里。
6.还剩最后一个节表5,是一个空节,壳代码还原原PE时,要还原导入表,于是这个节的作用就体现出来了,shellcode在这里玩了一波偷梁换柱,直接毙掉了x64dbg的脱壳后导入表自动修复功能,等会介绍shellcode的时候你们就知道了。设计节表5的表头
至此,新的壳PE结构我们就设计好了,然后将新PE头、节表234写入到新文件(节表1、5是空节,不用写入),就大功告成,加壳完毕!
由于壳PE可能是随机基址,所以执行shellcode时,一定要确保它的代码跟地址无关。我的shellcode是在VS上编译的,为了保证VS不会生成多余的代码,要修改以下几个设置:
然后就是最麻烦的shellcode代码编写了。首先要清楚shellcode代码的功能:
1.要解压缩,那么必定要调用库的API,如果直接调用的话,call的就是死地址,违背了地址无关原则。那么使用LoadLibrary然后GetProcAddress?显然也不行,LoadLibrary和GetProcAddress也是死地址,所以我们要自己实现LoadLibrary和GetProcAddress的功能。
首先通过_PEB来拿到kernel32的模块基址
然后通过kernel32的导出函数表,拿到GetProcAddress函数地址,代码如下:
有了GetProcAddress函数地址和kernel32的基址,同理就能拿到LoadLibrary函数地址了(注意定义字符串变量时,用单个字符一个一个排列,在汇编里面看就是db出来的,否则字符串会有一个常量区地址,影响shellcode的通用性)
2.有了LoadLibrary和GetProcAddress,就可以使用任何库函数了,解压缩便是小菜一碟。
3.解压缩完毕,得到了原PE,接下来就是将原PE覆盖到现在的PE头+节表1的地方,同时还原节表头、导入表和重定位表,相当于LoadPE的功能了
这里重点要介绍的,就是还原导入表的过程,也是整篇文章的核心主题,加点“料”。我在遍历还原导入表时,并没有直接将API的地址填入到IAT里,而是将节表5的地址,从起始位置开始,每隔16个字节,将地址填入到IAT里,然后在对应的节表5地址上填入push 真实函数地址 + retn的汇编指令。这样一来,原PE程序运行调用API时,就会跳到节表5里面,再从节表5里面跳到真实API地址,直接干掉了x64dbg的脱壳导入表自动修复功能。嘿嘿~~
最后来看看效果,对扫雷进行加壳,加壳后的程序可以正常运行
先用x64找到真实入口点,进行一波dump操作
dump后无法直接运行
然后去x64里使用自动搜索修复导入表的功能,可以看到,IAT这里存放的压根就不是真实API的地址,所以x64也无法识别出来。
搞定,收工!
NumberOfSections
-
-
节表数量,要改为
5
AddressOfEntryPoint
-
-
入口点,要改为节表
3
里的代码入口
SizeOfImage
-
-
PE在内存中的大小,要改为新的PE的内存大小
pSecHdr
-
-
节表头,要拓展为
5
个节表
void CPacker::GetNewPeHdr()
{
/
/
拷贝原PE的PE头
m_dwNewPeHdrSize
=
m_pNtHdr
-
>OptionalHeader.SizeOfHeaders;
m_pNewPeHdr
=
new BYTE[m_dwNewPeHdrSize];
CopyMemory(m_pNewPeHdr, m_pDosHdr, m_dwNewPeHdrSize);
/
/
修改
auto pDosHdr
=
(PIMAGE_DOS_HEADER)m_pNewPeHdr;
auto pNtHdr
=
(PIMAGE_NT_HEADERS)(m_pNewPeHdr
+
pDosHdr
-
>e_lfanew);
auto pSecHdr
=
(PIMAGE_SECTION_HEADER)
((LPBYTE)&pNtHdr
-
>OptionalHeader
+
pNtHdr
-
>FileHeader.SizeOfOptionalHeader);
pNtHdr
-
>FileHeader.NumberOfSections
=
5
;
pNtHdr
-
>OptionalHeader.AddressOfEntryPoint
=
m_newSecHdr[
2
].VirtualAddress;
pNtHdr
-
>OptionalHeader.SizeOfImage
=
m_newSecHdr[
4
].VirtualAddress
+
m_newSecHdr[
4
].Misc.VirtualSize;
/
/
清空DataDirectory目录
ZeroMemory(pNtHdr
-
>OptionalHeader.DataDirectory, sizeof(pNtHdr
-
>OptionalHeader.DataDirectory));
/
/
修改新的节表头
CopyMemory(pSecHdr, m_newSecHdr, sizeof(m_newSecHdr));
}
NumberOfSections
-
-
节表数量,要改为
5
AddressOfEntryPoint
-
-
入口点,要改为节表
3
里的代码入口
SizeOfImage
-
-
PE在内存中的大小,要改为新的PE的内存大小
pSecHdr
-
-
节表头,要拓展为
5
个节表
void CPacker::GetNewPeHdr()
{
/
/
拷贝原PE的PE头
m_dwNewPeHdrSize
=
m_pNtHdr
-
>OptionalHeader.SizeOfHeaders;
m_pNewPeHdr
=
new BYTE[m_dwNewPeHdrSize];
CopyMemory(m_pNewPeHdr, m_pDosHdr, m_dwNewPeHdrSize);
/
/
修改
auto pDosHdr
=
(PIMAGE_DOS_HEADER)m_pNewPeHdr;
auto pNtHdr
=
(PIMAGE_NT_HEADERS)(m_pNewPeHdr
+
pDosHdr
-
>e_lfanew);
auto pSecHdr
=
(PIMAGE_SECTION_HEADER)
((LPBYTE)&pNtHdr
-
>OptionalHeader
+
pNtHdr
-
>FileHeader.SizeOfOptionalHeader);
pNtHdr
-
>FileHeader.NumberOfSections
=
5
;
pNtHdr
-
>OptionalHeader.AddressOfEntryPoint
=
m_newSecHdr[
2
].VirtualAddress;
pNtHdr
-
>OptionalHeader.SizeOfImage
=
m_newSecHdr[
4
].VirtualAddress
+
m_newSecHdr[
4
].Misc.VirtualSize;
/
/
清空DataDirectory目录
ZeroMemory(pNtHdr
-
>OptionalHeader.DataDirectory, sizeof(pNtHdr
-
>OptionalHeader.DataDirectory));
/
/
修改新的节表头
CopyMemory(pSecHdr, m_newSecHdr, sizeof(m_newSecHdr));
}
/
/
空节
strcpy((char
*
)m_newSecHdr[
0
].Name,
".cr42"
);
m_newSecHdr[
0
].Misc.VirtualSize
=
m_pNtHdr
-
>OptionalHeader.SizeOfImage;
m_newSecHdr[
0
].VirtualAddress
=
m_pSecHdr[
0
].VirtualAddress;
m_newSecHdr[
0
].SizeOfRawData
=
0
;
m_newSecHdr[
0
].PointerToRawData
=
0
;
m_newSecHdr[
0
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
/
/
空节
strcpy((char
*
)m_newSecHdr[
0
].Name,
".cr42"
);
m_newSecHdr[
0
].Misc.VirtualSize
=
m_pNtHdr
-
>OptionalHeader.SizeOfImage;
m_newSecHdr[
0
].VirtualAddress
=
m_pSecHdr[
0
].VirtualAddress;
m_newSecHdr[
0
].SizeOfRawData
=
0
;
m_newSecHdr[
0
].PointerToRawData
=
0
;
m_newSecHdr[
0
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
/
/
压缩数据节
strcpy((char
*
)m_newSecHdr[
1
].Name,
".data"
);
m_newSecHdr[
1
].Misc.VirtualSize
=
GetAlign(m_dwComSecSize, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
1
].VirtualAddress
=
m_newSecHdr[
0
].VirtualAddress
+
m_newSecHdr[
0
].Misc.VirtualSize;
m_newSecHdr[
1
].SizeOfRawData
=
m_dwComSecSize;
m_newSecHdr[
1
].PointerToRawData
=
m_pNtHdr
-
>OptionalHeader.SizeOfHeaders;
m_newSecHdr[
1
].Characteristics
=
IMAGE_SCN_MEM_READ;
m_newSecHdr[
1
].PointerToRelocations
=
m_dwComSize;
/
/
压缩后大小
m_newSecHdr[
1
].PointerToLinenumbers
=
m_dwSrcPeSize;
/
/
压缩前大小
/
/
压缩数据节
strcpy((char
*
)m_newSecHdr[
1
].Name,
".data"
);
m_newSecHdr[
1
].Misc.VirtualSize
=
GetAlign(m_dwComSecSize, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
1
].VirtualAddress
=
m_newSecHdr[
0
].VirtualAddress
+
m_newSecHdr[
0
].Misc.VirtualSize;
m_newSecHdr[
1
].SizeOfRawData
=
m_dwComSecSize;
m_newSecHdr[
1
].PointerToRawData
=
m_pNtHdr
-
>OptionalHeader.SizeOfHeaders;
m_newSecHdr[
1
].Characteristics
=
IMAGE_SCN_MEM_READ;
m_newSecHdr[
1
].PointerToRelocations
=
m_dwComSize;
/
/
压缩后大小
m_newSecHdr[
1
].PointerToLinenumbers
=
m_dwSrcPeSize;
/
/
压缩前大小
bool
CPacker::GetCompressData()
{
COMPRESSOR_HANDLE hCompressor
=
NULL;
BOOL
Success
=
CreateCompressor(
COMPRESS_ALGORITHM_XPRESS_HUFF,
NULL,
&hCompressor
);
m_pComData
=
new BYTE[m_dwSrcPeSize
+
0x28
];
LPBYTE m_pSrcPeTmp
=
new BYTE[m_dwSrcPeSize];
CopyMemory(m_pSrcPeTmp, m_pSrcPe, m_dwSrcPeSize);
PIMAGE_DOS_HEADER m_pDosHdrTmp
=
(PIMAGE_DOS_HEADER)m_pSrcPeTmp;
PIMAGE_NT_HEADERS m_pNtHdrTmp
=
(PIMAGE_NT_HEADERS)(m_pSrcPeTmp
+
m_pDosHdrTmp
-
>e_lfanew);
PIMAGE_SECTION_HEADER m_pSecHdrTmp
=
(PIMAGE_SECTION_HEADER)
((LPBYTE)&m_pNtHdrTmp
-
>OptionalHeader
+
m_pNtHdrTmp
-
>FileHeader.SizeOfOptionalHeader);
/
/
1.
在压缩前,把节表保存并清空
nSecNum
=
m_pNtHdrTmp
-
>FileHeader.NumberOfSections;
CopyMemory(m_pSaveSecHdr, m_pSecHdrTmp, nSecNum
*
40
);
ZeroMemory(m_pSecHdrTmp, nSecNum
*
40
);
/
/
2.
把导入表保存并清空
m_pImportAddr
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
1
].VirtualAddress;
nImpSize
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
1
].Size;
ZeroMemory(&(m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
1
]),
8
);
/
/
3.
把重定位表保存并清空
m_pRelocAddr
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
5
].VirtualAddress;
nRelocSize
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
5
].Size;
ZeroMemory(&(m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
5
]),
8
);
Success
=
Compress(
hCompressor,
m_pSrcPeTmp,
m_dwSrcPeSize,
m_pComData,
m_dwSrcPeSize
+
0x28
,
&m_dwComSize
);
return
true;
}
bool
CPacker::GetCompressData()
{
COMPRESSOR_HANDLE hCompressor
=
NULL;
BOOL
Success
=
CreateCompressor(
COMPRESS_ALGORITHM_XPRESS_HUFF,
NULL,
&hCompressor
);
m_pComData
=
new BYTE[m_dwSrcPeSize
+
0x28
];
LPBYTE m_pSrcPeTmp
=
new BYTE[m_dwSrcPeSize];
CopyMemory(m_pSrcPeTmp, m_pSrcPe, m_dwSrcPeSize);
PIMAGE_DOS_HEADER m_pDosHdrTmp
=
(PIMAGE_DOS_HEADER)m_pSrcPeTmp;
PIMAGE_NT_HEADERS m_pNtHdrTmp
=
(PIMAGE_NT_HEADERS)(m_pSrcPeTmp
+
m_pDosHdrTmp
-
>e_lfanew);
PIMAGE_SECTION_HEADER m_pSecHdrTmp
=
(PIMAGE_SECTION_HEADER)
((LPBYTE)&m_pNtHdrTmp
-
>OptionalHeader
+
m_pNtHdrTmp
-
>FileHeader.SizeOfOptionalHeader);
/
/
1.
在压缩前,把节表保存并清空
nSecNum
=
m_pNtHdrTmp
-
>FileHeader.NumberOfSections;
CopyMemory(m_pSaveSecHdr, m_pSecHdrTmp, nSecNum
*
40
);
ZeroMemory(m_pSecHdrTmp, nSecNum
*
40
);
/
/
2.
把导入表保存并清空
m_pImportAddr
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
1
].VirtualAddress;
nImpSize
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
1
].Size;
ZeroMemory(&(m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
1
]),
8
);
/
/
3.
把重定位表保存并清空
m_pRelocAddr
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
5
].VirtualAddress;
nRelocSize
=
m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
5
].Size;
ZeroMemory(&(m_pNtHdrTmp
-
>OptionalHeader.DataDirectory[
5
]),
8
);
Success
=
Compress(
hCompressor,
m_pSrcPeTmp,
m_dwSrcPeSize,
m_pComData,
m_dwSrcPeSize
+
0x28
,
&m_dwComSize
);
return
true;
}
/
/
代码节
strcpy((char
*
)m_newSecHdr[
2
].Name,
".text"
);
m_newSecHdr[
2
].Misc.VirtualSize
=
GetAlign(m_dwCodeSecSize, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
2
].VirtualAddress
=
m_newSecHdr[
1
].VirtualAddress
+
m_newSecHdr[
1
].Misc.VirtualSize;
m_newSecHdr[
2
].SizeOfRawData
=
m_dwCodeSecSize;
m_newSecHdr[
2
].PointerToRawData
=
m_newSecHdr[
1
].PointerToRawData
+
m_newSecHdr[
1
].SizeOfRawData;
m_newSecHdr[
2
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
/
/
代码节
strcpy((char
*
)m_newSecHdr[
2
].Name,
".text"
);
m_newSecHdr[
2
].Misc.VirtualSize
=
GetAlign(m_dwCodeSecSize, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
2
].VirtualAddress
=
m_newSecHdr[
1
].VirtualAddress
+
m_newSecHdr[
1
].Misc.VirtualSize;
m_newSecHdr[
2
].SizeOfRawData
=
m_dwCodeSecSize;
m_newSecHdr[
2
].PointerToRawData
=
m_newSecHdr[
1
].PointerToRawData
+
m_newSecHdr[
1
].SizeOfRawData;
m_newSecHdr[
2
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
/
/
存放原节表、导入表、重定位表的节
strcpy((char
*
)m_newSecHdr[
3
].Name,
".info"
);
m_newSecHdr[
3
].Misc.VirtualSize
=
GetAlign(m_dwTableSecSize, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
3
].VirtualAddress
=
m_newSecHdr[
2
].VirtualAddress
+
m_newSecHdr[
2
].Misc.VirtualSize;
m_newSecHdr[
3
].SizeOfRawData
=
m_dwTableSecSize;
m_newSecHdr[
3
].PointerToRawData
=
m_newSecHdr[
2
].PointerToRawData
+
m_newSecHdr[
2
].SizeOfRawData;
m_newSecHdr[
3
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
/
/
存放原节表、导入表、重定位表的节
strcpy((char
*
)m_newSecHdr[
3
].Name,
".info"
);
m_newSecHdr[
3
].Misc.VirtualSize
=
GetAlign(m_dwTableSecSize, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
3
].VirtualAddress
=
m_newSecHdr[
2
].VirtualAddress
+
m_newSecHdr[
2
].Misc.VirtualSize;
m_newSecHdr[
3
].SizeOfRawData
=
m_dwTableSecSize;
m_newSecHdr[
3
].PointerToRawData
=
m_newSecHdr[
2
].PointerToRawData
+
m_newSecHdr[
2
].SizeOfRawData;
m_newSecHdr[
3
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
bool
CPacker::GetTable()
{
m_dwTableSize
=
nSecNum
*
40
+
4
+
8
+
8
;
m_pTable
=
new BYTE[m_dwTableSize];
RtlCopyMemory(m_pTable, &nSecNum,
4
);
RtlCopyMemory(m_pTable
+
4
, m_pSaveSecHdr, nSecNum
*
40
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
, &m_pImportAddr,
4
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
+
4
, &nImpSize,
4
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
+
4
+
4
, &m_pRelocAddr,
4
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
+
4
+
4
+
4
, &nRelocSize,
4
);
return
true;
}
bool
CPacker::GetTable()
{
m_dwTableSize
=
nSecNum
*
40
+
4
+
8
+
8
;
m_pTable
=
new BYTE[m_dwTableSize];
RtlCopyMemory(m_pTable, &nSecNum,
4
);
RtlCopyMemory(m_pTable
+
4
, m_pSaveSecHdr, nSecNum
*
40
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
, &m_pImportAddr,
4
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
+
4
, &nImpSize,
4
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
+
4
+
4
, &m_pRelocAddr,
4
);
RtlCopyMemory(m_pTable
+
4
+
nSecNum
*
40
+
4
+
4
+
4
, &nRelocSize,
4
);
return
true;
}
/
/
绕过x64搜索导入表的节(空节)
strcpy((char
*
)m_newSecHdr[
4
].Name,
".imp"
);
m_newSecHdr[
4
].Misc.VirtualSize
=
GetAlign(
0x10000
, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
4
].VirtualAddress
=
m_newSecHdr[
3
].VirtualAddress
+
m_newSecHdr[
3
].Misc.VirtualSize;
m_newSecHdr[
4
].SizeOfRawData
=
0
;
m_newSecHdr[
4
].PointerToRawData
=
m_newSecHdr[
3
].PointerToRawData
+
m_newSecHdr[
2
].SizeOfRawData;
m_newSecHdr[
4
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
/
/
绕过x64搜索导入表的节(空节)
strcpy((char
*
)m_newSecHdr[
4
].Name,
".imp"
);
m_newSecHdr[
4
].Misc.VirtualSize
=
GetAlign(
0x10000
, m_pNtHdr
-
>OptionalHeader.SectionAlignment);
m_newSecHdr[
4
].VirtualAddress
=
m_newSecHdr[
3
].VirtualAddress
+
m_newSecHdr[
3
].Misc.VirtualSize;
m_newSecHdr[
4
].SizeOfRawData
=
0
;
m_newSecHdr[
4
].PointerToRawData
=
m_newSecHdr[
3
].PointerToRawData
+
m_newSecHdr[
2
].SizeOfRawData;
m_newSecHdr[
4
].Characteristics
=
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
bool
CPacker::WriteNewPe(CString strNewPe)
{
/
/
创建文件
HANDLE hFile
=
CreateFile(strNewPe,
GENERIC_WRITE,
0
,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
/
/
写入PE头
DWORD dwBytesWrited
=
0
;
WriteFile(hFile, m_pNewPeHdr, m_dwNewPeHdrSize, &dwBytesWrited, NULL);
/
/
写入数据节
WriteFile(hFile, m_pComSec, m_dwComSecSize, &dwBytesWrited, NULL);
/
/
写入代码节
WriteFile(hFile, m_pCodeSec, m_dwCodeSecSize, &dwBytesWrited, NULL);
/
/
写入存放表数据节
WriteFile(hFile, m_pTableSec, m_dwTableSecSize, &dwBytesWrited, NULL);
CloseHandle(hFile);
return
true;
}
bool
CPacker::WriteNewPe(CString strNewPe)
{
/
/
创建文件
HANDLE hFile
=
CreateFile(strNewPe,
GENERIC_WRITE,
0
,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
/
/
写入PE头
DWORD dwBytesWrited
=
0
;
WriteFile(hFile, m_pNewPeHdr, m_dwNewPeHdrSize, &dwBytesWrited, NULL);
/
/
写入数据节
WriteFile(hFile, m_pComSec, m_dwComSecSize, &dwBytesWrited, NULL);
/
/
写入代码节
WriteFile(hFile, m_pCodeSec, m_dwCodeSecSize, &dwBytesWrited, NULL);
/
/
写入存放表数据节
WriteFile(hFile, m_pTableSec, m_dwTableSecSize, &dwBytesWrited, NULL);
CloseHandle(hFile);
return
true;
}
1.
使用Release版(Debug会加地址有关代码)
2.
不使用main函数,自己定义一个,放在链接器
-
>高级
-
>入口点(main不是程序真正入口点)
3.
关掉代码生成
-
>安全检查
4.
关掉增强指令集
5.
关掉全程序优化
1.
使用Release版(Debug会加地址有关代码)
2.
不使用main函数,自己定义一个,放在链接器
-
>高级
-
>入口点(main不是程序真正入口点)
3.
关掉代码生成
-
>安全检查
4.
关掉增强指令集
5.
关掉全程序优化
1.
解压缩节表
2
里面的压缩数据
2.
将解压缩数据覆盖到PE头处,连带着空节表
1
也被覆盖
3.
还原节表、导入表、重定位表
1.
解压缩节表
2
里面的压缩数据
2.
将解压缩数据覆盖到PE头处,连带着空节表
1
也被覆盖
3.
还原节表、导入表、重定位表
HMODULE GetKernel32()
{
HMODULE hKer;
__asm {
mov eax, dword ptr fs:[
0x30
]
mov eax, dword ptr[eax
+
0x0C
]
mov eax, dword ptr[eax
+
0x0C
]
mov eax, dword ptr[eax]
mov eax, dword ptr[eax]
mov eax, dword ptr[eax
+
0x18
]
mov hKer, eax
}
return
hKer;
}
HMODULE GetKernel32()
{
HMODULE hKer;
__asm {
mov eax, dword ptr fs:[
0x30
]
mov eax, dword ptr[eax
+
0x0C
]
mov eax, dword ptr[eax
+
0x0C
]
mov eax, dword ptr[eax]
mov eax, dword ptr[eax]
mov eax, dword ptr[eax
+
0x18
]
mov hKer, eax
}
return
hKer;
}
FARPROC MyGetProcAddress(HMODULE hMod, LPCSTR lpProcName) {
IMAGE_DOS_HEADER
*
pDosHdr;
IMAGE_NT_HEADERS
*
pNTHdr;
IMAGE_EXPORT_DIRECTORY
*
pExpDir;
DWORD pAddrTbl;
DWORD pNameTbl;
DWORD pOrdTbl;
/
/
解析dos头
pDosHdr
=
(IMAGE_DOS_HEADER
*
)hMod;
/
/
nt头
pNTHdr
=
(IMAGE_NT_HEADERS
*
)(pDosHdr
-
>e_lfanew
+
(DWORD)hMod);
/
/
获取导出表
pExpDir
=
(IMAGE_EXPORT_DIRECTORY
*
)(pNTHdr
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress
+
(DWORD)hMod);
/
/
导出函数地址表
pAddrTbl
=
(DWORD)(pExpDir
-
>AddressOfFunctions
+
(DWORD)hMod);
/
/
导出函数名称表
pNameTbl
=
(DWORD)(pExpDir
-
>AddressOfNames
+
(DWORD)hMod);
/
/
导出序号表
pOrdTbl
=
(DWORD)(pExpDir
-
>AddressOfNameOrdinals
+
(DWORD)hMod);
/
/
判断是序号还是名称
if
((
int
)lpProcName &
0xffff0000
) {
/
/
名称
int
i
=
0
;
while
(i < pExpDir
-
>NumberOfNames) {
/
/
获取名称地址
int
nNameOff
=
(
int
)(
*
(DWORD
*
)(pNameTbl
+
i
*
4
)
+
(DWORD)hMod);
/
/
字符串比较
if
(((char
*
)nNameOff)[
0
]
=
=
'G'
&& ((char
*
)nNameOff)[
1
]
=
=
'e'
&& ((char
*
)nNameOff)[
2
]
=
=
't'
&&
((char
*
)nNameOff)[
3
]
=
=
'P'
&& ((char
*
)nNameOff)[
4
]
=
=
'r'
&& ((char
*
)nNameOff)[
5
]
=
=
'o'
&&
((char
*
)nNameOff)[
6
]
=
=
'c'
&& ((char
*
)nNameOff)[
7
]
=
=
'A'
&& ((char
*
)nNameOff)[
8
]
=
=
'd'
&&
((char
*
)nNameOff)[
9
]
=
=
'd'
&& ((char
*
)nNameOff)[
10
]
=
=
'r'
&& ((char
*
)nNameOff)[
11
]
=
=
'e'
&&
((char
*
)nNameOff)[
12
]
=
=
's'
&& ((char
*
)nNameOff)[
13
]
=
=
's'
) {
/
/
找到了, 从导出序号表取出函数地址下标
int
nOrdinal
=
*
(WORD
*
)(pOrdTbl
+
i
*
2
);
/
/
从导出地址表,下标寻址,获取导出函数地址
int
nFuncAddr
=
*
(DWORD
*
)(pAddrTbl
+
nOrdinal
*
4
);
/
/
不是转发
nFuncAddr
+
=
(
int
)hMod;
/
/
返回地址
if
(nFuncAddr !
=
NULL) {
return
(FARPROC)nFuncAddr;
}
}
i
+
+
;
}
}
else
{
/
/
序号
int
nOrdinal
=
(DWORD)lpProcName
-
pExpDir
-
>Base;
/
/
从导出地址表,下标寻址,获取导出函数地址
int
nFuncAddr
=
*
(DWORD
*
)(pAddrTbl
+
nOrdinal
*
4
);
/
/
返回地址
if
(nFuncAddr !
=
NULL) {
return
(FARPROC)(nFuncAddr
+
(DWORD)hMod);
}
}
return
0
;
}
FARPROC MyGetProcAddress(HMODULE hMod, LPCSTR lpProcName) {
IMAGE_DOS_HEADER
*
pDosHdr;
IMAGE_NT_HEADERS
*
pNTHdr;
IMAGE_EXPORT_DIRECTORY
*
pExpDir;
DWORD pAddrTbl;
DWORD pNameTbl;
DWORD pOrdTbl;
/
/
解析dos头
pDosHdr
=
(IMAGE_DOS_HEADER
*
)hMod;
/
/
nt头
pNTHdr
=
(IMAGE_NT_HEADERS
*
)(pDosHdr
-
>e_lfanew
+
(DWORD)hMod);
/
/
获取导出表
pExpDir
=
(IMAGE_EXPORT_DIRECTORY
*
)(pNTHdr
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress
+
(DWORD)hMod);
/
/
导出函数地址表
pAddrTbl
=
(DWORD)(pExpDir
-
>AddressOfFunctions
+
(DWORD)hMod);
/
/
导出函数名称表
pNameTbl
=
(DWORD)(pExpDir
-
>AddressOfNames
+
(DWORD)hMod);
/
/
导出序号表
pOrdTbl
=
(DWORD)(pExpDir
-
>AddressOfNameOrdinals
+
(DWORD)hMod);
/
/
判断是序号还是名称
if
((
int
)lpProcName &
0xffff0000
) {
/
/
名称
int
i
=
0
;
while
(i < pExpDir
-
>NumberOfNames) {
/
/
获取名称地址
int
nNameOff
=
(
int
)(
*
(DWORD
*
)(pNameTbl
+
i
*
4
)
+
(DWORD)hMod);
/
/
字符串比较
if
(((char
*
)nNameOff)[
0
]
=
=
'G'
&& ((char
*
)nNameOff)[
1
]
=
=
'e'
&& ((char
*
)nNameOff)[
2
]
=
=
't'
&&
((char
*
)nNameOff)[
3
]
=
=
'P'
&& ((char
*
)nNameOff)[
4
]
=
=
'r'
&& ((char
*
)nNameOff)[
5
]
=
=
'o'
&&
((char
*
)nNameOff)[
6
]
=
=
'c'
&& ((char
*
)nNameOff)[
7
]
=
=
'A'
&& ((char
*
)nNameOff)[
8
]
=
=
'd'
&&
((char
*
)nNameOff)[
9
]
=
=
'd'
&& ((char
*
)nNameOff)[
10
]
=
=
'r'
&& ((char
*
)nNameOff)[
11
]
=
=
'e'
&&
((char
*
)nNameOff)[
12
]
=
=
's'
&& ((char
*
)nNameOff)[
13
]
=
=
's'
) {
/
/
找到了, 从导出序号表取出函数地址下标
int
nOrdinal
=
*
(WORD
*
)(pOrdTbl
+
i
*
2
);
/
/
从导出地址表,下标寻址,获取导出函数地址
int
nFuncAddr
=
*
(DWORD
*
)(pAddrTbl
+
nOrdinal
*
4
);
/
/
不是转发
nFuncAddr
+
=
(
int
)hMod;
/
/
返回地址
if
(nFuncAddr !
=
NULL) {
return
(FARPROC)nFuncAddr;
}
}
i
+
+
;
}
}
else
{
/
/
序号
int
nOrdinal
=
(DWORD)lpProcName
-
pExpDir
-
>Base;
/
/
从导出地址表,下标寻址,获取导出函数地址
int
nFuncAddr
=
*
(DWORD
*
)(pAddrTbl
+
nOrdinal
*
4
);
/
/
返回地址
if
(nFuncAddr !
=
NULL) {
return
(FARPROC)(nFuncAddr
+
(DWORD)hMod);
}
}
return
0
;
}
char szLoadLibrary[]
=
{
'L'
,
'o'
,
'a'
,
'd'
,
'L'
,
'i'
,
'b'
,
'r'
,
'a'
,
'r'
,
'y'
,
'A'
,
'\0'
};
pEnv
-
>pfnLoadLibraryA
=
(PFN_LoadLibraryA)pEnv
-
>pfnGetProcAddress(hKer, szLoadLibrary);
char szLoadLibrary[]
=
{
'L'
,
'o'
,
'a'
,
'd'
,
'L'
,
'i'
,
'b'
,
'r'
,
'a'
,
'r'
,
'y'
,
'A'
,
'\0'
};
pEnv
-
>pfnLoadLibraryA
=
(PFN_LoadLibraryA)pEnv
-
>pfnGetProcAddress(hKer, szLoadLibrary);
/
/
有了LoadLibrary和GetProcAddress,就可以使用任意函数了
/
/
获取解压缩相关函数
char szCab[]
=
{
'C'
,
'a'
,
'b'
,
'i'
,
'n'
,
'e'
,
't'
,
'\0'
};
HMODULE hCab
=
pEnv
-
>pfnLoadLibraryA(szCab);
char szCreateDecompressor[]
=
{
'C'
,
'r'
,
'e'
,
'a'
,
't'
,
'e'
,
'D'
,
'e'
,
'c'
,
'o'
,
'm'
,
'p'
,
'r'
,
'e'
,
's'
,
's'
,
'o'
,
'r'
,
'\0'
};
pEnv
-
>pfnCreateDecompressor
=
(PFN_CreateDecompressor)pEnv
-
>pfnGetProcAddress(hCab, szCreateDecompressor);
char szDecompress[]
=
{
'D'
,
'e'
,
'c'
,
'o'
,
'm'
,
'p'
,
'r'
,
'e'
,
's'
,
's'
,
'\0'
};
pEnv
-
>pfnDecompress
=
(PFN_Decompress)pEnv
-
>pfnGetProcAddress(hCab, szDecompress);
char szVirtualAlloc[]
=
{
'V'
,
'i'
,
'r'
,
't'
,
'u'
,
'a'
,
'l'
,
'A'
,
'l'
,
'l'
,
'o'
,
'c'
,
'\0'
};
pEnv
-
>pfnVirtualAlloc
=
(PFN_VirtualAlloc)pEnv
-
>pfnGetProcAddress(hKer, szVirtualAlloc);
char szVirtualProtect[]
=
{
'V'
,
'i'
,
'r'
,
't'
,
'u'
,
'a'
,
'l'
,
'P'
,
'r'
,
'o'
,
't'
,
'e'
,
'c'
,
't'
,
'\0'
};
pEnv
-
>pfnVirtualProtect
=
(PFN_VirtualProtect)pEnv
-
>pfnGetProcAddress(hKer, szVirtualProtect);
/
/
解压缩
LPBYTE pPEBuff
=
(LPBYTE)env.pfnVirtualAlloc(NULL, dwDeComSize, MEM_COMMIT, PAGE_READWRITE);
DECOMPRESSOR_HANDLE hDecompressor;
BOOL
bSuccess
=
env.pfnCreateDecompressor(
COMPRESS_ALGORITHM_XPRESS_HUFF,
NULL,
&hDecompressor
);
DWORD dwDecompressedBufferSize
=
0
;
bSuccess
=
env.pfnDecompress(
hDecompressor,
pComData,
dwComSize,
pPEBuff,
dwDeComSize,
&dwDecompressedBufferSize
);
/
/
有了LoadLibrary和GetProcAddress,就可以使用任意函数了
/
/
获取解压缩相关函数
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2022-8-23 12:21
被橘喵Cat编辑
,原因: