节区权限处理

Fix Protection

PE 文件中的每个节区(Section)在 IMAGE_SECTION_HEADER 中通过 Characteristics 字段定义了初始内存保护属性。例如:

  • IMAGE_SCN_MEM_EXECUTE (可执行)
  • IMAGE_SCN_MEM_READ (可读)
  • IMAGE_SCN_MEM_WRITE (可写)

内存保护属性转换

将 PE 文件节区的 Characteristics 转换为 Windows 内存保护常量:

PE 节区属性内存保护属性 (WinAPI)
EXECUTEPAGE_EXECUTE
READPAGE_READONLY
READ + EXECUTEPAGE_EXECUTE_READ
READ + WRITEPAGE_READWRITE
READ + WRITE + EXECUTEPAGE_EXECUTE_READWRITE

“由于 IMAGE_SECTION_HEADER.Characteristics 是一个位域(bitfield),它可能同时包含多个标志(flags)。例如,检查 IMAGE_SCN_MEM_READ 标志时,必须使用按位与运算符(&),而不是相等性比较(==)。”

代码注释:

BOOL FixMemPermissions(IN ULONG_PTR pPeBaseAddress, IN PIMAGE_NT_HEADERS pImgNtHdrs, IN PIMAGE_SECTION_HEADER pImgSecHdr) {
//其中pPeBaseAddress:是使用virtualAlloc分配的PE基地址
//IN PIMAGE_NT_HEADERS pImgNtHdrs:是Headers.NTheaders
//IN PIMAGE_SECTION_HEADER pImgSecHdr:是IMAGE_SECTION_HEADER里面的Characteristics 
for (DWORD i = 0; i < pImgNtHdrs->FileHeader.NumberOfSections; i++) {
        DWORD dwProtection = PAGE_NOACCESS; // 默认无访问权限
        DWORD dwOldProtection = 0x00;

        // 跳过无效节
        if (!pImgSecHdr[i].SizeOfRawData || !pImgSecHdr[i].VirtualAddress)
            continue;

        // 按优先级判断组合
        DWORD ch = pImgSecHdr[i].Characteristics;
        if (ch & IMAGE_SCN_MEM_EXECUTE) {
            if ((ch & IMAGE_SCN_MEM_WRITE) && (ch & IMAGE_SCN_MEM_READ)) {
                dwProtection = PAGE_EXECUTE_READWRITE;
            } else if (ch & IMAGE_SCN_MEM_WRITE) {
                dwProtection = PAGE_EXECUTE_READWRITE; // WRITE 隐含 READ
            } else if (ch & IMAGE_SCN_MEM_READ) {
                dwProtection = PAGE_EXECUTE_READ;
            } else {
                dwProtection = PAGE_EXECUTE;
            }
        } else {
            if ((ch & IMAGE_SCN_MEM_WRITE) && (ch & IMAGE_SCN_MEM_READ)) {
                dwProtection = PAGE_READWRITE;
            } else if (ch & IMAGE_SCN_MEM_WRITE) {
                dwProtection = PAGE_READWRITE; // WRITE 隐含 READ
            } else if (ch & IMAGE_SCN_MEM_READ) {
                dwProtection = PAGE_READONLY;
            }
        }

        // 应用保护属性
        if (!VirtualProtect(
            (PVOID)(pPeBaseAddress + pImgSecHdr[i].VirtualAddress),
            pImgSecHdr[i].SizeOfRawData,
            dwProtection,
            &dwOldProtection
        )) {
            PRINT_WINAPI_ERR("VirtualProtect");
            return FALSE;
        }
    }
    return TRUE;
}

每个头的应有之权限都储存在SECTION头的character字段中

通过FIRST_SECTION(NT)访问IMAGE_SECTION_HEADER字段

接下来就很简单了

 int numOfSec=pNt->FileHeader.NumberOfSections;//头的数量储存在这里
    for(int i=0;i<numOfSec;i++){//处理每个头
        DWORD dwprotction=0;//将要赋予的权限
        DWORD oldprotc=0;
        printf("节区%s\t",pSec[i].Name);
        //单一权限判断
        if(pSec[i].Characteristics&IMAGE_SCN_MEM_WRITE){
            dwprotction=PAGE_WRITECOPY;
        }
        if(pSec[i].Characteristics&IMAGE_SCN_MEM_READ){
            dwprotction=PAGE_READONLY;
        }
        if(pSec[i].Characteristics&IMAGE_SCN_MEM_EXECUTE){
            dwprotction=PAGE_EXECUTE;
        }
        //双重权限判断
        if((pSec[i].Characteristics&IMAGE_SCN_MEM_WRITE)&&
        (pSec[i].Characteristics&IMAGE_SCN_MEM_READ)){
            dwprotction=PAGE_READWRITE;
        }
        if((pSec[i].Characteristics&IMAGE_SCN_MEM_WRITE)&&
        (pSec[i].Characteristics&IMAGE_SCN_MEM_EXECUTE)){
            dwprotction=PAGE_EXECUTE_WRITECOPY;
        }
        if((pSec[i].Characteristics&IMAGE_SCN_MEM_READ)&&
        (pSec[i].Characteristics&IMAGE_SCN_MEM_EXECUTE)){
            dwprotction=PAGE_EXECUTE_READ;
        }
        //全部权限
        if((pSec[i].Characteristics&IMAGE_SCN_MEM_READ)&&
        (pSec[i].Characteristics&IMAGE_SCN_MEM_EXECUTE)&&
        (pSec[i].Characteristics&IMAGE_SCN_MEM_WRITE)){
            dwprotction=PAGE_EXECUTE_READWRITE;
        }
        printf("新保护:0x%08x\n",dwprotction);
        //通过VirtualProtect赋予新权限
        if(!VirtualProtect(peBase+pSec[i].VirtualAddress,
        //这里当VirtualSize为0时才使用硬盘大小
        pSec[i].Misc.VirtualSize?pSec[i].Misc.VirtualSize:pSec[i].SizeOfRawData,
        dwprotction,
        &oldprotc)){
            printf("VirtualProtect error:0x%08x\n",GetLastError());
            return FALSE;
        }
    }
    return TRUE;