PE文件代码段特征码扫描 以及进程代码段扫描

好久没写博客了  今天好累  休息一下 想起来写个博客  (未加壳文件)

最近在做PE文件的特征码扫描   刚开始的时候一头雾水  因为对PE文件的格式不是很了解   之前虽然看过一些PE文件的帖子 但是都是看不下去  现在针对这几天的努力 贴上我对PE文件特征码扫描的一些见解  方法和代码    本文不考虑后面的节点以及节点格式和内容


1、PE文件特征码扫描

  a). 读文件  判断是否是PE格式的文件

读文件,文件的开始是DOS头  IMAGE_DOS_HEADER   这个结构体很长  想详细了解就去百度 (http://blog.csdn.net/pxm2525/article/details/39895487#) 有用的就两个字段。该结构体中,有两个字段需要注意,分别是第一个字段 e_magic,和最后一个字段 e_lfanew字段。第一个字段e_magic就是Dos 头,两个字节 0x5a4d, 第二个字段e_lfanew是跳转到 IMAGE_NT_HEADERS 结构体的偏移地址。IMAGE_NT_HEADERS 包括三部分,(1)PE头,四个字节0x00004550  ; (2)Image_File_Header(0x14h大小);(3)Image_Optinoal_Header(0xe0h)

判断是否是标准的PE文件就是判断DOS头中的e_magic 是否为0x5a4d 以及 e_magic偏移的字节在读取四个字节是否为0x00004550  还有一些是否为.exe或者是否为.dll的一些判断 这个判断在IMAGE_FILE_HEADER 中的Characteristics字节 如果是 IMAGE_FILE_EXECUTABLE_IMAGE 0x0002  如果是IMAGE_FILE_DLL (0x2000)就是.dll  

 b). 获取到代码段的偏移以及大小  定位到文件的代码段

获取代码段的偏移地址和大小是在Image_Optinoal_Header中的baseofcode 字段,大小在baseofcode字段。

下面是我写的一些片段代码

// lpdata 是文件的内容buffer
lpmImageDosHeader = (IMAGE_DOS_HEADER*)lpData;
lpmImageNtHeaders = (IMAGE_NT_HEADERS*)((SIZE_T)lpData + lpmImageDosHeader->e_lfanew);
dwBaseOfCode = lpmImageNtHeaders->OptionalHeader.BaseOfCode;
dwSizeOfCode = lpmImageNtHeaders->OptionalHeader.SizeOfCode;
//  获取到代码段的大小以后,偏移到代码段
BYTE* bByte = NULL;
// 跳转到代码段
bByte = (BYTE*)((SIZE_T)lpData + dwBaseOfCode);


c). 扫描代码段

扫描代码段的时候,需要在外部给出一些要扫描的特征码 我自己写个按照字节一个个比较的,然后这个方法经理看了以后觉得比较low 然后就建议我用这个方法:

首先读取前四个特征码的的值 (int ulog)都行 然后跟代码段的比较 一次比较四个字节 但是每次比较完之后偏移一个字节 如果前四个字节相等的话 下面我做了特征码的哈希值,如果哈希值也相等的话,就说明在代码段匹配到特征码

说是这个方法简单一些 ,下面是获取哈希值的一些代码 (水平有限,高手略过)

// 获取一段字节的hash值
BOOL GetHashValue(BYTE* lpByte, ULONG uSize, WCHAR* wszHashValue)
{
if ((lpByte == NULL) || (uSize == 0))
{
return FALSE;
}
HCRYPTPROV hProv = NULL;
if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) // 获得CSP中一个密钥容器的句柄
{
return FALSE;
}
HCRYPTPROV hHash = NULL;
// 初始化对数据流的hash,创建并返回一个与CSP的hash对象相关的句柄。这个句柄接下来将被CryptHashData调用。
if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash) == FALSE)
{
CryptReleaseContext(hProv, 0);
return FALSE;
}
if (CryptHashData(hHash, lpByte, uSize, 0) == FALSE) // hash文件
{
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
return FALSE;
}
DWORD dwHashLen = sizeof(DWORD);
if (!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwHashLen, 0)) //参照msdn
{
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
return FALSE;
}
BYTE *pbHash = NULL;
pbHash = (byte*)malloc(dwHashLen);
if (CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashLen, 0))//获得SHA1值
{
DWORD i = 0;
for (i = 0; i < dwHashLen; i++) // 输出SHA1值 
{
swprintf_s(wszHashValue + 2 * i, 4, L"%02x", pbHash[i]);
}
wszHashValue[2 * i] = '\0';
}
free(pbHash);
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return TRUE;
}

一些字节比较的代码我就不贴了  简单的一逼


二、 进程的PE特征码扫描

进程这个特征码扫描跟文件的差不多,只不过是其他进程的一些地址的获取或者偏移等。在这里今天犯了一个特别蠢的问题,在自己的进程获取到要扫描的进程的加载地址的时候, 我在VS上用alt+6 调出来内存窗口  添加地址 回车  发现全是 cc cc cc cc....  我就无语了 难道我取错了吗  还是怎么搞得 。最后同事说进程在不同的空间  我才恍然大悟  真是醉了....  

a) . 遍历所有进程 拿到pid   注意提权

CreateToolhelp32Snapshot       Process32First   

 

while (Process32Next(hProcessSnap, &ProcessInfoFirst))
{
if (FALSE == ScanSignalProcess(ProcessInfoFirst.th32ProcessID, stThreadParasPro.lpScanBytes, stThreadParasPro.uScanSize))
{
CloseHandle(hProcessSnap);
return FALSE;
}
}

b).  根据Pid,获取进程的加载的首地址

CreateToolhelp32Snapshot     代码正在调试  做个参考吧

PVOID GetModulBaseAddr(DWORD dwProcessID, PVOID pvModuleRemote)
{
if (pvModuleRemote == NULL)
{
return NULL;
}
PVOID pvBaseAddr = NULL;
IMAGE_DOS_HEADER dosHdr;
IMAGE_NT_HEADERS ntHdr;
Toolhelp32ReadProcessMemory(dwProcessID, pvModuleRemote, &dosHdr, sizeof(dosHdr), NULL);
if (dosHdr.e_magic == IMAGE_DOS_SIGNATURE)
{
Toolhelp32ReadProcessMemory(dwProcessID, (PBYTE)pvModuleRemote + dosHdr.e_lfanew, &ntHdr, sizeof(ntHdr), NULL);
if (ntHdr.Signature == IMAGE_NT_SIGNATURE)
{
pvBaseAddr = (PVOID)ntHdr.OptionalHeader.ImageBase;
}
}

return pvBaseAddr;
}


BOOL GetSignalProcessStarAddr(IN ULONG Pid, IN OUT DWORD& dwStarAddr)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, Pid); 
if (hSnapshot == INVALID_HANDLE_VALUE)
{
CloseHandle(hSnapshot);
return -1;
}

MODULEENTRY32 me;
ZeroMemory(&me, sizeof(MODULEENTRY32));
me.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(hSnapshot,&me))
{
return FALSE;
}
// 获取基址
PVOID pAddr = GetModulBaseAddr(Pid, me.modBaseAddr);
if (pAddr == NULL)
{
return FALSE;
}
if (pAddr == me.modBaseAddr)
{
printf("%08x ", (DWORD)pAddr);
dwStarAddr = (DWORD)pAddr;
}
else
{
printf("%08x ", (DWORD)me.modBaseAddr);
dwStarAddr = (DWORD)me.modBaseAddr;
}


CloseHandle(hSnapshot);
return TRUE;
}
PVOID GetModulBaseAddr(DWORD dwProcessID, PVOID pvModuleRemote)
{
if (pvModuleRemote == NULL)
{
return NULL;
}
PVOID pvBaseAddr = NULL;
IMAGE_DOS_HEADER dosHdr;
IMAGE_NT_HEADERS ntHdr;
Toolhelp32ReadProcessMemory(dwProcessID, pvModuleRemote, &dosHdr, sizeof(dosHdr), NULL);
if (dosHdr.e_magic == IMAGE_DOS_SIGNATURE)
{
Toolhelp32ReadProcessMemory(dwProcessID, (PBYTE)pvModuleRemote + dosHdr.e_lfanew, &ntHdr, sizeof(ntHdr), NULL);
if (ntHdr.Signature == IMAGE_NT_SIGNATURE)
{
pvBaseAddr = (PVOID)ntHdr.OptionalHeader.ImageBase;
}
}

return pvBaseAddr;
}



c).  readprocessmemory或者Toolhelp32ReadProcessMemory 读取指定进程的PE结构信息  (IMAGE_DOS_HEADER  IMAGE_NT_HEADERS 等 )

d).  获取到指定进程的代码段数据到本地

这个就不说了  直接读取指定进程内容到本地

e).  和文件扫描一样进行特征码的匹配扫描

这个就更不说了

附件:PE格式表

PE文件代码段特征码扫描 以及进程代码段扫描_第1张图片



















你可能感兴趣的:(PE文件详解,WIN32,PE,PE格式,PE特征码扫描,PE文件扫描,PE进程扫描)