Windows捕获会引起程序崩溃的异常
1.给空指针赋值引起的异常(__try __except)
访问冲突(Access Violation):
- 这通常发生在尝试访问无效或不可访问的内存地址时,例如空指针解引用或访问已释放的内存。
- 示例:
int* ptr = NULL; *ptr = 5; 或 free(ptr); ptr = NULL; *ptr = 10;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <windows.h> #include <stdio.h>
void test() { __try { int* ptr = NULL; *ptr = 42; } __except (EXCEPTION_EXECUTE_HANDLER) { printf("An exception occurred!\n"); } }
int main() { printf("Hello, World!\n"); test(); return 0; }
|
2.修改常量引起的异常(__try __except)
- 如果程序尝试修改一个常量,通常会遇到内存保护错误。
- 示例:
const int x = 10; x = 20; 这会引起访问保护错误,因为常量被存储在只读区域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <windows.h> #include <stdio.h>
const int c_Var = 0x400000;
int main(int argc, char* argv[]) { printf("%d\n", c_Var); DWORD dwOldProtect = 0; __try { __asm { mov dword ptr[c_Var], 634 } } __except (EXCEPTION_EXECUTE_HANDLER) { printf("Exception caught while executing arry.\n"); }
printf("%d\n", c_Var); return 0; }
|
3.执行不可执行的内存区域引起的异常(__try __except)
例如数组的地址是可读可写不可执行的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <stdio.h> #include <windows.h>
unsigned char arry[] = { 0xC3 };
void test() { __try { void (*func)() = (void(*)())arry; func(); printf("Function call succeeded.\n"); } __except (EXCEPTION_EXECUTE_HANDLER) { printf("Exception caught while executing arry.\n"); } }
int main() { printf("Hello, World!\n"); test(); return 0; }
|
🧠 各种分配方式的默认执行权限:
| 分配方式 |
默认是否可执行 |
说明 |
std::vector |
❌ 否 |
在堆上分配,内存标记为 RW(读写),不可执行(受到 DEP 保护) |
malloc |
❌ 否 |
与 vector 类似,分配到堆,默认 RW |
new |
❌ 否 |
和 malloc 类似,不可执行 |
| 局部变量(栈上) |
❌ 否 |
栈内存默认不可执行 |
VirtualAlloc |
✅ 可设置 |
可以手动指定 PAGE_EXECUTE_READWRITE 或 PAGE_EXECUTE_READ 等权限 |
mmap(Linux) |
✅ 可设置 |
可以指定 PROT_EXEC 使内存可执行 |
🧠 哪些内存区域可以设置为可执行?
| 内存区域 |
描述 |
可设置为可执行? |
推荐使用? |
.text 段 |
编译后的代码段 |
✅ 是 |
✅ 系统自动设置 |
.data 段 |
全局变量(可读写) |
⛔ 否(通常 NX) |
❌ |
.rdata 段 |
常量数据(只读) |
⛔ 否 |
❌ |
| 栈(stack) |
函数局部变量 |
⛔ 否 |
❌ |
| 堆(heap) |
malloc / new 分配的 |
⛔ 否 |
❌ |
VirtualAlloc |
显式请求的一段内存 |
✅ 是 |
✅ 推荐 |
| 内存映射文件区 |
CreateFileMapping + MapViewOfFile |
✅ 可配置 |
⚠️ 灵活复杂 |
4.写入异常捕获更改自身内存属性,使自身程序不崩溃_try __except
windows函数默认是可读可执行不可写,修改指令需要先设置内存属性为可读可写可执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <windows.h> #include <stdio.h>
void Demo() { } int main() { DWORD oldProtect; LPVOID address = (LPVOID)&Demo; printf("%p\n", Demo); unsigned char data[] = { 0xC3,0xC3}; __try { memcpy(address, data, sizeof(data)); } __except (EXCEPTION_EXECUTE_HANDLER) { SIZE_T size = sizeof(data); if (VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &oldProtect)) { printf("Memory protection changed successfully.\n"); memcpy(address, data, sizeof(data)); } else { printf("Failed to change memory protection. Error: %lu\n", GetLastError()); } VirtualProtect(address, size, oldProtect, &oldProtect); } Demo(); getchar(); return 0; }
|
5.写入异常捕获更改自身内存属性(AddVectoredExceptionHandler)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <windows.h> #include <stdio.h>
PVOID g_handler = NULL;
LONG WINAPI VectoredHandler(PEXCEPTION_POINTERS pExceptionInfo) { printf("Exception code: 0x%X at address: %p\n", pExceptionInfo->ExceptionRecord->ExceptionCode, pExceptionInfo->ExceptionRecord->ExceptionAddress);
if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { DWORD oldProtect; SIZE_T size = 2; LPVOID address = (LPVOID)pExceptionInfo->ExceptionRecord->ExceptionInformation[1];
if (VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &oldProtect)) { printf("Memory protection changed successfully inside exception handler.\n"); return EXCEPTION_CONTINUE_EXECUTION; } else { printf("Failed to change memory protection.\n"); return EXCEPTION_CONTINUE_SEARCH; } }
return EXCEPTION_CONTINUE_SEARCH; }
void Demo() { }
int main() { g_handler = AddVectoredExceptionHandler(1, VectoredHandler);
LPVOID address = (LPVOID)&Demo; printf("Demo address: %p\n", address);
unsigned char data[] = { 0xC3, 0xC3 }; size_t size = sizeof(data);
memcpy(address, data, size);
Demo();
if (g_handler) RemoveVectoredExceptionHandler(g_handler);
getchar(); return 0; }
|
6.修改函数内存页属性后运行函数引起的异常(AddVectoredExceptionHandler)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <windows.h> #include <stdio.h>
void Demo() { char str[] = "Demo******************"; printf("%s\n", str); }
LONG CALLBACK VehHandler(PEXCEPTION_POINTERS ExceptionInfo) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) { printf("[VEH] Caught PAGE_GUARD at address: %p\n", ExceptionInfo->ExceptionRecord->ExceptionAddress);
return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; }
int main() { AddVectoredExceptionHandler(1, VehHandler);
DWORD oldProtect; LPVOID address = (LPVOID)&Demo;
SYSTEM_INFO si; GetSystemInfo(&si); DWORD pageSize = si.dwPageSize; LPVOID pageAddress = (LPVOID)((uintptr_t)address & ~(pageSize - 1));
MEMORY_BASIC_INFORMATION mbi; VirtualQuery(pageAddress, &mbi, sizeof(mbi)); DWORD newProtect = (mbi.Protect & 0xFF) | PAGE_GUARD;
if (!VirtualProtect(pageAddress, pageSize, newProtect, &oldProtect)) { printf("Failed to change memory protection. Error: %lu\n", GetLastError()); return 1; }
printf("[*] PAGE_GUARD set. Now calling Demo...\n");
Demo();
printf("[*] Finished.\n");
getchar(); return 0; }
|
7.只有msvc会优化_try __except,其他编译器需要使用**AddVectoredExceptionHandler** 来安装自己的异常处理回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <windows.h> #include <stdio.h>
LONG CALLBACK MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) { printf("[VEH] Exception code: 0x%X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { DWORD oldProtect; LPVOID address = (LPVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1]; SIZE_T size = 2;
if (VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &oldProtect)) { printf("[VEH] Memory protection changed successfully.\n"); return EXCEPTION_CONTINUE_EXECUTION; } } return EXCEPTION_CONTINUE_SEARCH; }
__declspec(noinline) void Demo() { volatile int dummy = 0; dummy++; }
int main() { PVOID handler = AddVectoredExceptionHandler(1, MyExceptionHandler);
LPVOID address = (LPVOID)&Demo; printf("Demo address: %p\n", address);
unsigned char data[] = { 0xC3, 0xC3 }; memcpy(address, data, sizeof(data));
Demo();
RemoveVectoredExceptionHandler(handler);
getchar(); return 0; }
|
8.其他行为引起的异常