by devseed,此篇教程同时发在论坛和我的博客 上,完整源码见我的github
正好赶上元宵节写完了,祝大家元宵节快乐~ 写教程不易,希望大家来给我的github点个star~
5年前,初入逆向,看着PE结构尤其是IAT一头雾水,对于脱壳原理理解不深刻,于是就用c++自己写了个简单的加壳工具(SimpleDpack )。最近回顾发现以前代码写的挺乱的,于是重构了一下代码,规范了命名和拆分了几个函数,使得结构清晰,稍微拓展一下支持64位。虽然这个toy example程序本身意义不大,但是通过这个程序可以来熟悉PE结构和加壳原理,深刻理解各种指针和内存分布等操作,对于初学者非常有帮助。于是我打算以此例来讲解Windows PE结构,谈谈加壳原理、编写shellcode等方法,解决方案和一些技巧等。
来讲述PE结构的教程虽然已经有很多了,但好多都是偏向于理论,很多东西不去文件中自己看看很不容易理解。这里将结合PE实例来分析其结构与作用。由于32位程序PE结构分析很多了,此处以64位程序为例分析 。其实pe64也就ImageBase
、VA
如IAT
和OFT
等、堆栈大小等是ULONGLONG,其他和pe32基本保持一致。
Windows PE的数据结构定义在winnt.h
头文件里,大体可以归纳下列几点:
具体细节可以看此图(来源于网络)
在OptionalHeader
的最后,有DataDirectory[16]
,定义了PE文件各 Directory的RVA
和size
,如下:
DLL导出表头,一般在.rdata
、.edata
。
里面有三个表的指针(RVA),都是数组形式存储(每个表里的项地址上是连续的),
上面这个看着有点抽象,来用user32.dll举个例子,可以手动的计算段内偏移。
IMAGE_EXPORT_DIRECTORY
结构如下图,
之后可以根据上表段内偏移来看查看函数RVA表
,函数名称RVA表
,如下:
DLL导入表,一般在.rdata
、.idata
。
描述了若干个导入的DLL(IMAGE_IMPORT_DESCRIPTOR
),每个DLL导入若干个函数(IMAGE_THUNK_DATA
)
若干个IMAGE_IMPORT_DESCRIPTOR
项组成数组,描述导入的若干个DLL,以全0项结尾
IMAGE_IMPORT_DESCRIPTOR
结构中有IMAGE_THUNK_DAT
数组指针(RVA),同样以全0项结尾。结构内含有其导入DLL中的函数信息指针(RVA)。两个数组指针如下:
OriginalFirstThunk(OFT)
表:导入函数的函数序数、名称表,AddressOfData
指针(RVA)指向IMAGE_IMPORT_BY_NAME
结构
FirstThunk(FT)
表:运行前的内容和OriginalFirstThunk
一样,运行时加载为各函数的VA,即IAT
IMAGE_IMPORT_DESCRIPTOR
中的一项,上面用section offset表示的,这里就用file offset表示了, 如下图所示:
加载前OFT
表和FT(IAT)
表内容相同,都是指向IMAGE_IMPORT_BY_NAME
结构,里面有序号和函数名。b1148h
的file offset为b1148h-91000h + 8fe00h = aff48h
,如下图所示。
即为我们所说的IAT
表,多在.rdata
。
IAT
表存储了各DLL函数的运行时地址VA
(IAT在data directory中的声明并不是必要的,主要在运行时调用)。
程序运行前,IAT
表的值与OFT
表的值一样(即上一节说的运行前FT
表与OFT
表内的值一样)
编译器会把动态库函数用call [imagebase + iat + offset]
这种内存间接寻址,即
call -> IAT(FT) -> func_addr
这个IMAGE_DIRECTORY_ENTRY_IAT
和IMAGE_DIRECTORY_ENTRY_IMPORT
的概念还挺绕的,为了形象说明,下面再以user32.dll为例分析,这次来分析x64的IAT
。IAT
的首项(64位每项占8字节)RVA
为91c58h
,正好是FT
指向的RVA
,其值(b1148h
)在程序加载前和OFT
一样 。
我们在IDA中找到一处调用IAT第一项的call,即下图call cs:PatBlt
。由于是64位汇编,call和jmp只能是对于于此RIP的+-2g地址空间跳转。即此处call (44 FF 15)后四字节(8e bb 06 00)为下一条指令地址和间接寻址内存的相对地址,即6bb8eh+260cah = 91c58h
,正好是IAT的第一项地址。
重定向表,多在.reloc
。
记载了需要重定向的地址,在DLL中或是开启ASLR后,基址改变,通过此表来修改地址以匹配新的基址。
如下图,RVA=b20a0h
中存储的地址需要重定向,因为F0 CE 02 80 01 00 00 00 00
是以18000000000h
为基址的VA,程序运行前需要重定向到对应基址。
section header
为PE头的最后一部分,里面储存的各个区段的File Offset
,RVA
,Size
,Characteristics
等。RVA
和File Offset
地址转换要来查此表。关于区段头注意:
数据结构如下:
这部分主要就是根据结构,和偏移,来用指针指向对应的数据,详见CPEinfo 类。
上一节说了好多,其实并不难,就是PE结构有一些地方比较绕,因此来分析了实际PE文件的几个部分。熟悉了PE结构,接下来开始谈谈加壳相关的了。
加壳主要有两部分:负责压缩修改等写入exe的加壳程序、嵌入exe的负责解压还原等操作的壳程序本身。
加壳程序作用:将区段压缩等原来的数据结构写入exe,重建PE结构等;把原程序OEP
等参数重定向在壳内,重定向壳shellcode的地址等。
壳的作用:大体上来讲就是还原源程序各区段代码,同时模拟windows对程序的初始化,比如IAT
表的载入等。
对于压缩壳,我们壳内的索引需要有
落实到代码上,在dPackType.h 中
压缩我们采取开源算法LZMA
,简单wrapper一下,将LZMA
的参数与解压大小等放到压缩数据头即可。其他方面如加密、反调试、花指令什么的暂不考虑,不过在这个我定义的框架下也很好添加。
shellcode一般都是用汇编去编写,但是我们要同时去做32位和64位程序,就要写两份汇编了。因此我们采取用c来编写shellcode,必要的地方加入汇编即可。同时,为了方便将shellcode附加到源程序上,我们采取将shellcode编译为DLL,这样就可以通过reloc
方便的调整基址了。
在我们这个简单的压缩壳中,主要的是四部分:
为了方便扩展,比如说加密,添加stolen oep
等,前后分别加上BeforeUnpack()
,AfterUnpack()
空函数。此部分的完整代码在simpledpackshell.cpp 、shellcode64.asm 。
直接用VirtualQueryEx
和VirtualAllocEx
即可
VirtualProtect
申请写权限,解压代码到缓冲区再memcpy
到制定位置即可,之后再恢复原来的保护权限。注意这里new
的缓冲区一定要够,否则运行的时候会出现heap损坏等exception。同时,我们引入DPACK_SECTION_RAW
和DPACK_SECTION_DLZMA
宏来作为压缩标志。
DPACK_SHELL_INDEX
这个结构记载了原程序IAT
,我们需要LoadLibrary
和GetProcAddress
手动得到函数的地址,再写入源IAT
中。
这个最简单的方法就是用push和ret实现了,我们用g_orgOep来表示源OEP的地址。
加壳程序主要进行下面方面的处理:
下面挑重点说一些操作,加壳程序完整代码在CSimpleDpack ,对PE进行修改的代码见CPEedit 。
由于我们的shellcode在DLL中,因此可以直接LoadLibrary
载入,GetProcAddress
可以获取g_dpackShellIndex
这个我们导出的壳的索引结构。对shellcode
进行重定向和IAT
的处理如下:
我们需要把一些信息调到壳上,还有最后一定要关掉ASLR
,因为壳内跳转到OEP是硬编码的,不能让基址变化。
最后就是根据索引合并各个缓存区了,这里我们把shellcode和压缩数据都放到了最后一个区段,之后把PE缓存区根据FileAlignment
保存即可。
由于64位的相关教程比较少,这里来说说如何同时支持64位和32位。
其实64位和32位结构很相似,也就是涉及到VA
或size
是ULONGLONG类型,大部分名称微软已经帮我们用宏重定向了64还是32位结构;还有一个麻烦事,在visual studio里面64位程序是没法开启内联汇编的。
关于64位数据类型不一样的地方,我们可以用宏_WIN64
来区分是否64此程序,这样我们编译64位加壳程序后就能解析64位程序加壳了。比如说:
关于64位visual studio无法内联汇编,我们要:
至此,我们的程序可以同时支持64位和32位了。
|DOS header
/
/
e_lfanew
|NT header
|
file
header
/
/
NumberOfSections, SizeOfOptionalHeader(x86
=
0xe0
, x64
=
0xf0
)
|optional header
|...
/
/
AddressOfEntryPoint(oep), ImageBase, SizeOfImage, SizeOfHeaders
|data directory[
16
]
/
/
IMAGE_DIRECTORY_ENTRY_EXPORT, ..._IMPORT, ..._IAT
|section headers[n]
|DOS header
/
/
e_lfanew
|NT header
|
file
header
/
/
NumberOfSections, SizeOfOptionalHeader(x86
=
0xe0
, x64
=
0xf0
)
|optional header
|...
/
/
AddressOfEntryPoint(oep), ImageBase, SizeOfImage, SizeOfHeaders
|data directory[
16
]
/
/
IMAGE_DIRECTORY_ENTRY_EXPORT, ..._IMPORT, ..._IAT
|section headers[n]
typedef struct _IMAGE_OPTIONAL_HEADER {
...
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64,
*
PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY,
*
PIMAGE_DATA_DIRECTORY;
/
/
IMAGE_DIRECTORY_ENTRY_COPYRIGHT
7
/
/
(X86 usage)
typedef struct _IMAGE_OPTIONAL_HEADER {
...
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64,
*
PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY,
*
PIMAGE_DATA_DIRECTORY;
/
/
IMAGE_DIRECTORY_ENTRY_COPYRIGHT
7
/
/
(X86 usage)
typedef struct _IMAGE_EXPORT_DIRECTORY {
/
/
Export Directory Table
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
/
/
the name of the DLL, RVA
DWORD Base;
/
/
The starting ordinal number
for
exports
in
this image, usually
1
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions;
/
/
Export Address Table, RVA
from
base of image
DWORD AddressOfNames;
/
/
Export Name Pointer Table, RVA
DWORD AddressOfNameOrdinals;
/
/
Export Ordinal Table, RVA
from
base of image
} IMAGE_EXPORT_DIRECTORY,
*
PIMAGE_EXPORT_DIRECTORY;
typedef struct _IMAGE_EXPORT_DIRECTORY {
/
/
Export Directory Table
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
/
/
the name of the DLL, RVA
DWORD Base;
/
/
The starting ordinal number
for
exports
in
this image, usually
1
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions;
/
/
Export Address Table, RVA
from
base of image
DWORD AddressOfNames;
/
/
Export Name Pointer Table, RVA
DWORD AddressOfNameOrdinals;
/
/
Export Ordinal Table, RVA
from
base of image
} IMAGE_EXPORT_DIRECTORY,
*
PIMAGE_EXPORT_DIRECTORY;
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
/
/
0
for
terminating null
import
descriptor
DWORD OriginalFirstThunk;
/
/
RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
/
/
index of the first forwarder reference,
-
1
if
no
DWORD Name;
/
/
RVA to the name of dll
DWORD FirstThunk;
/
/
RVA to IAT (
if
bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED
*
PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
/
/
PBYTE
DWORD Function;
/
/
PDWORD, va of the function,
in
ft, oat
DWORD Ordinal;
DWORD AddressOfData;
/
/
PIMAGE_IMPORT_BY_NAME,
in
oft
} u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_THUNK_DATA64 {
union {
ULONGLONG ForwarderString;
/
/
PBYTE
ULONGLONG Function;
/
/
PDWORD, va of the function,
in
ft, oat
ULONGLONG Ordinal;
ULONGLONG AddressOfData;
/
/
PIMAGE_IMPORT_BY_NAME,
in
oft
} u1;
} IMAGE_THUNK_DATA64;
typedef struct _IMAGE_IMPORT_BY_NAME {
/
/
in
oft
WORD Hint;
CHAR Name[
1
];
/
/
char
*
Name
} IMAGE_IMPORT_BY_NAME,
*
PIMAGE_IMPORT_BY_NAME;
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
/
/
0
for
terminating null
import
descriptor
DWORD OriginalFirstThunk;
/
/
RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
/
/
index of the first forwarder reference,
-
1
if
no
DWORD Name;
/
/
RVA to the name of dll
DWORD FirstThunk;
/
/
RVA to IAT (
if
bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED
*
PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
/
/
PBYTE
DWORD Function;
/
/
PDWORD, va of the function,
in
ft, oat
DWORD Ordinal;
DWORD AddressOfData;
/
/
PIMAGE_IMPORT_BY_NAME,
in
oft
} u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_THUNK_DATA64 {
union {
ULONGLONG ForwarderString;
/
/
PBYTE
ULONGLONG Function;
/
/
PDWORD, va of the function,
in
ft, oat
ULONGLONG Ordinal;
ULONGLONG AddressOfData;
/
/
PIMAGE_IMPORT_BY_NAME,
in
oft
} u1;
} IMAGE_THUNK_DATA64;
typedef struct _IMAGE_IMPORT_BY_NAME {
/
/
in
oft
WORD Hint;
CHAR Name[
1
];
/
/
char
*
Name
} IMAGE_IMPORT_BY_NAME,
*
PIMAGE_IMPORT_BY_NAME;
typedef struct _IMAGE_BASE_RELOCATION {
/
/
it has multi base relocation block,
DWORD VirtualAddress;
/
/
rva of this base relocation area
DWORD SizeOfBlock;
/
/
The total number of bytes
in
the base relocation block, including the Page RVA
and
Block Size fields
and
the
Type
/
Offset fields that follow.
/
/
WORD TypeOffset[
1
];
} IMAGE_BASE_RELOCATION;
/
/
Each base relocation block starts with this struct
typedef struct TypeOffset
/
/
after one base_relation, it has multi typeoffset
{
WORD offset :
12
;
/
/
偏移值
WORD
type
:
4
;
/
/
重定位属性(方式), 高
4
位
/
/
IMAGE_REL_BASED_ABSOLUTE
0
The base relocation
is
skipped,used to pad a block.
/
/
IMAGE_REL_BASED_HIGHLOW
3
The base relocation applies
all
32
bits of the difference to the
32
-
bit field at offset. va
=
offset
+
base_rva
+
imagebase
/
/
IMAGE_REL_BASED_DIR64
10
for
64bit
}TypeOffset,
*
PTypeOffset
typedef struct _IMAGE_BASE_RELOCATION {
/
/
it has multi base relocation block,
DWORD VirtualAddress;
/
/
rva of this base relocation area
DWORD SizeOfBlock;
/
/
The total number of bytes
in
the base relocation block, including the Page RVA
and
Block Size fields
and
the
Type
/
Offset fields that follow.
/
/
WORD TypeOffset[
1
];
} IMAGE_BASE_RELOCATION;
/
/
Each base relocation block starts with this struct
typedef struct TypeOffset
/
/
after one base_relation, it has multi typeoffset
{
WORD offset :
12
;
/
/
偏移值
WORD
type
:
4
;
/
/
重定位属性(方式), 高
4
位
/
/
IMAGE_REL_BASED_ABSOLUTE
0
The base relocation
is
skipped,used to pad a block.
/
/
IMAGE_REL_BASED_HIGHLOW
3
The base relocation applies
all
32
bits of the difference to the
32
-
bit field at offset. va
=
offset
+
base_rva
+
imagebase
/
/
IMAGE_REL_BASED_DIR64
10
for
64bit
}TypeOffset,
*
PTypeOffset
typedef struct _IMAGE_SECTION_HEADER {
/
/
0x28
bytes, the last
is
all
zero
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
/
/
8
bytes, null end
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
/
/
rva (, relative to the image base)
DWORD SizeOfRawData;
/
/
The size of the initialized data on disk,
in
bytes
DWORD PointerToRawData;
/
/
fileoffset of the section data
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
/
/
for
debug line number
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
/
/
IMAGE_SCN_MEM_EXECUTE
0x20000000
,IMAGE_SCN_MEM_READ
0x40000000
, IMAGE_SCN_MEM_WRITE
0x80000000
} IMAGE_SECTION_HEADER,
*
PIMAGE_SWECTION_HEADER;
typedef struct _IMAGE_SECTION_HEADER {
/
/
0x28
bytes, the last
is
all
zero
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
/
/
8
bytes, null end
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
/
/
rva (, relative to the image base)
DWORD SizeOfRawData;
/
/
The size of the initialized data on disk,
in
bytes
DWORD PointerToRawData;
/
/
fileoffset of the section data
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
/
/
for
debug line number
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
/
/
IMAGE_SCN_MEM_EXECUTE
0x20000000
,IMAGE_SCN_MEM_READ
0x40000000
, IMAGE_SCN_MEM_WRITE
0x80000000
} IMAGE_SECTION_HEADER,
*
PIMAGE_SWECTION_HEADER;
PIMAGE_NT_HEADERS CPEinfo::getNtHeader(LPBYTE pPeBuf)
{
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pPeBuf;
return
(PIMAGE_NT_HEADERS)(pPeBuf
+
pDosHeader
-
>e_lfanew);
}
PIMAGE_FILE_HEADER CPEinfo::getFileHeader(LPBYTE pPeBuf)
{
return
&getNtHeader(pPeBuf)
-
>FileHeader;
}
PIMAGE_OPTIONAL_HEADER CPEinfo::getOptionalHeader(LPBYTE pPeBuf)
{
return
&getNtHeader(pPeBuf)
-
>OptionalHeader;
}
PIMAGE_DATA_DIRECTORY CPEinfo::getImageDataDirectory(LPBYTE pPeBuf)
{
PIMAGE_OPTIONAL_HEADER pOptionalHeader
=
getOptionalHeader(pPeBuf);
return
pOptionalHeader
-
>DataDirectory;
}
PIMAGE_SECTION_HEADER CPEinfo::getSectionHeader(LPBYTE pPeBuf)
{
PIMAGE_NT_HEADERS pNtHeader
=
getNtHeader(pPeBuf);
return
(PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeader
+
sizeof(IMAGE_NT_HEADERS));
}
PIMAGE_IMPORT_DESCRIPTOR CPEinfo::getImportDescriptor(LPBYTE pPeBuf,
bool
bMemAlign
=
true)
{
PIMAGE_DATA_DIRECTORY pImageDataDirectory
=
getImageDataDirectory(pPeBuf);
DWORD rva
=
pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
DWORD offset
=
bMemAlign ? rva: rva2faddr(pPeBuf, rva);
return
(PIMAGE_IMPORT_DESCRIPTOR)(pPeBuf
+
offset);
}
PIMAGE_EXPORT_DIRECTORY CPEinfo::getExportDirectory(LPBYTE pPeBuf,
bool
bMemAlign
=
true)
{
PIMAGE_DATA_DIRECTORY pImageDataDirectory
=
getImageDataDirectory(pPeBuf);
DWORD rva
=
pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
DWORD offset
=
bMemAlign ? rva : rva2faddr(pPeBuf, rva);
return
(PIMAGE_EXPORT_DIRECTORY)(pPeBuf
+
offset);
}
DWORD CPEinfo::getOepRva(LPBYTE pPeBuf)
{
if
(pPeBuf
=
=
NULL)
return
0
;
if
(isPe(pPeBuf) <
=
0
)
return
0
;
return
getOptionalHeader(pPeBuf)
-
>AddressOfEntryPoint;
}
WORD CPEinfo::getSectionNum(LPBYTE pPeBuf)
{
return
getFileHeader(pPeBuf)
-
>NumberOfSections;
}
PIMAGE_NT_HEADERS CPEinfo::getNtHeader(LPBYTE pPeBuf)
{
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pPeBuf;
return
(PIMAGE_NT_HEADERS)(pPeBuf
+
pDosHeader
-
>e_lfanew);
}
PIMAGE_FILE_HEADER CPEinfo::getFileHeader(LPBYTE pPeBuf)
{
return
&getNtHeader(pPeBuf)
-
>FileHeader;
}
PIMAGE_OPTIONAL_HEADER CPEinfo::getOptionalHeader(LPBYTE pPeBuf)
{
return
&getNtHeader(pPeBuf)
-
>OptionalHeader;
}
PIMAGE_DATA_DIRECTORY CPEinfo::getImageDataDirectory(LPBYTE pPeBuf)
{
PIMAGE_OPTIONAL_HEADER pOptionalHeader
=
getOptionalHeader(pPeBuf);
return
pOptionalHeader
-
>DataDirectory;
}
PIMAGE_SECTION_HEADER CPEinfo::getSectionHeader(LPBYTE pPeBuf)
{
PIMAGE_NT_HEADERS pNtHeader
=
getNtHeader(pPeBuf);
return
(PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeader
+
sizeof(IMAGE_NT_HEADERS));
}
PIMAGE_IMPORT_DESCRIPTOR CPEinfo::getImportDescriptor(LPBYTE pPeBuf,
bool
bMemAlign
=
true)
{
PIMAGE_DATA_DIRECTORY pImageDataDirectory
=
getImageDataDirectory(pPeBuf);
DWORD rva
=
pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
DWORD offset
=
bMemAlign ? rva: rva2faddr(pPeBuf, rva);
return
(PIMAGE_IMPORT_DESCRIPTOR)(pPeBuf
+
offset);
}
PIMAGE_EXPORT_DIRECTORY CPEinfo::getExportDirectory(LPBYTE pPeBuf,
bool
bMemAlign
=
true)
{
PIMAGE_DATA_DIRECTORY pImageDataDirectory
=
getImageDataDirectory(pPeBuf);
DWORD rva
=
pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
DWORD offset
=
bMemAlign ? rva : rva2faddr(pPeBuf, rva);
return
(PIMAGE_EXPORT_DIRECTORY)(pPeBuf
+
offset);
}
DWORD CPEinfo::getOepRva(LPBYTE pPeBuf)
{
if
(pPeBuf
=
=
NULL)
return
0
;
if
(isPe(pPeBuf) <
=
0
)
return
0
;
return
getOptionalHeader(pPeBuf)
-
>AddressOfEntryPoint;
}
WORD CPEinfo::getSectionNum(LPBYTE pPeBuf)
{
return
getFileHeader(pPeBuf)
-
>NumberOfSections;
}
typedef struct _DLZMA_HEADER
{
size_t RawDataSize;
/
/
原始数据尺寸(不含此头)
size_t DataSize;
/
/
压缩后的数据大小
char LzmaProps[LZMA_PROPS_SIZE];
/
/
原始lzma的文件头
}DLZMA_HEADER,
*
PDLZMA_HEADER;
/
/
此处外围添加适用于dpack的lzma头
typedef struct _DPACK_ORGPE_INDEX
/
/
源程序被隐去的信息,此结构为明文表示,地址全是rva
{
ULONGLONG ImageBase;
/
/
源程序基址
DWORD ImageBase;
/
/
源程序基址
DWORD OepRva;
/
/
原程序rva入口
DWORD ImportRva;
/
/
导入表信息
DWORD ImportSize;
}DPACK_ORGPE_INDEX,
*
PDPACK_ORGPE_INDEX;
typedef struct _DPACK_SECTION_ENTRY
/
/
源信息与压缩变换后信息索引表是
{
/
/
假设不超过
4g
DWORD OrgRva;
/
/
OrgRva为
0
时则是不解压到原来区段
DWORD OrgSize;
DWORD DpackRva;
DWORD DpackSize;
DWORD Characteristics;
DWORD DpackSectionType;
/
/
dpack区段类型
}DPACK_SECTION_ENTRY,
*
PDPACK_SECTION_ENTRY;
typedef struct _DPACK_SHELL_INDEX
/
/
DPACK变换头
{
union
{
PVOID DpackOepFunc;
/
/
初始化壳的入口函数(放第一个元素方便初始化)
DWORD DpackOepRva;
/
/
加载shellcode后也许改成入口RVA
};
DPACK_ORGPE_INDEX OrgIndex;
WORD SectionNum;
/
/
变换的区段数,最多MAX_DPACKSECTNUM区段
DPACK_SECTION_ENTRY SectionIndex[MAX_DPACKSECTNUM];
/
/
变换区段索引, 以全
0
结尾
PVOID Extra;
/
/
其他信息,方便之后拓展
}DPACK_SHELL_INDEX,
*
PDPACK_SHELL_INDEX;
size_t dlzmaPack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize);
size_t dlzmaUnpack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize);
typedef struct _DLZMA_HEADER
{
size_t RawDataSize;
/
/
原始数据尺寸(不含此头)
size_t DataSize;
/
/
压缩后的数据大小
char LzmaProps[LZMA_PROPS_SIZE];
/
/
原始lzma的文件头
}DLZMA_HEADER,
*
PDLZMA_HEADER;
/
/
此处外围添加适用于dpack的lzma头
typedef struct _DPACK_ORGPE_INDEX
/
/
源程序被隐去的信息,此结构为明文表示,地址全是rva
{
ULONGLONG ImageBase;
/
/
源程序基址
DWORD ImageBase;
/
/
源程序基址
DWORD OepRva;
/
/
原程序rva入口
DWORD ImportRva;
/
/
导入表信息
DWORD ImportSize;
}DPACK_ORGPE_INDEX,
*
PDPACK_ORGPE_INDEX;
typedef struct _DPACK_SECTION_ENTRY
/
/
源信息与压缩变换后信息索引表是
{
/
/
假设不超过
4g
DWORD OrgRva;
/
/
OrgRva为
0
时则是不解压到原来区段
DWORD OrgSize;
DWORD DpackRva;
DWORD DpackSize;
DWORD Characteristics;
DWORD DpackSectionType;
/
/
dpack区段类型
}DPACK_SECTION_ENTRY,
*
PDPACK_SECTION_ENTRY;
typedef struct _DPACK_SHELL_INDEX
/
/
DPACK变换头
{
union
{
PVOID DpackOepFunc;
/
/
初始化壳的入口函数(放第一个元素方便初始化)
DWORD DpackOepRva;
/
/
加载shellcode后也许改成入口RVA
};
DPACK_ORGPE_INDEX OrgIndex;
WORD SectionNum;
/
/
变换的区段数,最多MAX_DPACKSECTNUM区段
DPACK_SECTION_ENTRY SectionIndex[MAX_DPACKSECTNUM];
/
/
变换区段索引, 以全
0
结尾
PVOID Extra;
/
/
其他信息,方便之后拓展
}DPACK_SHELL_INDEX,
*
PDPACK_SHELL_INDEX;
size_t dlzmaPack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize);
size_t dlzmaUnpack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize);
size_t dlzmaPack(LPBYTE pDstBuf,LPBYTE pSrcBuf,size_t srcSize)
{
size_t dstSize
=
-
1
;
/
/
最大的buffersize, 为
0
会出错
size_t propSize
=
sizeof(DLZMA_HEADER);
PDLZMA_HEADER pDlzmah
=
(PDLZMA_HEADER)pDstBuf;
LzmaCompress(pDstBuf
+
sizeof(DLZMA_HEADER), &dstSize,
pSrcBuf, srcSize,
pDlzmah
-
>LzmaProps, (size_t
*
)&propSize,
-
1
,
0
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
);
pDlzmah
-
>RawDataSize
=
srcSize;
pDlzmah
-
>DataSize
=
dstSize;
return
dstSize;
}
size_t dlzmaUnpack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize)
{
PDLZMA_HEADER pdlzmah
=
(PDLZMA_HEADER)pSrcBuf;
size_t dstSize
=
pdlzmah
-
>RawDataSize;
/
/
release版不赋初值会出错,由于debug将其赋值为cccccccc很大的数
LzmaUncompress(pDstBuf, &dstSize,
/
/
此处必须赋最大值
pSrcBuf
+
sizeof(DLZMA_HEADER), &srcSize,
pdlzmah
-
>LzmaProps, LZMA_PROPS_SIZE);
return
dstSize;
}
size_t dlzmaPack(LPBYTE pDstBuf,LPBYTE pSrcBuf,size_t srcSize)
{
size_t dstSize
=
-
1
;
/
/
最大的buffersize, 为
0
会出错
size_t propSize
=
sizeof(DLZMA_HEADER);
PDLZMA_HEADER pDlzmah
=
(PDLZMA_HEADER)pDstBuf;
LzmaCompress(pDstBuf
+
sizeof(DLZMA_HEADER), &dstSize,
pSrcBuf, srcSize,
pDlzmah
-
>LzmaProps, (size_t
*
)&propSize,
-
1
,
0
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
);
pDlzmah
-
>RawDataSize
=
srcSize;
pDlzmah
-
>DataSize
=
dstSize;
return
dstSize;
}
size_t dlzmaUnpack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize)
{
PDLZMA_HEADER pdlzmah
=
(PDLZMA_HEADER)pSrcBuf;
size_t dstSize
=
pdlzmah
-
>RawDataSize;
/
/
release版不赋初值会出错,由于debug将其赋值为cccccccc很大的数
LzmaUncompress(pDstBuf, &dstSize,
/
/
此处必须赋最大值
pSrcBuf
+
sizeof(DLZMA_HEADER), &srcSize,
pdlzmah
-
>LzmaProps, LZMA_PROPS_SIZE);
return
dstSize;
}
void dpackStart()
__declspec(naked) void dpackStart()
/
/
此函数中不要有局部变量
{
BeforeUnpack();
MallocAll(NULL);
UnpackAll(NULL);
g_orgOep
=
g_dpackShellIndex.OrgIndex.ImageBase
+
g_dpackShellIndex.OrgIndex.OepRva;
LoadOrigionIat(NULL);
AfterUnpack();
JmpOrgOep();
}
void dpackStart()
__declspec(naked) void dpackStart()
/
/
此函数中不要有局部变量
{
BeforeUnpack();
MallocAll(NULL);
UnpackAll(NULL);
g_orgOep
=
g_dpackShellIndex.OrgIndex.ImageBase
+
g_dpackShellIndex.OrgIndex.OepRva;
LoadOrigionIat(NULL);
AfterUnpack();
JmpOrgOep();
}
void MallocAll(PVOID arg)
{
MEMORY_BASIC_INFORMATION mi
=
{
0
};
HANDLE hProcess
=
GetCurrentProcess();
HMODULE imagebase
=
GetModuleHandle(NULL);
for
(
int
i
=
0
; i < g_dpackShellIndex.SectionNum; i
+
+
)
{
if
(g_dpackShellIndex.SectionIndex[i].OrgSize
=
=
0
)
continue
;
LPBYTE tVa
=
(LPBYTE)imagebase
+
g_dpackShellIndex.SectionIndex[i].OrgRva;
DWORD tSize
=
g_dpackShellIndex.SectionIndex[i].OrgSize;
VirtualQueryEx(hProcess, tVa, &mi, tSize);
if
(mi.State
=
=
MEM_FREE)
{
DWORD flProtect
=
PAGE_EXECUTE_READWRITE;
switch (g_dpackShellIndex.SectionIndex[i].Characteristics)
{
case IMAGE_SCN_MEM_EXECUTE:
flProtect
=
PAGE_EXECUTE;
break
;
case IMAGE_SCN_MEM_READ:
flProtect
=
PAGE_READONLY;
break
;
case IMAGE_SCN_MEM_WRITE:
flProtect
=
PAGE_READWRITE;
break
;
}
if
(!VirtualAllocEx(hProcess, tVa, tSize, MEM_COMMIT, flProtect))
{
MessageBox(NULL,
"Alloc memory failed"
,
"error"
, NULL);
ExitProcess(
1
);
}
}
}
}
void MallocAll(PVOID arg)
{
MEMORY_BASIC_INFORMATION mi
=
{
0
};
HANDLE hProcess
=
GetCurrentProcess();
HMODULE imagebase
=
GetModuleHandle(NULL);
for
(
int
i
=
0
; i < g_dpackShellIndex.SectionNum; i
+
+
)
{
if
(g_dpackShellIndex.SectionIndex[i].OrgSize
=
=
0
)
continue
;
LPBYTE tVa
=
(LPBYTE)imagebase
+
g_dpackShellIndex.SectionIndex[i].OrgRva;
DWORD tSize
=
g_dpackShellIndex.SectionIndex[i].OrgSize;
VirtualQueryEx(hProcess, tVa, &mi, tSize);
if
(mi.State
=
=
MEM_FREE)
{
DWORD flProtect
=
PAGE_EXECUTE_READWRITE;
switch (g_dpackShellIndex.SectionIndex[i].Characteristics)
{
case IMAGE_SCN_MEM_EXECUTE:
flProtect
=
PAGE_EXECUTE;
break
;
case IMAGE_SCN_MEM_READ:
flProtect
=
PAGE_READONLY;
break
;
case IMAGE_SCN_MEM_WRITE:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!