You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在执行前解密附加PE程序的汇编指令?含间接寻址疑问

逐指令解密加密.text段的实现方案(含间接寻址处理)

可以实现逐指令解密加密的.text段,你当前用VEH+PAGE_NOACCESS触发异常的思路是可行的,核心是在异常处理中完成「解密指令→单步执行→重新加密」的闭环,间接寻址的情况不需要提前解析目标,靠异常机制就能自动处理。

核心实现逻辑

  • 触发异常:将附加程序的.text段设置为PAGE_NOACCESS,CPU试图执行该区域指令时会抛出EXCEPTION_ACCESS_VIOLATION异常。
  • 解密指令:在VEH中识别异常地址属于.text段后,用反汇编工具(比如Capstone)获取当前指令的长度,解密对应字节。
  • 单步执行:开启CPU的单步标志(EFlags |= 0x100),让CPU执行完当前解密后的指令后抛出EXCEPTION_SINGLE_STEP异常。
  • 重新加密:在单步异常处理中,将刚执行完的指令重新加密,并恢复.text段的PAGE_NOACCESS权限,等待下一次异常触发。

间接寻址的处理方法

像48 FF 25 5A 08 00 00 JMP QWORD PTR [RIP + 0x85A]这类间接寻址指令,不需要提前解析目标地址:

  1. 当VEH处理这条跳转指令的ACCESS_VIOLATION异常时,会解密该跳转指令并允许单步执行。
  2. CPU执行完跳转指令后,会计算出目标地址并试图访问该地址。
  3. 如果目标地址在加密的.text段内,会再次触发ACCESS_VIOLATION异常,VEH重复「解密→执行→加密」的流程,自动处理目标地址的指令。

全程不需要手动解析间接寻址的目标,异常机制会自动捕获所有对加密.text段的访问请求。

修改后的代码示例

依赖准备

需要引入Capstone反汇编库(用于准确获取指令长度),你可以编译官方源码或使用预编译版本。

VEH异常处理函数(含解密/加密逻辑)

#include <capstone/capstone.h>
#include <memory.h>
#include <windows.h>

// 全局变量:保存附加程序.text段的关键信息
PBYTE g_encryptedTextBase;
SIZE_T g_textSize;
BYTE* g_originalEncryptedData;
csh g_csHandle;
PVOID g_lastDecryptedAddr;
SIZE_T g_lastDecryptedLen;

// 示例解密函数(替换为你的实际解密逻辑)
void DecryptInstruction(BYTE* addr, SIZE_T len) {
    const BYTE key = 0xAA; // 替换为你的解密密钥
    for (SIZE_T i = 0; i < len; ++i) {
        addr[i] ^= key;
    }
}

// 重新加密指令(恢复为原始加密状态)
void ReEncryptInstruction(BYTE* addr, SIZE_T len) {
    memcpy(addr, g_originalEncryptedData + (addr - g_encryptedTextBase), len);
}

LONG NTAPI VehHandler(PEXCEPTION_POINTERS pExceptInfo) {
    DWORD exceptionCode = pExceptInfo->ExceptionRecord->ExceptionCode;
    PVOID exceptionAddr = pExceptInfo->ExceptionRecord->ExceptionAddress;

    // 仅处理附加程序.text段内的异常
    if (exceptionAddr >= g_encryptedTextBase && exceptionAddr < (PBYTE)g_encryptedTextBase + g_textSize) {
        if (exceptionCode == EXCEPTION_ACCESS_VIOLATION) {
            // 反汇编获取当前指令长度
            cs_insn* insn = NULL;
            size_t disasmCount = cs_disasm(g_csHandle, (const uint8_t*)exceptionAddr, 15, (uint64_t)exceptionAddr, 1, &insn);
            if (disasmCount == 0) {
                return EXCEPTION_CONTINUE_SEARCH; // 反汇编失败,交给系统处理
            }

            g_lastDecryptedAddr = exceptionAddr;
            g_lastDecryptedLen = insn[0].size;

            // 解密当前指令
            DecryptInstruction((BYTE*)exceptionAddr, g_lastDecryptedLen);

            // 设置内存权限为可执行可读
            DWORD oldProtect;
            VirtualProtect(exceptionAddr, g_lastDecryptedLen, PAGE_EXECUTE_READ, &oldProtect);

            // 开启单步执行
            pExceptInfo->ContextRecord->EFlags |= 0x100;

            cs_free(insn, disasmCount);
            return EXCEPTION_CONTINUE_EXECUTION;
        }
        else if (exceptionCode == EXCEPTION_SINGLE_STEP) {
            // 重新加密刚执行完的指令
            ReEncryptInstruction((BYTE*)g_lastDecryptedAddr, g_lastDecryptedLen);

            // 恢复内存权限为PAGE_NOACCESS
            DWORD oldProtect;
            VirtualProtect(g_lastDecryptedAddr, g_lastDecryptedLen, PAGE_NOACCESS, &oldProtect);

            // 关闭单步执行
            pExceptInfo->ContextRecord->EFlags &= ~0x100;

            return EXCEPTION_CONTINUE_EXECUTION;
        }
    }

    // 其他异常交给系统处理
    return EXCEPTION_CONTINUE_SEARCH;
}

主程序代码

int main(int argc, char** argv) {
    // 初始化Capstone(64位模式,32位程序改为CS_MODE_32)
    if (cs_open(CS_ARCH_X86, CS_MODE_64, &g_csHandle) != CS_ERR_OK) {
        return 1;
    }
    cs_option(g_csHandle, CS_OPT_DETAIL, CS_OPT_ON);

    // 注册VEH
    AddVectoredExceptionHandler(1, &VehHandler);

    // 假设你已完成附加程序的加载、导入表修复、重定位
    // 替换为你实际的.text段地址和大小
    g_encryptedTextBase = (PBYTE)retAddr;
    g_textSize = textsize;

    // 保存加密后的原始数据,用于后续重新加密
    g_originalEncryptedData = (BYTE*)malloc(g_textSize);
    if (!g_originalEncryptedData) {
        cs_close(&g_csHandle);
        return 1;
    }
    memcpy(g_originalEncryptedData, g_encryptedTextBase, g_textSize);

    // 设置.text段为PAGE_NOACCESS
    DWORD oldProtect;
    VirtualProtect(g_encryptedTextBase, g_textSize, PAGE_NOACCESS, &oldProtect);

    // 调用附加程序的入口点
    ((void(*)())g_encryptedTextBase)();

    // 资源清理
    cs_close(&g_csHandle);
    free(g_originalEncryptedData);
    return 0;
}

注意事项

  • 指令长度准确性:必须用可靠的反汇编库(如Capstone)获取指令长度,手动解析容易出错,尤其是复杂的x86/x64指令。
  • 内存权限管理:每次解密后仅设置必要的权限(如PAGE_EXECUTE_READ),执行完立即恢复PAGE_NOACCESS,避免内存被非法访问。
  • 跨页指令处理:极少数情况下指令会跨内存页,此时需要同时解密两个页的对应区域,确保指令完整。
  • 架构适配:如果附加程序是32位,需要将Capstone的模式改为CS_MODE_32,并调整上下文记录中的寄存器(如Eip而非Rip)。

内容的提问来源于stack exchange,提问作者getunstuckaq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 07:55:16