看了windows核心编程相关篇章后所写:
1.进程代码注入一般会调用VirtualAllocEx来给目标注入进程分配shellcode的空间,并且修改这段内存属性为可执行可写。
2.检测思路:程序最开始运行时候初始化一个链表,保存每一个区域中页面的属性,然后开启一个线程对每一个页面的属性进行检查是否有新的可执行属性,如果检测到有就说明有VirtualAllocEx进行的操作(一般自己的程序不会调用VirtualAllocEx分配可执行空间),判断时候要加上判断页面的Type不为MEM_IMAGE,因为调用一些windowsAPI后就会在MEM_IMAGE区域中分配可执行内存地址(例如MessageBoxA --- 自己测试的时候发现的。。懵逼好久)
写了一个类进行封装一下:代码如下:
"Memory_Property.h"
#pragma once
#include<Windows.h>
#include<stdio.h>
typedef struct SMEM_PROPERTY
{
DWORD dwBaseAddress;
DWORD dwMemorySize;
DWORD dwType; // MEM_IMAGE MEM_MAPPED MEM_PRIVATE 为MEM_IMAGE则不是
DWORD dwProtect;
struct SMEM_PROPERTY * pNext;
}SMEM_PROPERTY,*PSMEM_PROPERTY;
class Memory_List
{
private:
PSMEM_PROPERTY pHead; //链表头
HANDLE hProcess; //进程句柄,默认自身进程
public:
Memory_List(HANDLE hPro = (HANDLE)0xFFFFFFFF);
~Memory_List();
bool AddNode(SMEM_PROPERTY node);
bool CheckProtectProperty(SMEM_PROPERTY mem_property); //检查这个是否有该属性
bool EnumCheckAddressProperty(); //遍历所有的地址
};
Memory_Property.cpp:
#define _CRT_SECURE_NO_WARNINGS
#include"Memory_Property.h"
Memory_List::Memory_List(HANDLE hPro)//默认当前进程
{
pHead = NULL;
this->hProcess = hPro;
//初始化链表
MEMORY_BASIC_INFORMATION mbi;
LPVOID lpQueryAddr = 0;
while (true)
{
if (VirtualQueryEx(hProcess, lpQueryAddr, &mbi, sizeof(mbi)) != sizeof(mbi))
return; //查询完毕
if (mbi.State == MEM_FREE)
{
lpQueryAddr = (PBYTE)lpQueryAddr + mbi.RegionSize;
SMEM_PROPERTY mem_property;
mem_property.dwBaseAddress = (DWORD)mbi.BaseAddress;
mem_property.dwMemorySize = mbi.RegionSize;
mem_property.dwProtect = mbi.Protect;
mem_property.dwType = mbi.Type;
AddNode(mem_property);
continue;
}
LPVOID lpAllocaBaseAddr = mbi.AllocationBase;
LPVOID lpvAddressBlk = lpAllocaBaseAddr;
for (;;)
{
if (VirtualQueryEx(hProcess, lpvAddressBlk, &mbi, sizeof(mbi)) != sizeof(mbi))
break;
if (lpAllocaBaseAddr != mbi.AllocationBase) //属于这一块
break;
lpQueryAddr = (PBYTE)lpQueryAddr + mbi.RegionSize;
SMEM_PROPERTY mem_property;
mem_property.dwBaseAddress = (DWORD)mbi.BaseAddress;
mem_property.dwMemorySize = mbi.RegionSize;
mem_property.dwProtect = mbi.Protect;
mem_property.dwType = mbi.Type;
AddNode(mem_property);
lpvAddressBlk = (PBYTE)lpvAddressBlk + mbi.RegionSize;
}
}
}
Memory_List::~Memory_List()
{
PSMEM_PROPERTY pNow = pHead;
PSMEM_PROPERTY pTemp;
while (pNow != NULL)
{
pTemp = pNow;
pNow = pNow->pNext;
delete pTemp;
}
CloseHandle(hProcess);
}
//增加一个节点到pHead中
bool Memory_List::AddNode(SMEM_PROPERTY node)
{
PSMEM_PROPERTY pmem_property = new SMEM_PROPERTY(node);
pmem_property->pNext = NULL;
if (pmem_property == NULL)
return false;
if (pHead == NULL)
pHead = pmem_property;
else
{
PSMEM_PROPERTY pTemp = pHead;
while (pTemp->pNext != NULL)
pTemp = pTemp->pNext;
pTemp->pNext = pmem_property;
}
return true;
}
//内存执行属性不一致返回true
bool Memory_List::CheckProtectProperty(SMEM_PROPERTY mem_property)
{
PSMEM_PROPERTY pTemp = pHead;
//遍历初始化后的内存信息链表
while (pTemp != NULL)
{
//如果这个节点不是最后一个并且这个地址在最后一个节点地址之后
if (pTemp->pNext == NULL && pTemp->dwBaseAddress <= mem_property.dwBaseAddress)
{
if (!(pTemp->dwProtect & 0xF0) &&
(mem_property.dwProtect & 0xF0) &&
mem_property.dwType != MEM_IMAGE)
return true; //检测原来的属性是否是有可执行,0xF0表示11110000,前四位都是可执行才有
return false;
}
//如果这个地址大于了当前节点,并且小于下一个节点(在这个节点地址内),就进行检查属性
else if (pTemp->dwBaseAddress <= mem_property.dwBaseAddress &&
pTemp->pNext->dwBaseAddress > mem_property.dwBaseAddress)
{
if (!(pTemp->dwProtect & 0xF0) &&
(mem_property.dwProtect & 0xF0) &&
mem_property.dwType != MEM_IMAGE)
return true;
return false;
}
pTemp = pTemp->pNext;
}
return false;
}
//刷新进程的内存信息,检测到可执行返回true
bool Memory_List::EnumCheckAddressProperty()
{
MEMORY_BASIC_INFORMATION mbi;
LPVOID lpQueryAddr = 0;
bool bFlag = FALSE;
while (true)
{
if (VirtualQueryEx(hProcess, lpQueryAddr, &mbi, sizeof(mbi)) != sizeof(mbi))
break; //查询完毕
if (mbi.State == MEM_FREE)
{
lpQueryAddr = (PBYTE)lpQueryAddr + mbi.RegionSize;
SMEM_PROPERTY mem_property;
mem_property.dwBaseAddress = (DWORD)mbi.BaseAddress;
mem_property.dwMemorySize = mbi.RegionSize;
mem_property.dwProtect = mbi.Protect;
mem_property.dwType = mbi.Type;
if (CheckProtectProperty(mem_property))
{
//检测到后的操作
/*char str[255];
sprintf(str,"检测到分配的可执行地址段:0x%0X --- %s",mem_property.dwBaseAddress, mem_property.dwBaseAddress);
printf("%s\n", str);*/
bFlag = true;
}
continue;
}
LPVOID lpAllocaBaseAddr = mbi.AllocationBase;
LPVOID lpvAddressBlk = lpAllocaBaseAddr;
for (;;)
{
if (VirtualQueryEx(hProcess, lpvAddressBlk, &mbi, sizeof(mbi)) != sizeof(mbi))
break;
if (lpAllocaBaseAddr != mbi.AllocationBase)
break;
lpQueryAddr = (PBYTE)lpQueryAddr + mbi.RegionSize;
SMEM_PROPERTY mem_property;
mem_property.dwBaseAddress = (DWORD)mbi.BaseAddress;
mem_property.dwMemorySize = mbi.RegionSize;
mem_property.dwProtect = mbi.Protect;
mem_property.dwType = mbi.Type;
if (CheckProtectProperty(mem_property))
{
//检测到后的操作
/*char str[255];
sprintf(str, "检测到分配的可执行地址段:0x%0X --- %s", mem_property.dwBaseAddress, mem_property.dwBaseAddress);
printf("%s\n",str);*/
bFlag = true;
}
lpvAddressBlk = (PBYTE)lpvAddressBlk + mbi.RegionSize;
}
}
return bFlag;
}
调用时候直接:
Memory_List mList;
if (mList.EnumCheckAddressProperty())
DebugBreak();