节区权限处理
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) |
---|---|
EXECUTE | PAGE_EXECUTE |
READ | PAGE_READONLY |
READ + EXECUTE | PAGE_EXECUTE_READ |
READ + WRITE | PAGE_READWRITE |
READ + WRITE + EXECUTE | PAGE_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;