外壳编写基础

加壳主程序

判断文件是否为PE文件

  1. 根据文件开始两个字节是否为”MZ”来判断文件否为DOS文件:
1
2
3
4
5
dos_header = (PIMAGE_DOS_HEADER)file_image_base;
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
WARNF("错误!目标文件DOS头校验失败!");
return FALSE;
}
  1. 根据PE header中的Signature是否为”PE”来判断DOS文件是否为PE文件:
1
2
3
4
5
nt_header = (PIMAGE_NT_HEADERS32)((DWORD)dos_header+dos_header->e_lfanew);
if (nt_header->Signature != IMAGE_NT_SIGNATURE) {
WARNF("错误!目标文件PE头校验失败!");
return FALSE;
}
  1. 如果PE文件只有一个区段,说明PE文件加壳来:
1
2
3
4
5
file_header = &nt_header->FileHeader;
if (file_header->NumberOfSections == 1) {
WARNF("错误!目标文件可能已经被压缩!");
return FALSE;
}
  1. 如果PE文件的入口点的值大于第二个区段的虚拟地址也说明PE文件被加壳了:
1
2
3
4
5
6
pSecHeader=IMAGE_FIRST_SECTION(pNtHeader);
pSecHeader++;//得到第二个区块的起始地址
if((pOptHeader->AddressOfEntryPoint) > (pSecHeader->VirtualAddress)){
AddLine(hDlg,"文件可能已被压缩,放弃!");
return FALSE;
}
  1. 判断是EXE文件还是DLL文件:
1
2
3
4
5
6
if (((file_header->Characteristics)&IMAGE_FILE_DLL) != 0) {
ACTF("是PE—DLL文件,可以压缩。");
}
else {
ACTF("是PE—EXE文件,可以压缩。");
}

读取PE文件到内存中

根据PE文件的文件结构读取文件。

  1. 读取PE头:
1
2
3
4
5
6
//读取DOS头
ReadFile(file,&dos_header,sizeof(dos_header),&number_of_bytes,NULL);
//定位到PE头起始处e_lfanew,从而定位到PE(NT)头
SetFilePointer(file, dos_header.e_lfanew,NULL,FILE_BEGIN);
//读取NT头
ReadFile(file,&nt_headers,sizeof(nt_headers),&number_of_bytes,NULL);
  1. 获取文件大小等信息:
1
2
3
4
5
6
file_size = GetFileSize(file,NULL);//文件大小
number_of_sections = nt_headers.FileHeader.NumberOfSections;//文件区块数
image_size = nt_headers.OptionalHeader.SizeOfImage;//映像大小
file_align = nt_headers.OptionalHeader.FileAlignment;//文件区块对齐值
section_align = nt_headers.OptionalHeader.SectionAlignment;//内存区块对齐值
header_size = nt_headers.OptionalHeader.SizeOfHeaders;//文件头大小
  1. 将文件的大小向上对齐文件区块对齐值(高斯函数):
1
2
3
4
5
m_image_size = align_size(image_size,section_align);
......
UINT align_size(UINT size,UINT align){
return ((size + align - 1) / align * align);
}
  1. 初始化存放文件映像的内存空间:
1
2
3
4
5
6
7
8
9
10
//申请内存用于保存映像
m_image_base = new char[m_image_size];
if (m_image_base == NULL) {
WARNF("错误!内存申请失败!");
return FALSE;
}
//初始化申请的空间为0
memset(m_image_base,0,m_image_size);
SetFilePointer(file,0,NULL,FILE_BEGIN);
ReadFile(file,m_image_base,header_size,&number_of_bytes,NULL);
  1. 定位PE header等:
1
2
3
4
5
6
//定位nt header
m_nt_headers = (PIMAGE_NT_HEADERS)((DWORD)m_image_base + dos_header.e_lfanew);
//计算nt header大小
nt_header_size = sizeof(nt_headers.FileHeader) + sizeof(nt_headers.Signature) + nt_headers.FileHeader.SizeOfOptionalHeader;
//获取区块表
section_header = (PIMAGE_SECTION_HEADER)((DWORD)m_nt_headers + nt_header_size);
  1. 读取相关数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//获取各区段的内容
for (n_index = 0, cur_section_header = m_section_header; n_index < number_of_sections; ++n_index, ++cur_section_header) {
raw_data_size = cur_section_header->SizeOfRawData;
raw_data_offset = cur_section_header->PointerToRawData;
virtual_address = cur_section_header->VirtualAddress;
virtual_size = cur_section_header->Misc.VirtualSize;
SetFilePointer(file, raw_data_offset, NULL, FILE_BEGIN);
ReadFile(file, &m_image_base[virtual_address], raw_data_size, &number_of_bytes, NULL);
}
ACTF("基本数据读取完毕");

//读取额外数据
if (is_save_data) {
map_of_data_size = file_size - (cur_section_header->PointerToRawData + cur_section_header->SizeOfRawData);
if (map_of_data_size > 0) {
map_of_data = new char[map_of_data_size];
memset(map_of_data, 0, map_of_data_size);
ReadFile(file, map_of_data, map_of_data_size, &number_of_bytes, NULL);
ACTF("额外数据读取完毕。");
}
else {
ACTF("无额外数据。");
}
}
else {
WARNF("不保存额外数据。");
}