本节主要讲 通过添加新区块、修改输入表方法实现PE感染
本次课程涉及到的技术点有几个部分:
1,添加输入表项。
2,添加新区块。
3,插入 Stub。
********************************************************************************************
下面是实现这个程序的主要步骤,其中使用内存映射机制来实现文件的操作。
1)添加用于存放Stub的新区块。添加新区块需要进行一下操作:
增加区块数量
添加区块信息
修正PE文件的映像大小
添加PE文件大小
********************************************************************************************
Stub 要完成的任务很简单,显示消息框然后跳转到应用程序原始入口继续执行。
需要修改程序的入口点指向 Stub ,应用程序执行时就会直接转到 Stub 执行。
********************************************************************************************
新区块数据由以下7本分组成:
1,输入表,原输入表数据复制到新区块,并为user32.dll的MessageBoxA加新项。
2,DLL名字符串,固定为“USER32.dll”,在输入表新项中使用。
3,函数名字符串,固定为“MessageBoxA”,在输入表新项中使用。
4,新输入表项所对应的IAT表。
5,Nag消息框的标题字符串。
6,Nag消息框的内容字符串。
以下为完整代码:
PEInfo.h文件
#pragma oncetypedef struct _MAP_ITEM { HANDLE hFile ; HANDLE hMapFile ; LPVOID lpMapAddr ;} MAP_ITEM ;typedef MAP_ITEM* PMAP_ITEM ;typedef struct _BASE_INFO_ITEM { DWORD dwSectionAlign ; //块对齐大小 DWORD dwFileAlign ; //文件块对齐大小 WORD dwSectionBase ; //节表开始地址 WORD dwSectionNum ; //节表个数 DWORD dwHeadSize ; //所有头+节表大小,也可以以此值作为PE文件第一节的文件偏移量 DWORD dwRealHeadSize ; //节表结束地址 DWORD dwImageSize ; //镜像大小 DWORD dwFileSize ; //文件大小} BASE_INFO_ITEM ;typedef BASE_INFO_ITEM* PBASE_INFO_ITEM ;class CPEInfo{public: CPEInfo(void);public: ~CPEInfo(void);public: BOOL isReady ; BASE_INFO_ITEM BaseInfo ; IMAGE_SECTION_HEADER NewSection ; public: BOOL CreateMap ( TCHAR* pPathName, PMAP_ITEM pMapItem ) ; BOOL CreateNewMap ( TCHAR* pPathName, PMAP_ITEM pMapItem, DWORD dwFileSize ) ; VOID DeleteMap ( PMAP_ITEM pMapItem ) ; BOOL FlushData ( LPVOID lpAddr, UINT nNumToFlush ) ;//清除数据public: BOOL IsValidPE ( PMAP_ITEM pMapItem ) ; VOID GetBaseInfo ( PMAP_ITEM pMapItem ) ; VOID InitNewSection () ; //初始的新Section VOID AdjustSectionSize () ; // 包含了映像大小和文件大小 VOID AddNewSection ( PMAP_ITEM pOldMap, PMAP_ITEM pNewMap, DWORD dwNewFileSize ) ;public: DWORD FormatInt ( DWORD dwValue, DWORD dwAlign ) ;};
PEInfo.cpp
#include "StdAfx.h"#include "PEInfo.h"CPEInfo::CPEInfo(void){ int ii = sizeof(IMAGE_OPTIONAL_HEADER); isReady = FALSE ; memset ( &BaseInfo, 0, sizeof(BASE_INFO_ITEM) ) ;//新申请的内存做初始化工作 memset ( &NewSection, 0, sizeof(IMAGE_SECTION_HEADER) ) ;//新申请的内存做初始化工作}CPEInfo::~CPEInfo(void){}BOOL CPEInfo::CreateMap ( TCHAR* pPathName, PMAP_ITEM pMapItem ) { pMapItem->hFile = CreateFile ( pPathName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL , NULL ); if ( pMapItem->hFile == INVALID_HANDLE_VALUE ) { return FALSE; } //创建文件映射内核对象 pMapItem->hMapFile = CreateFileMapping ( pMapItem->hFile, NULL, PAGE_READONLY, 0, 0, NULL ) ; if ( pMapItem->hMapFile == NULL ) { CloseHandle ( pMapItem->hFile ) ; return FALSE; } //创建文件视图 pMapItem->lpMapAddr = (PBYTE)MapViewOfFile ( pMapItem->hMapFile, FILE_MAP_READ, 0, 0, 0 ) ; if ( pMapItem->lpMapAddr == NULL ) { DWORD dwErrorCode = GetLastError () ; CloseHandle ( pMapItem->hMapFile ) ; CloseHandle ( pMapItem->hFile ) ; return FALSE; } return TRUE ;}BOOL CPEInfo::CreateNewMap ( TCHAR* pPathName, PMAP_ITEM pMapItem, DWORD dwFileSize ) { pMapItem->hFile = CreateFile ( pPathName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL , NULL ); if ( pMapItem->hFile == INVALID_HANDLE_VALUE ) { return FALSE; } //创建文件映射内核对象 pMapItem->hMapFile = CreateFileMapping ( pMapItem->hFile, NULL, \ PAGE_READWRITE, 0, dwFileSize, NULL ) ; if ( pMapItem->hMapFile == NULL ) { CloseHandle ( pMapItem->hFile ) ; return FALSE; } //创建文件视图 pMapItem->lpMapAddr = (PBYTE)MapViewOfFile ( pMapItem->hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0 ) ; if ( pMapItem->lpMapAddr == NULL ) { DWORD dwErrorCode = GetLastError () ; CloseHandle ( pMapItem->hMapFile ) ; CloseHandle ( pMapItem->hFile ) ; return FALSE; } return TRUE ;}void CPEInfo::DeleteMap ( PMAP_ITEM pMapItem ){ UnmapViewOfFile ( pMapItem->lpMapAddr ) ; CloseHandle ( pMapItem->hMapFile ) ; CloseHandle ( pMapItem->hFile ) ;}BOOL CPEInfo::FlushData ( LPVOID lpAddr, UINT nNumToFlush ){ return FlushViewOfFile ( lpAddr, nNumToFlush ) ;}BOOL CPEInfo::IsValidPE ( PMAP_ITEM pMapItem ){ return TRUE ;} VOID CPEInfo::GetBaseInfo ( PMAP_ITEM pMapItem ){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(pMapItem->lpMapAddr) ; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((LONG)(pMapItem->lpMapAddr)+ (pDosHeader->e_lfanew)) ; PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = &(pNtHeader->OptionalHeader) ; BaseInfo.dwSectionAlign = pOptionalHeader->SectionAlignment ;// 内存中的区块的对齐大小 BaseInfo.dwFileAlign = pOptionalHeader->FileAlignment ;// 文件中的区块的对齐大小 BaseInfo.dwSectionNum = pNtHeader->FileHeader.NumberOfSections ;//PE文件中有多少个节 BaseInfo.dwHeadSize = pOptionalHeader->SizeOfHeaders ;// 所有头 + 区块表的尺寸大小 BaseInfo.dwSectionBase = pDosHeader->e_lfanew + 0x18 + pNtHeader->FileHeader.SizeOfOptionalHeader;//块表开始的地址DOS_Header+File__Header+Optional Header BaseInfo.dwRealHeadSize = BaseInfo.dwSectionBase + BaseInfo.dwSectionNum * 0x28 ;//块表结束的地址 BaseInfo.dwImageSize = pOptionalHeader->SizeOfImage ; PIMAGE_SECTION_HEADER LastSec = (PIMAGE_SECTION_HEADER)((LONG)(pMapItem->lpMapAddr) + BaseInfo.dwSectionBase + 0x28 * ( BaseInfo.dwSectionNum - 1 ) ) ; BaseInfo.dwFileSize = LastSec->PointerToRawData + FormatInt ( LastSec->SizeOfRawData, pOptionalHeader->FileAlignment );//文件的大小}VOID CPEInfo::InitNewSection (){ strcpy ( (char*)NewSection.Name, ".NewSec" ) ; NewSection.Misc.VirtualSize = BaseInfo.dwSectionAlign ; NewSection.VirtualAddress = BaseInfo.dwImageSize ; NewSection.SizeOfRawData = BaseInfo.dwFileAlign ; NewSection.PointerToRawData = BaseInfo.dwFileSize ; NewSection.Characteristics = 0xE0000020 ;}DWORD CPEInfo::FormatInt ( DWORD dwValue, DWORD dwAlign ){ if ( dwValue % dwAlign ) return ( dwValue / dwAlign + 1 ) * dwAlign ; return dwValue / dwAlign * dwAlign ;}void CPEInfo::AdjustSectionSize (){ if ( NewSection.Misc.VirtualSize < NewSection.SizeOfRawData ) NewSection.Misc.VirtualSize = NewSection.SizeOfRawData ; if ( NewSection.Misc.VirtualSize % BaseInfo.dwSectionAlign ) NewSection.Misc.VirtualSize = NewSection.Misc.VirtualSize / BaseInfo.dwSectionAlign + 1 ; else NewSection.Misc.VirtualSize = NewSection.Misc.VirtualSize / BaseInfo.dwSectionAlign ; NewSection.Misc.VirtualSize *= BaseInfo.dwSectionAlign ; if ( NewSection.SizeOfRawData % BaseInfo.dwFileAlign ) NewSection.SizeOfRawData = NewSection.SizeOfRawData / BaseInfo.dwFileAlign + 1 ; else NewSection.SizeOfRawData = NewSection.SizeOfRawData / BaseInfo.dwFileAlign ; NewSection.SizeOfRawData *= BaseInfo.dwFileAlign ;}void CPEInfo::AddNewSection ( PMAP_ITEM pOldMap, PMAP_ITEM pNewMap, DWORD dwNewFileSize ){ memcpy ( LPVOID(pNewMap->lpMapAddr), LPVOID(pOldMap->lpMapAddr), NewSection.PointerToRawData ) ; // Fix PE Head's some item PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(pNewMap->lpMapAddr) ; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((LONG)(pNewMap->lpMapAddr)+ (pDosHeader->e_lfanew)) ; PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = &(pNtHeader->OptionalHeader) ; // Increase section numbers pNtHeader->FileHeader.NumberOfSections ++ ; // Write into new IMAGE_SECTION_HEADER memcpy ( LPVOID(DWORD(pDosHeader)+BaseInfo.dwRealHeadSize), &(NewSection), 0x28 ) ; // Modify IMAGE SIZE pOptionalHeader->SizeOfImage += NewSection.Misc.VirtualSize ; FlushData ( pNewMap->lpMapAddr, dwNewFileSize ) ;}
NagGenDlg.h文件
#pragma once
#include "PEInfo.h"
#include "Dbghelp.h"
#pragma comment ( lib, "Dbghelp.lib" )// CNagGenDlg 对话框
class CNagGenDlg : public CDialog{public:
CPEInfo PEInfo ;
DWORD dwDllNameOff, dwFunNameOff, dwIATOff, dwIIDNum, dwNewEntryOff, dwNagTitleOff, dwNagContentOff ;
CString m_NagTitle;
#include "stdafx.h"#include "NagGen.h"#include "NagGenDlg.h"#ifdef _DEBUG#define new DEBUG_NEW#endif#define MAX_BUF_SIZE 2048#define MAX_TITLE_SIZE 64#define MAX_CONTENT_SIZE 256TCHAR a[] = TEXT("fjda") ;TCHAR b[] = TEXT("fdsadagfdsa") ;////void StubCode ()//{ // PTCHAR pTitle = NULL ;// PTCHAR pContent = NULL ;//// DWORD begin, end ;// __asm// {// push edx// call $+5// pop edx// add edx, 7// mov begin, edx//BEGIN:// pushad// push 0// push pTitle// push pContent// push 0// call dword ptr MessageBoxW// popad//END: // call $+5// pop edx// add edx, -5// mov end, edx// pop edx// }//}void CNagGenDlg::OnBnClickedOk(){ MAP_ITEM OldMap = { 0}, NewMap = { 0} ;//初始化数据 TCHAR pBuf[MAX_BUF_SIZE] = { 0} ; BYTE pData[MAX_BUF_SIZE] ={ 0} ; wsprintf ( pBuf, m_PathName.GetBuffer(m_PathName.GetLength()) ) ;//m_PathName需要修改的PE文件 if ( PEInfo.CreateMap(pBuf, &OldMap) == FALSE ) //创建文件映射到内存 { MessageBox ( TEXT("Please ensure this file is not been used!") ) ; return ; } // 取得PE文件基本信息 PEInfo.GetBaseInfo (&OldMap) ; // 初始化新区段信息 PEInfo.InitNewSection () ; // 产生并组合数据,保存到pData缓冲区,并根据所需要的长度对区段信息进行调整 DWORD dwNeedSize = GenerateData ( pData, OldMap.lpMapAddr );//生成数据 if ( PEInfo.NewSection.SizeOfRawData < dwNeedSize )//判断所需的大小,并确定大小 PEInfo.NewSection.SizeOfRawData = dwNeedSize ; PEInfo.AdjustSectionSize () ; // 建立新文件(*_nag.exe) CString TempStr = m_PathName ; DWORD dwNewFileSize = PEInfo.NewSection.PointerToRawData + PEInfo.NewSection.SizeOfRawData ; TempStr.SetAt ( TempStr.GetLength()-4, 0 ) ; wsprintf ( pBuf, TEXT("%s%s"), TempStr.GetBuffer(TempStr.GetLength()), TEXT("_nag.exe") ) ; if ( PEInfo.CreateNewMap ( pBuf, &NewMap, dwNewFileSize ) == FALSE ) { MessageBox ( TEXT("Could't create new file!") ) ; DeleteFile ( pBuf ) ; return ; } // 添加区块信息 PEInfo.AddNewSection ( &OldMap, &NewMap, dwNewFileSize ) ; // 删除原PE文件的映射 PEInfo.DeleteMap ( &OldMap ) ; // 把pData中的数据写入到新区块( contain IID, NewDllName, NewFunName, NewIat) LPVOID lpNewSection = LPVOID(DWORD(NewMap.lpMapAddr)+PEInfo.NewSection.PointerToRawData) ; memcpy ( lpNewSection, pData, PEInfo.NewSection.SizeOfRawData ) ; // 取得PE头信息 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(NewMap.lpMapAddr) ; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((LONG)(NewMap.lpMapAddr)+ (pDosHeader->e_lfanew)) ; PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = &(pNtHeader->OptionalHeader) ; // 写入新IID信息 IMAGE_IMPORT_DESCRIPTOR // 如果OriginalFirstThunk 字段的值为0,加载就不会改写IAT,也就是说,需要自己向IAT写入对应函数的地址 // 这里把OriginalFirstThunk的值设置为与FirstThunk相同,这样初始化的时候加载器就会改写IAT PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)lpNewSection + 0x14 * dwIIDNum) ; pIID->Name = dwDllNameOff + PEInfo.NewSection.VirtualAddress ; pIID->FirstThunk = dwIATOff + PEInfo.NewSection.VirtualAddress ; pIID->OriginalFirstThunk = pIID->FirstThunk ; pIID->ForwarderChain = 0xFFFFFFFF ; pIID->TimeDateStamp = 0xFFFFFFFF ; *((DWORD*)(dwIATOff + (DWORD)lpNewSection)) = dwFunNameOff + PEInfo.NewSection.VirtualAddress ; // 修改数据目录项中输入表项信息 pOptionalHeader->DataDirectory[1].VirtualAddress = PEInfo.NewSection.VirtualAddress ; pOptionalHeader->DataDirectory[1].Size += 0x14 ; // 清楚绑定输入表 pOptionalHeader->DataDirectory[11].VirtualAddress = 0 ; pOptionalHeader->DataDirectory[11].Size = 0 ; // 修改OEP pOptionalHeader->AddressOfEntryPoint = dwNewEntryOff + PEInfo.NewSection.VirtualAddress ; PEInfo.FlushData ( LPVOID(PEInfo.NewSection.VirtualAddress), PEInfo.NewSection.SizeOfRawData ) ; PEInfo.DeleteMap ( &NewMap ) ; MessageBox ( TEXT("添加Nag窗口成功!") ) ;}//生成新区块数据DWORD CNagGenDlg::GenerateData(PBYTE pData, LPVOID lpImageBase){ // 取得PE文件基本信息 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(lpImageBase) ; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((LONG)(lpImageBase)+ (pDosHeader->e_lfanew)) ; PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = &(pNtHeader->OptionalHeader) ; PIMAGE_DATA_DIRECTORY pImportData = (PIMAGE_DATA_DIRECTORY)(&( pOptionalHeader->DataDirectory[1]) ) ; // //定位到导入表 //IMAGE_IMPORT_DESCRIPTOR *pImportDesc=(IMAGE_IMPORT_DESCRIPTOR*)((BYTE*)lpBase+ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR) ImageRvaToVa ( pNtHeader, lpImageBase, pImportData->VirtualAddress, NULL ) ; // 把原IID表拷贝到缓冲区 memcpy ( pData, LPVOID(pIID), pOptionalHeader->DataDirectory[1].Size );//ImportTable //统计IID数量(dll的数量) dwIIDNum = 0 ; while ( pIID[dwIIDNum].FirstThunk ) dwIIDNum ++ ; char szDllName[] = "USER32.dll" ; char szFunName[] = "MessageBoxA" ; // 设置偏移信息 dwDllNameOff = pOptionalHeader->DataDirectory[1].Size + 0x14; dwFunNameOff = dwDllNameOff + sizeof (szDllName) + 3 ; dwIATOff = dwFunNameOff + sizeof (szFunName) + 4 ; dwNagTitleOff = dwIATOff + 12 ; // 写入DLL名 memcpy ( &(pData[dwDllNameOff]), szDllName, sizeof(szDllName) ) ; // 写入函数名 memcpy ( &(pData[dwFunNameOff+2]), szFunName, sizeof(szFunName) ) ; // 2, 序号 CHAR pTitle[128] = { 0}, pContent[512] = { 0} ; ::GetDlgItemTextA ( GetSafeHwnd(), IDC_NAG_TITLE, pTitle, 128 ) ; ::GetDlgItemTextA ( GetSafeHwnd(), IDC_NAG_CONTENT, pContent, 512 ) ; // 设置偏移信息(NagTitle + NagContent) dwNagContentOff = dwNagTitleOff + strlen(pTitle) + 1 ; dwNewEntryOff = dwNagContentOff + strlen(pContent) + 1 ; // 把NagTitle和NagContent信息写入缓冲区 wsprintf ( PTCHAR(&pData[dwNagTitleOff]), TEXT("%s"), pTitle ) ; wsprintf ( PTCHAR(&pData[dwNagContentOff]), TEXT("%s"), pContent ) ; // // Stub Code ( 0x11111111为数据占位符,需修正) 0x6a,push 命令, 0xe8,cal命令 // pushad 60 // push 0 6A 0x00 // push 0x11111111 68 11111111 // push 0x11111111 68 11111111 // push 0 6A 00 // call dword ptr MessageBoxW FF15 11111111 // popad 61 // jmp 0x11111111 E9 11111111 ///// //Stub数据 BYTE pbCommand[] = { 0x60, 0x6A, 0x00, 0x68, 0x11, 0x11, 0x11, 0x11, \ 0x68, 0x11, 0x11, 0x11, 0x11, 0x6A, 0x00, 0xFF, 0x15, 0xBC, 0xF2, 0x42, 0x00, 0x61, \ 0xE9, 0x11, 0x11, 0x11, 0x11 } ; // 统计Stub Code长度,然后写入pData缓冲区 DWORD dwStubLength = sizeof(pbCommand); memcpy ( &(pData[dwNewEntryOff]), pbCommand, dwStubLength ) ; // 计算 JMP 指令的操作数 DWORD dwOldEntryRVA = pOptionalHeader->AddressOfEntryPoint ; DWORD dwNewEntryRVA = PEInfo.NewSection.VirtualAddress + dwNewEntryOff + 22 ; DWORD dwJmpDist = dwOldEntryRVA - dwNewEntryRVA - 5 ; // 修正TitleVA *((DWORD*)(&(pData[dwNewEntryOff+4]))) = pOptionalHeader->ImageBase + PEInfo.NewSection.VirtualAddress + dwNagTitleOff ; // 修正ContentVA *((DWORD*)(&(pData[dwNewEntryOff+9]))) = pOptionalHeader->ImageBase + PEInfo.NewSection.VirtualAddress + dwNagContentOff ; // 修正 CALL [0x********] *((DWORD*)(&(pData[dwNewEntryOff+17]))) = pOptionalHeader->ImageBase + PEInfo.NewSection.VirtualAddress + dwIATOff ; // 修正 JMP 的操作数 ==> E9 ** ** ** ** *((DWORD*)(&(pData[dwNewEntryOff+23]))) = dwJmpDist ; // 返回总长度 return dwNewEntryOff + dwStubLength + 0x10 ;}
CString m_NagContent; afx_msg void OnBnClickedOk(); DWORD GenerateData(PBYTE pData, LPVOID lpImageBase);};