|
您遇到的问题涉及多个方面,我将逐一分析并提供解决方案:
### 问题1:函数大小获取错误
错误 `LNK2001: 无法解析的外部符号` 是因为没有正确定义 `finddgStart` 和 `finddgEnd` 符号。以下是正确实现方式:
- // 在头文件中声明
- extern "C" void finddg();
- extern "C" __declspec(selectany) const BYTE* finddgStart = nullptr;
- extern "C" __declspec(selectany) const BYTE* finddgEnd = nullptr;
- // 在函数实现文件中
- #pragma code_seg(".finddg_sec")
- #pragma optimize("", off) // 禁用优化确保函数边界准确
- // 函数实现
- __declspec(allocate(".finddg_sec"))
- void finddg() {
- // 您的函数代码
- }
- // 标记函数边界
- __declspec(allocate(".finddg_sec"))
- static const BYTE _finddgStart = 0;
- __declspec(allocate(".finddg_sec"))
- static const BYTE _finddgEnd = 0;
- #pragma code_seg() // 恢复默认代码段
- #pragma optimize("", on)
- // 初始化边界指针
- const BYTE* finddgStart = &_finddgStart;
- const BYTE* finddgEnd = &_finddgEnd;
- // 获取函数大小
- DWORD GetFinddgSize() {
- return (DWORD)(finddgEnd - finddgStart);
- }
复制代码
### 问题2:程序闪退和无法运行
主要原因是PE文件修改不完整,需要完整处理以下方面:
#### 修正后的完整注入代码
- BOOL PEFile::AddFunction(LPVOID pFunc, DWORD dwFuncSize) {
- if (!pNtHeaders) return FALSE;
- // 1. 查找合适节区(优先使用可执行节区)
- IMAGE_SECTION_HEADER* pSection = IMAGE_FIRST_SECTION(pNtHeaders);
- IMAGE_SECTION_HEADER* pTargetSection = nullptr;
- DWORD dwSpaceRVA = 0;
- for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i, ++pSection) {
- // 优先选择可执行节区
- if (!(pSection->Characteristics & IMAGE_SCN_MEM_EXECUTE)) continue;
-
- DWORD dwFreeSpace = pSection->SizeOfRawData - pSection->Misc.VirtualSize;
- if (dwFreeSpace >= dwFuncSize + 14) {
- dwSpaceRVA = pSection->VirtualAddress + pSection->Misc.VirtualSize;
- pTargetSection = pSection;
- break;
- }
- }
- // 2. 没有合适节区则使用.text节区
- if (!pTargetSection) {
- pSection = IMAGE_FIRST_SECTION(pNtHeaders);
- for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i, ++pSection) {
- if (strcmp((const char*)pSection->Name, ".text") == 0) {
- DWORD dwFreeSpace = pSection->SizeOfRawData - pSection->Misc.VirtualSize;
- if (dwFreeSpace >= dwFuncSize + 14) {
- dwSpaceRVA = pSection->VirtualAddress + pSection->Misc.VirtualSize;
- pTargetSection = pSection;
- break;
- }
- }
- }
- }
- if (!pTargetSection) return FALSE;
- // 3. 计算物理偏移
- DWORD dwWriteOffset = pTargetSection->PointerToRawData +
- (dwSpaceRVA - pTargetSection->VirtualAddress);
- // 4. 扩展文件大小(如果需要)
- DWORD dwRequiredSize = dwWriteOffset + dwFuncSize + 14;
- DWORD dwFileSize = GetFileSize(hDstFile, NULL);
-
- if (dwRequiredSize > dwFileSize) {
- SetFilePointer(hDstFile, dwRequiredSize, NULL, FILE_BEGIN);
- SetEndOfFile(hDstFile);
-
- // 重新映射文件视图
- UnmapViewOfFile(pBase);
- CloseHandle(hMap);
-
- hMap = CreateFileMapping(hDstFile, NULL, PAGE_READWRITE, 0, 0, NULL);
- pBase = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);
-
- // 重新获取NT头
- IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER*)pBase;
- pNtHeaders = (IMAGE_NT_HEADERS64*)((BYTE*)pBase + pDos->e_lfanew);
- pTargetSection = IMAGE_FIRST_SECTION(pNtHeaders) +
- (pTargetSection - IMAGE_FIRST_SECTION(pNtHeaders));
- }
- // 5. 构造跳转指令(64位)
- BYTE jmpCode[] = {
- 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP [RIP+0]
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 目标地址
- };
-
- // 计算原始入口点
- ULONGLONG ullOldEntry = pNtHeaders->OptionalHeader.ImageBase +
- pNtHeaders->OptionalHeader.AddressOfEntryPoint;
-
- // 设置跳转目标地址
- memcpy(jmpCode + 6, &ullOldEntry, 8);
- // 6. 写入函数和跳转指令
- memcpy((BYTE*)pBase + dwWriteOffset, pFunc, dwFuncSize);
- memcpy((BYTE*)pBase + dwWriteOffset + dwFuncSize, jmpCode, sizeof(jmpCode));
- // 7. 更新PE头信息
- // 更新节区虚拟大小
- pTargetSection->Misc.VirtualSize += dwFuncSize + sizeof(jmpCode);
-
- // 更新入口点
- DWORD dwOldEntry = pNtHeaders->OptionalHeader.AddressOfEntryPoint;
- pNtHeaders->OptionalHeader.AddressOfEntryPoint = dwSpaceRVA;
-
- // 更新映像大小
- DWORD dwNewImageSize = pTargetSection->VirtualAddress +
- pTargetSection->Misc.VirtualSize;
- dwNewImageSize = (dwNewImageSize + pNtHeaders->OptionalHeader.SectionAlignment - 1) /
- pNtHeaders->OptionalHeader.SectionAlignment *
- pNtHeaders->OptionalHeader.SectionAlignment;
-
- if (dwNewImageSize > pNtHeaders->OptionalHeader.SizeOfImage) {
- pNtHeaders->OptionalHeader.SizeOfImage = dwNewImageSize;
- }
-
- // 清除校验和
- pNtHeaders->OptionalHeader.CheckSum = 0;
-
- // 8. 设置内存保护属性
- DWORD dwOldProtect;
- VirtualProtect((BYTE*)pBase + dwWriteOffset,
- dwFuncSize + sizeof(jmpCode),
- PAGE_EXECUTE_READWRITE,
- &dwOldProtect);
- return TRUE;
- }
复制代码
### 关键修复点说明
1. **节区选择策略优化**:
- 优先选择可执行节区(`IMAGE_SCN_MEM_EXECUTE`)
- 次选`.text`节区(主代码节)
- 避免使用只读数据节区
2. **文件扩展处理**:
- // 扩展文件后必须重新映射视图
- UnmapViewOfFile(pBase);
- CloseHandle(hMap);
- hMap = CreateFileMapping(hDstFile, ...);
- pBase = MapViewOfFile(...);
复制代码
3. **64位跳转指令修正**:
- BYTE jmpCode[] = {
- 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP [RIP+0]
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 绝对地址
- };
复制代码
这是64位模式下正确的绝对跳转指令格式
4. **映像大小更新**:
- DWORD dwNewImageSize = (pTargetSection->VirtualAddress +
- pTargetSection->Misc.VirtualSize);
- dwNewImageSize = ALIGN_UP(dwNewImageSize,
- pNtHeaders->OptionalHeader.SectionAlignment);
复制代码
### 验证和调试建议
1. **使用PE查看工具**:
- 用CFF Explorer或PEView检查修改后的文件
- 确认:
* 入口点地址已更新
* 目标节区的VirtualSize已增加
* SizeOfImage已正确更新
2. **调试注入的代码**:
- // 在注入代码前添加调试指令
- BYTE debugCode[] = {
- 0xCC, // INT 3 (断点)
- 0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00, // MOV RAX, 1
- 0x48, 0x83, 0xF8, 0x00 // CMP RAX, 0
- };
- memcpy((BYTE*)pBase + dwWriteOffset, debugCode, sizeof(debugCode));
复制代码
然后使用调试器附加到目标进程
3. **最小化函数大小**:
- // 测试用最小函数
- __declspec(noinline) void finddg() {
- __nop();
- __nop();
- __nop();
- }
复制代码
4. **错误检查**:
- if (pNtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
- MessageBox(NULL, L"不是64位PE文件", L"错误", MB_ICONERROR);
- return FALSE;
- }
复制代码
### 注意事项
1. **数字签名**:
- 修改后的文件会破坏数字签名
- 测试时关闭Windows的驱动签名强制
2. **DEP保护**:
- 确保注入的代码所在内存区域具有可执行权限
- 使用`VirtualProtect`设置`PAGE_EXECUTE_READWRITE`
3. **系统DLL**:
- 不要修改系统关键DLL(如kernel32.dll)
- 测试时使用自定义DLL
4. **防病毒软件**:
- 临时禁用防病毒软件,它们会拦截PE文件修改
如果问题仍然存在,建议分阶段验证:
1. 先验证小函数(<0x50字节)注入
2. 确认文件修改后能正确执行跳转指令
3. 逐步增加函数大小,观察失败点
4. 使用调试器分析崩溃时的寄存器状态
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 |
|