# VMProtect PE 文件处理实现详解

## 1. PE 段管理
### PESegment 类实现
“`cpp
class PESegment : public BaseSection {
public:
PESegment(PESegmentList *owner);
PESegment(PESegmentList *owner, uint64_t address, uint32_t size,
uint32_t physical_offset, uint32_t physical_size,
uint32_t flags, const std::string &name);
PESegment(PESegmentList *owner, const PESegment &src);
PESegment *Clone(ISectionList *owner) const;
// 从文件读取
void ReadFromFile(PEArchitecture &file);
// 写入文件
void WriteToFile(PEArchitecture &file) const;
// 属性
uint64_t address() const { return address_; }
uint32_t size() const { return size_; }
uint32_t physical_offset() const { return physical_offset_; }
uint32_t physical_size() const { return physical_size_; }
uint32_t flags() const { return flags_; }
const std::string &name() const { return name_; }
// 内存类型转换
uint32_t memory_type() const;
void update_type(uint32_t mt);
// 重定位
void Rebase(uint64_t delta_base);
private:
std::string name_; // 段名称
uint64_t address_; // 虚拟地址
uint32_t size_; // 虚拟大小
uint32_t physical_offset_; // 文件偏移
uint32_t physical_size_; // 文件大小
uint32_t flags_; // 段特征
};
“`
### 从文件读取段
“`cpp
void PESegment::ReadFromFile(PEArchitecture &file) {
IMAGE_SECTION_HEADER section_header;
file.Read(§ion_header, sizeof(section_header));
// 读取段名(最多8字符)
name_ = std::string(
reinterpret_cast<char *>(§ion_header.Name),
strnlen(reinterpret_cast<char *>(§ion_header.Name),
sizeof(section_header.Name))
);
// 虚拟大小和地址
size_ = section_header.Misc.VirtualSize;
address_ = section_header.VirtualAddress + file.image_base();
// 文件偏移和大小
physical_offset_ = section_header.PointerToRawData;
physical_size_ = section_header.SizeOfRawData;
// 段特征
flags_ = section_header.Characteristics;
}
“`
### 写入段到文件
“`cpp
void PESegment::WriteToFile(PEArchitecture &file) const {
IMAGE_SECTION_HEADER section_header = IMAGE_SECTION_HEADER();
// 写入段名
memcpy(section_header.Name, name_.c_str(),
std::min(name_.size(), sizeof(section_header.Name)));
// 虚拟大小和地址
section_header.Misc.VirtualSize = size_;
section_header.VirtualAddress = static_cast<uint32_t>(
address_ – file.image_base()
);
// 文件偏移和大小
section_header.PointerToRawData = physical_size_ ? physical_offset_ : 0;
section_header.SizeOfRawData = physical_size_;
// 段特征
section_header.Characteristics = flags_;
file.Write(§ion_header, sizeof(section_header));
}
“`
### 内存类型转换
“`cpp
uint32_t PESegment::memory_type() const {
uint32_t res = mtNone;
if (flags_ & IMAGE_SCN_MEM_READ)
res |= mtReadable;
if (flags_ & IMAGE_SCN_MEM_WRITE)
res |= mtWritable;
if (flags_ & IMAGE_SCN_MEM_EXECUTE)
res |= mtExecutable;
if (flags_ & IMAGE_SCN_MEM_DISCARDABLE)
res |= mtDiscardable;
if (flags_ & IMAGE_SCN_MEM_NOT_PAGED)
res |= mtNotPaged;
if (flags_ & IMAGE_SCN_MEM_SHARED)
res |= mtShared;
return res;
}
void PESegment::update_type(uint32_t mt) {
if (mt & mtReadable) {
flags_ |= IMAGE_SCN_MEM_READ;
if ((mt & mtExecutable) == 0)
flags_ |= IMAGE_SCN_CNT_INITIALIZED_DATA;
}
if (mt & mtWritable)
flags_ |= IMAGE_SCN_MEM_WRITE;
if (mt & mtExecutable)
flags_ |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE;
if ((mt & (mtDiscardable | mtNotDiscardable)) == mtDiscardable)
flags_ |= IMAGE_SCN_MEM_DISCARDABLE;
else
flags_ &= ~IMAGE_SCN_MEM_DISCARDABLE;
if (mt & mtNotPaged)
flags_ |= IMAGE_SCN_MEM_NOT_PAGED;
if (mt & mtShared)
flags_ |= IMAGE_SCN_MEM_SHARED;
}
“`
## 2. PE 目录管理
### PEDirectory 类实现
“`cpp
class PEDirectory : public BaseLoadCommand {
public:
PEDirectory(PEDirectoryList *owner, uint32_t type);
PEDirectory(PEDirectoryList *owner, const PEDirectory &src);
PEDirectory *Clone(ILoadCommandList *owner) const;
void clear();
// 从文件读取
void ReadFromFile(PEArchitecture &file);
// 写入文件
void WriteToFile(PEArchitecture &file) const;
// 属性
uint64_t address() const { return address_; }
uint32_t size() const { return size_; }
uint32_t type() const { return type_; }
uint32_t physical_size() const { return physical_size_; }
std::string name() const;
// 重定位
void Rebase(uint64_t delta_base);
// 内存管理
void FreeByManager(MemoryManager &manager);
private:
uint64_t address_; // 虚拟地址
uint32_t size_; // 大小
uint32_t type_; // 目录类型
uint32_t physical_size_; // 物理大小
};
“`
### 读取目录
“`cpp
void PEDirectory::ReadFromFile(PEArchitecture &file) {
IMAGE_DATA_DIRECTORY dir;
file.Read(&dir, sizeof(IMAGE_DATA_DIRECTORY));
// 转换虚拟地址为绝对地址
address_ = (dir.VirtualAddress == 0) ? 0 :
dir.VirtualAddress + file.image_base();
size_ = dir.Size;
}
“`
### 写入目录
“`cpp
void PEDirectory::WriteToFile(PEArchitecture &file) const {
IMAGE_DATA_DIRECTORY dir;
dir.VirtualAddress = address_ ?
static_cast<uint32_t>(address_ – file.image_base()) : 0;
dir.Size = size_;
file.Write(&dir, sizeof(IMAGE_DATA_DIRECTORY));
}
“`
### 目录名称
“`cpp
std::string PEDirectory::name() const {
switch (type_) {
case IMAGE_DIRECTORY_ENTRY_EXPORT:
return std::string(“Export”);
case IMAGE_DIRECTORY_ENTRY_IMPORT:
return std::string(“Import”);
case IMAGE_DIRECTORY_ENTRY_RESOURCE:
return std::string(“Resource”);
case IMAGE_DIRECTORY_ENTRY_EXCEPTION:
return std::string(“Exception”);
case IMAGE_DIRECTORY_ENTRY_SECURITY:
return std::string(“Security”);
case IMAGE_DIRECTORY_ENTRY_BASERELOC:
return std::string(“Relocation”);
case IMAGE_DIRECTORY_ENTRY_DEBUG:
return std::string(“Debug”);
case IMAGE_DIRECTORY_ENTRY_ARCHITECTURE:
return std::string(“Architecture”);
case IMAGE_DIRECTORY_ENTRY_GLOBALPTR:
return std::string(“Reserved”);
case IMAGE_DIRECTORY_ENTRY_TLS:
return std::string(“Thread Local Storage”);
case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG:
return std::string(“Configuration”);
case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT:
return std::string(“Bound Import”);
case IMAGE_DIRECTORY_ENTRY_IAT:
return std::string(“Import Address Table”);
case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT:
return std::string(“Delay Import”);
case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:
return std::string(“.NET MetaData”);
}
return BaseLoadCommand::name();
}
“`
## 3. 导入表处理
### 导入描述符
“`cpp
class PEImport : public BaseImport {
public:
PEImport(PEImportList *owner);
PEImport(PEImportList *owner, const std::string &name);
void ReadFromFile(PEArchitecture &file, IMAGE_IMPORT_DESCRIPTOR &descriptor);
void WriteToFile(PEArchitecture &file);
// 编译导入表
void Compile(PEArchitecture &file);
private:
uint32_t original_first_thunk_; // 原始 First Thunk
uint32_t time_stamp_; // 时间戳
uint32_t forwarder_chain_; // 转发链
uint32_t name_rva_; // 名称 RVA
uint32_t first_thunk_; // First Thunk
};
“`
### 读取导入表
“`cpp
void PEImport::ReadFromFile(PEArchitecture &file,
IMAGE_IMPORT_DESCRIPTOR &descriptor) {
original_first_thunk_ = descriptor.OriginalFirstThunk;
time_stamp_ = descriptor.TimeDateStamp;
forwarder_chain_ = descriptor.ForwarderChain;
name_rva_ = descriptor.Name;
first_thunk_ = descriptor.FirstThunk;
// 读取 DLL 名称
uint64_t name_addr = name_rva_ + file.image_base();
name_ = file.ReadString(name_addr);
// 读取导入函数
uint64_t thunk_addr = (original_first_thunk_ ?
original_first_thunk_ : first_thunk_) +
file.image_base();
while (true) {
uint64_t thunk = file.ReadQWord(thunk_addr);
if (thunk == 0) break;
if (thunk & IMAGE_ORDINAL_FLAG64) {
// 按序号导入
uint16_t ordinal = thunk & 0xFFFF;
AddFunction(ordinal);
} else {
// 按名称导入
uint64_t hint_name_addr = (thunk & ~IMAGE_ORDINAL_FLAG64) +
file.image_base();
uint16_t hint = file.ReadWord(hint_name_addr);
std::string func_name = file.ReadString(hint_name_addr + 2);
AddFunction(func_name, hint);
}
thunk_addr += sizeof(uint64_t);
}
}
“`
### 保护导入表
“`cpp
void PEImport::Compile(PEArchitecture &file) {
if (!IsOptionEnabled(cpImportProtection))
return;
// 1. 保存原始导入信息
original_imports_ = *this;
// 2. 创建新的导入表(仅包含必要的 API)
ClearFunctions();
// 3. 添加加载器需要的 API
AddFunction(“LoadLibraryA”);
AddFunction(“GetProcAddress”);
// 4. 运行时动态解析其他 API
for (auto &func : original_imports_.functions()) {
runtime_resolver_.AddFunction(func.name(), func.hint());
}
}
“`
## 4. 导出表处理
### 导出目录处理
“`cpp
void PEExport::ReadFromFile(PEArchitecture &file) {
IMAGE_EXPORT_DIRECTORY export_dir;
file.Read(&export_dir, sizeof(export_dir));
// 读取 DLL 名称
uint64_t name_addr = export_dir.Name + file.image_base();
name_ = file.ReadString(name_addr);
// 读取导出函数
uint64_t functions_addr = export_dir.AddressOfFunctions + file.image_base();
uint64_t names_addr = export_dir.AddressOfNames + file.image_base();
uint64_t ordinals_addr = export_dir.AddressOfNameOrdinals + file.image_base();
for (uint32_t i = 0; i < export_dir.NumberOfFunctions; i++) {
uint32_t func_rva = file.ReadDWord(functions_addr + i * 4);
// 查找函数名
std::string func_name;
for (uint32_t j = 0; j < export_dir.NumberOfNames; j++) {
uint16_t ordinal = file.ReadWord(ordinals_addr + j * 2);
if (ordinal == i) {
uint32_t name_rva = file.ReadDWord(names_addr + j * 4);
func_name = file.ReadString(name_rva + file.image_base());
break;
}
}
AddExport(func_name, func_rva, export_dir.Base + i);
}
}
“`
## 5. 重定位处理
### 重定位块处理
“`cpp
class PERelocation : public BaseRelocation {
public:
void ReadFromFile(PEArchitecture &file, uint64_t address, uint16_t type);
void WriteToFile(PEArchitecture &file);
// 应用重定位
void Apply(PEArchitecture &file, uint64_t delta_base);
};
void PERelocation::ReadFromFile(PEArchitecture &file,
uint64_t address, uint16_t type) {
address_ = address;
type_ = static_cast<RelocationType>(type);
}
void PERelocation::Apply(PEArchitecture &file, uint64_t delta_base) {
switch (type_) {
case rtHighLow:
// 32位重定位
{
uint32_t value = file.ReadDWord(address_);
value += static_cast<uint32_t>(delta_base);
file.WriteDWord(address_, value);
}
break;
case rtDir64:
// 64位重定位
{
uint64_t value = file.ReadQWord(address_);
value += delta_base;
file.WriteQWord(address_, value);
}
break;
case rtRel32:
// 32位相对重定位
{
uint32_t value = file.ReadDWord(address_);
value += static_cast<uint32_t>(delta_base);
file.WriteDWord(address_, value);
}
break;
}
}
“`
## 6. 资源处理
### 资源目录解析
“`cpp
class PEResource : public BaseResource {
public:
void ReadFromFile(PEArchitecture &file, uint64_t address,
uint32_t level = 0);
void WriteToFile(PEArchitecture &file);
// 加密资源
void Encrypt(ICryptor *cryptor);
void Decrypt(ICryptor *cryptor);
};
void PEResource::ReadFromFile(PEArchitecture &file, uint64_t address,
uint32_t level) {
IMAGE_RESOURCE_DIRECTORY dir;
file.Read(&dir, sizeof(dir));
uint64_t entry_addr = address + sizeof(dir);
for (uint32_t i = 0; i < dir.NumberOfNamedEntries +
dir.NumberOfIdEntries; i++) {
IMAGE_RESOURCE_DIRECTORY_ENTRY entry;
file.Read(&entry, sizeof(entry));
if (entry.DataIsDirectory) {
// 子目录
uint64_t subdir_addr = (entry.OffsetToDirectory & 0x7FFFFFFF) +
resource_base_;
ReadFromFile(file, subdir_addr, level + 1);
} else {
// 数据项
uint64_t data_entry_addr = entry.OffsetToData + resource_base_;
IMAGE_RESOURCE_DATA_ENTRY data_entry;
file.Read(&data_entry, sizeof(data_entry));
// 读取资源数据
uint64_t data_addr = data_entry.OffsetToData + file.image_base();
Data resource_data;
file.Read(data_addr, data_entry.Size, resource_data);
AddResource(entry.Name, resource_data);
}
}
}
“`
### 资源加密
“`cpp
void PEResource::Encrypt(ICryptor *cryptor) {
if (!IsOptionEnabled(cpResourceProtection))
return;
for (auto &res : resources_) {
// 加密资源数据
Data encrypted;
cryptor->Encrypt(res.data(), encrypted);
res.set_data(encrypted);
// 标记为加密
res.set_encrypted(true);
}
}
“`
## 7. TLS 回调处理
### TLS 目录解析
“`cpp
class PETLS : public BaseTLS {
public:
void ReadFromFile(PEArchitecture &file);
void WriteToFile(PEArchitecture &file);
// 添加运行时 TLS 回调
void AddRuntimeCallback(uint64_t callback);
};
void PETLS::ReadFromFile(PEArchitecture &file) {
IMAGE_TLS_DIRECTORY64 tls_dir;
file.Read(&tls_dir, sizeof(tls_dir));
start_address_ = tls_dir.StartAddressOfRawData;
end_address_ = tls_dir.EndAddressOfRawData;
index_address_ = tls_dir.AddressOfIndex;
callbacks_address_ = tls_dir.AddressOfCallBacks;
size_of_zero_fill_ = tls_dir.SizeOfZeroFill;
characteristics_ = tls_dir.Characteristics;
// 读取回调函数
if (callbacks_address_) {
uint64_t callback_addr = callbacks_address_;
while (true) {
uint64_t callback = file.ReadQWord(callback_addr);
if (callback == 0) break;
callbacks_.push_back(callback);
callback_addr += sizeof(uint64_t);
}
}
}
“`
## 8. 运行时集成
### 嵌入运行时
“`cpp
void PEArchitecture::EmbedRuntime() {
// 1. 选择运行时
const uint8_t *runtime_data;
size_t runtime_size;
#ifdef DEMO
if (is_x64()) {
runtime_data = win_runtime64demo_dll;
runtime_size = sizeof(win_runtime64demo_dll);
} else {
runtime_data = win_runtime32demo_dll;
runtime_size = sizeof(win_runtime32demo_dll);
}
#else
if (is_x64()) {
runtime_data = win_runtime64_dll;
runtime_size = sizeof(win_runtime64_dll);
} else {
runtime_data = win_runtime32_dll;
runtime_size = sizeof(win_runtime32_dll);
}
#endif
// 2. 解密运行时(如果加密存储)
Data decrypted_runtime;
DecryptRuntime(runtime_data, runtime_size, decrypted_runtime);
// 3. 创建新段
PESegment *runtime_segment = segment_list()->Add(
0, // 地址(稍后分配)
decrypted_runtime.size(), // 大小
0, // 文件偏移(稍后分配)
decrypted_runtime.size(), // 文件大小
IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE,
“.vmp” // 段名
);
// 4. 写入运行时数据
WriteSegmentData(runtime_segment, decrypted_runtime);
// 5. 添加运行时导入
AddRuntimeImports();
}
“`
### 修改入口点
“`cpp
void PEArchitecture::RedirectEntryPoint() {
// 1. 保存原始入口点
original_entry_point_ = entry_point_;
// 2. 创建跳转代码
Data trampoline;
// pushad/pushfd (32位) 或 push rax-rbx (64位)
if (is_x64()) {
// 保存寄存器
trampoline.PushByte(0x50); // push rax
trampoline.PushByte(0x53); // push rbx
// … 其他寄存器
} else {
trampoline.PushByte(0x60); // pushad
trampoline.PushByte(0x9C); // pushfd
}
// 调用运行时初始化
if (is_x64()) {
// mov rax, runtime_init
trampoline.PushByte(0x48);
trampoline.PushByte(0xB8);
trampoline.PushQWord(runtime_init_address_);
// call rax
trampoline.PushByte(0xFF);
trampoline.PushByte(0xD0);
} else {
// call runtime_init
trampoline.PushByte(0xE8);
int32_t rel = runtime_init_address_ –
(image_base_ + new_entry_point_ + trampoline.size() + 4);
trampoline.PushDWord(rel);
}
// 恢复寄存器
if (is_x64()) {
// pop rbx-rax
trampoline.PushByte(0x5B);
trampoline.PushByte(0x58);
} else {
trampoline.PushByte(0x9D); // popfd
trampoline.PushByte(0x61); // popad
}
// 跳转到原始入口点
trampoline.PushByte(0xE9);
int32_t jmp_rel = original_entry_point_ –
(image_base_ + new_entry_point_ + trampoline.size() + 4);
trampoline.PushDWord(jmp_rel);
// 3. 写入跳转代码
WriteCode(new_entry_point_, trampoline);
// 4. 更新入口点
entry_point_ = new_entry_point_;
}
“`
## 9. 文件保存
### 保存流程
“`cpp
bool PEArchitecture::Save(const char *filename) {
// 1. 打开文件
FILE *file = fopen(filename, “wb”);
if (!file) return false;
// 2. 对齐段
AlignSegments();
// 3. 更新头信息
UpdateHeaders();
// 4. 写入 DOS 头
WriteDOSHeader(file);
// 5. 写入 PE 签名
WritePESignature(file);
// 6. 写入 COFF 头
WriteCOFFHeader(file);
// 7. 写入可选头
WriteOptionalHeader(file);
// 8. 写入数据目录
WriteDataDirectories(file);
// 9. 写入段表
WriteSectionTable(file);
// 10. 写入段数据
for (auto &segment : segments_) {
WriteSegmentData(file, segment);
}
// 11. 关闭文件
fclose(file);
return true;
}
“`
### 对齐计算
“`cpp
void PEArchitecture::AlignSegments() {
uint32_t file_alignment = optional_header_.FileAlignment;
uint32_t section_alignment = optional_header_.SectionAlignment;
uint32_t current_file_offset =
dos_header_.e_lfanew +
sizeof(uint32_t) + // PE 签名
sizeof(IMAGE_FILE_HEADER) +
file_header_.SizeOfOptionalHeader +
sizeof(IMAGE_SECTION_HEADER) * segment_list()->count();
// 对齐到文件对齐边界
current_file_offset = AlignUp(current_file_offset, file_alignment);
uint64_t current_virtual_address =
AlignUp(optional_header_.ImageBase +
optional_header_.SizeOfHeaders, section_alignment);
for (size_t i = 0; i < segment_list()->count(); i++) {
PESegment *segment = segment_list()->item(i);
// 设置虚拟地址
segment->set_address(current_virtual_address);
current_virtual_address = AlignUp(
current_virtual_address + segment->size(), section_alignment);
// 设置文件偏移
if (segment->physical_size() > 0) {
segment->set_physical_offset(current_file_offset);
current_file_offset = AlignUp(
current_file_offset + segment->physical_size(), file_alignment);
}
}
// 更新镜像大小
optional_header_.SizeOfImage =
static_cast<uint32_t>(current_virtual_address – image_base_);
}
“`














暂无评论内容