[DLL注入的方法]进程创建期修改PE输入表法

  进程创建期修改PE输入表法的原理和静态修改PE输入表完全相同,可以在R3/R0的各个阶段进行干预(必须在主线程运行之前)。

1.以读写方式打开目标文件:

2    //以读写方式打开目标文件
3     HANDLE hFile = CreateFile(szImageFilePath,
4         GENERIC_READ|GENERIC_WRITE,
5         FILE_SHARE_READ,
6         NULL,
7         OPEN_EXISTING,
8         FILE_ATTRIBUTE_NORMAL,
9         NULL);

这里可以加上个文件是否打开的判断,如果失败则返回,如果成功则开始获取目标进程中的PE结构信息。

2.1 首先将要解析PE格式的初始值定义成构造函数避免重复赋值:

CImage::CImage()
{
    m_hFile = INVALID_HANDLE_VALUE;
    m_hModule = NULL;
    m_pDosHeader = NULL;
    m_pFileHeader = NULL ;
    m_pRelocTable = NULL;
    m_pSecHeader = NULL;
    m_pExportDir = NULL;
    m_pImportDesp = NULL;
    m_pOptHeader = NULL;

    //页的大小
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    m_dwPageSize = sysinfo.dwPageSize;
}

2.2 然后将解析PE格式的代码封装成一个函数,方便调用

PBYTE CImage::LoadImage(HANDLE hFile, BOOL bDoReloc, ULONG_PTR RelocBase, BOOL bDoImport)
{
    WORD i=0;
    BYTE *pMemory=NULL;
    BYTE *MappedBase = NULL;
    PIMAGE_SECTION_HEADER pTmpSecHeader = NULL ;
    BOOL bResult = FALSE ;
    DWORD dwFileSize = 0 ; //一般PE文件大小不会超过4G
    DWORD dwIoCnt = 0 ;

    __try
    {
        
        m_hFile = hFile;
        //获取文件大小
        dwFileSize = GetFileSize(m_hFile,NULL);
        if (dwFileSize == 0)
        {
            lstrcpy(m_szErrorMsg,"文件大小为0!");
            __leave;
        }
        
        //读取PE头
        DWORD dwSizeToRead = (dwFileSize > PEHEADER_SIZE) ? PEHEADER_SIZE:dwFileSize;
        ZeroMemory(m_HeaderData,PEHEADER_SIZE);
        bResult = ReadFile(m_hFile,m_HeaderData,dwSizeToRead,&dwIoCnt,NULL);
        if (!bResult)
        {
            FormatErrorMsg("读取文件失败!",GetLastError());
            __leave;
        }
        
        if (!VerifyImage(m_HeaderData))
        {
            lstrcpy(m_szErrorMsg,"不是有效的PE映像!");
            __leave;
        }

        //解析各个PE头部结构
        
    m_hModule = m_HeaderData ;
    m_pDosHeader =(PIMAGE_DOS_HEADER)m_hModule ;
    m_pNtHeaders = (PIMAGE_NT_HEADERS)(m_hModule + m_pDosHeader->e_lfanew);
    m_pFileHeader = &m_pNtHeaders->FileHeader;
    m_SectionCnt = m_pFileHeader->NumberOfSections;
    m_pOptHeader = &m_pNtHeaders->OptionalHeader;
    m_pRelocTable = &(m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
    m_pSecHeader = (PIMAGE_SECTION_HEADER)((BYTE*)m_pOptHeader+sizeof(IMAGE_OPTIONAL_HEADER));
    m_dwEntryPoint = m_pOptHeader->AddressOfEntryPoint;
    m_TotalImageSize = m_pOptHeader->SizeOfImage ;
    m_ImageBase = (ULONG_PTR)m_pOptHeader->ImageBase ;

    //导入表
    m_pImpDataDir = &m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    //因为导入表可能会被修改,所以先保存旧的导入表数据
    m_OldImpDir.VirtualAddress = m_pImpDataDir->VirtualAddress;
    m_OldImpDir.Size = m_pImpDataDir->Size;
    if (m_pImpDataDir->VirtualAddress != NULL)
    {
        m_pImportDesp = (PIMAGE_IMPORT_DESCRIPTOR)(pBase + m_pImpDataDir->VirtualAddress);
    }

    //导出表
    m_pExpDataDir = &m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (m_pExpDataDir->VirtualAddress != NULL)
    {
        m_pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + m_pExpDataDir->VirtualAddress);
    }


        pTmpSecHeader = m_pSecHeader;
        //开始申请内存,为避免麻烦,这里直接申请可读可写可执行的内存
        pMemory = m_hModule = (BYTE*)VirtualAlloc(NULL,m_TotalImageSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        if (m_hModule == NULL)
        {
            lstrcpy(m_szErrorMsg,"内存不足,申请内存失败!");
            __leave;
        }
        //printf("m_hModule = 0x%X
",m_hModule);
        //先拷贝PE头到申请的内存
        memcpy(pMemory,m_HeaderData,m_pOptHeader->SizeOfHeaders);
        pMemory += GetAlignedSize(m_pOptHeader->SizeOfHeaders,m_pOptHeader->SectionAlignment);
        
        //printf("Section  VirtualAddress VirtualSize   PointertoRawData  RawSize
");
        //printf("=================================================================
");
        LARGE_INTEGER liFileOffset;
        for (i=0;i< m_SectionCnt;i++)
        {
            liFileOffset.QuadPart = pTmpSecHeader->PointerToRawData;
            bResult = SetFilePointerEx(m_hFile,liFileOffset,NULL,FILE_BEGIN);
            if (!bResult)
            {
                FormatErrorMsg("设置文件读写位置失败!",GetLastError());
                __leave;
            }
            
            //读取各个节
            bResult = ReadFile(m_hFile,pMemory,pTmpSecHeader->SizeOfRawData,&dwIoCnt,NULL);
            if (!bResult)
            {
                FormatErrorMsg("读取文件失败!",GetLastError());
                __leave;
            }
            pMemory += GetAlignedSize(pTmpSecHeader->Misc.VirtualSize,m_pOptHeader->SectionAlignment);
            pTmpSecHeader++;
        }
        
        //重新解析PE头
        InitializePEHeaders(m_hModule);
        
        //开始处理重定位数据
        if (bDoReloc)
        {
            //如果RelocBase为0,则按实际加载位置进行重定位
            ULONG_PTR BaseToReloc = (RelocBase == 0 )?(DWORD)m_hModule : RelocBase ;
            ProcessRelocTable(BaseToReloc);
        }
        
        //处理导入表
        if (bDoImport)
        {
            ProcessImportTable();
        }

        bResult = TRUE; //加载成功
    }
    __finally
    {
        if (!bResult)
        {
            if (m_hFile != INVALID_HANDLE_VALUE)
            {
                CloseHandle(m_hFile);
                m_hFile = INVALID_HANDLE_VALUE;
            }
        }
    }
    
    return m_hModule;
}

完整代码:

//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Image.h"
#include <shlwapi.h>

#pragma comment(lib,"shlwapi.lib")
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CImage::CImage()
{
    m_hFile = INVALID_HANDLE_VALUE;
    m_hModule = NULL;
    m_pDosHeader = NULL;
    m_pFileHeader = NULL ;
    m_pRelocTable = NULL;
    m_pSecHeader = NULL;
    m_pExportDir = NULL;
    m_pImportDesp = NULL;
    m_pOptHeader = NULL;

    //页的大小
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    m_dwPageSize = sysinfo.dwPageSize;
}

CImage::~CImage()
{
    Cleanup();
}

//************************************
// Method:    LoadPE
// FullName:  CImage::LoadPE
// Access:    public 
// Returns:   PBYTE
// Qualifier:
// Parameter: char * szPEPath , 待加载的PE模块的全路径
// Parameter: BOOL bDoReloc , 是否处理重定位
// Parameter: DWORD RelocBase , 重定位的基址,如果为0,则按实际加载位置重定位
// Parameter: BOOL bDoImport , 是否处理导入表
//************************************



PBYTE CImage::LoadImage(HANDLE hFile, BOOL bDoReloc, ULONG_PTR RelocBase, BOOL bDoImport)
{
    WORD i=0;
    BYTE *pMemory=NULL;
    BYTE *MappedBase = NULL;
    PIMAGE_SECTION_HEADER pTmpSecHeader = NULL ;
    BOOL bResult = FALSE ;
    DWORD dwFileSize = 0 ; //一般PE文件大小不会超过4G
    DWORD dwIoCnt = 0 ;

    __try
    {
        
        m_hFile = hFile;
        //获取文件大小
        dwFileSize = GetFileSize(m_hFile,NULL);
        if (dwFileSize == 0)
        {
            lstrcpy(m_szErrorMsg,"文件大小为0!");
            __leave;
        }
        
        //读取PE头
        DWORD dwSizeToRead = (dwFileSize > PEHEADER_SIZE) ? PEHEADER_SIZE:dwFileSize;
        ZeroMemory(m_HeaderData,PEHEADER_SIZE);
        bResult = ReadFile(m_hFile,m_HeaderData,dwSizeToRead,&dwIoCnt,NULL);
        if (!bResult)
        {
            FormatErrorMsg("读取文件失败!",GetLastError());
            __leave;
        }
        
        if (!VerifyImage(m_HeaderData))
        {
            lstrcpy(m_szErrorMsg,"不是有效的PE映像!");
            __leave;
        }

        //解析各个PE头部结构
        InitializePEHeaders(m_HeaderData);

        pTmpSecHeader = m_pSecHeader;
        //开始申请内存,为避免麻烦,这里直接申请可读可写可执行的内存
        pMemory = m_hModule = (BYTE*)VirtualAlloc(NULL,m_TotalImageSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        if (m_hModule == NULL)
        {
            lstrcpy(m_szErrorMsg,"内存不足,申请内存失败!");
            __leave;
        }
        //printf("m_hModule = 0x%X
",m_hModule);
        //先拷贝PE头到申请的内存
        memcpy(pMemory,m_HeaderData,m_pOptHeader->SizeOfHeaders);
        pMemory += GetAlignedSize(m_pOptHeader->SizeOfHeaders,m_pOptHeader->SectionAlignment);
        
        //printf("Section  VirtualAddress VirtualSize   PointertoRawData  RawSize
");
        //printf("=================================================================
");
        LARGE_INTEGER liFileOffset;
        for (i=0;i< m_SectionCnt;i++)
        {
            liFileOffset.QuadPart = pTmpSecHeader->PointerToRawData;
            bResult = SetFilePointerEx(m_hFile,liFileOffset,NULL,FILE_BEGIN);
            if (!bResult)
            {
                FormatErrorMsg("设置文件读写位置失败!",GetLastError());
                __leave;
            }
            
            //读取各个节
            bResult = ReadFile(m_hFile,pMemory,pTmpSecHeader->SizeOfRawData,&dwIoCnt,NULL);
            if (!bResult)
            {
                FormatErrorMsg("读取文件失败!",GetLastError());
                __leave;
            }
            pMemory += GetAlignedSize(pTmpSecHeader->Misc.VirtualSize,m_pOptHeader->SectionAlignment);
            pTmpSecHeader++;
        }
        
        //重新解析PE头
        InitializePEHeaders(m_hModule);
        
        //开始处理重定位数据
        if (bDoReloc)
        {
            //如果RelocBase为0,则按实际加载位置进行重定位
            ULONG_PTR BaseToReloc = (RelocBase == 0 )?(DWORD)m_hModule : RelocBase ;
            ProcessRelocTable(BaseToReloc);
        }
        
        //处理导入表
        if (bDoImport)
        {
            ProcessImportTable();
        }

        bResult = TRUE; //加载成功
    }
    __finally
    {
        if (!bResult)
        {
            if (m_hFile != INVALID_HANDLE_VALUE)
            {
                CloseHandle(m_hFile);
                m_hFile = INVALID_HANDLE_VALUE;
            }
        }
    }
    
    return m_hModule;
}

//以文件路径方式打开PE
PBYTE CImage::LoadImage(char *szPEPath, BOOL bDoReloc, ULONG_PTR RelocBase ,BOOL bDoImport)
{
    //保存PE路径
    lstrcpy(m_szPEPath,szPEPath);
    //以只读方式打开文件
    m_hFile = CreateFile(szPEPath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if (m_hFile == INVALID_HANDLE_VALUE)
    {
        FormatErrorMsg("打开文件失败!",GetLastError());
        return NULL;
    }

    return LoadImage(m_hFile,bDoReloc,RelocBase,bDoImport);
}

VOID CImage::FreePE()
{
    VirtualFree(m_hModule,0,MEM_RELEASE);
    m_hModule = NULL;
}



DWORD CImage::GetAlignedSize(DWORD theSize, DWORD Alignment)
{
    DWORD dwAlignedVirtualSize=0;
    dwAlignedVirtualSize = ALIGN_SIZE_UP(theSize,Alignment);
    return dwAlignedVirtualSize;//返回对齐后的大小
}


ULONG_PTR CImage::GetAlignedPointer(ULONG_PTR uPointer, DWORD Alignment)
{
    DWORD dwAlignedAddress=0;
    dwAlignedAddress = ALIGN_SIZE_UP(uPointer,Alignment);
    return dwAlignedAddress;//返回对齐后的大小
}

/*
DWORD CImage::GetAlignedSize(DWORD theSize, DWORD Alignment)
{
    DWORD dwAlignedVirtualSize=0;
    DWORD moded=0,dived=0;
    dived=theSize/Alignment;
    moded=theSize%Alignment;
    if (moded)//有余数
    {
        dwAlignedVirtualSize=dived*Alignment;
        dwAlignedVirtualSize+=Alignment;
    }
    else
    {
        dwAlignedVirtualSize=theSize;
    }
    //printf("Recevid Size=%08X	dived=%X	moded=%X
",theSize,dived,moded);
    return dwAlignedVirtualSize;//返回对齐后的大小
}
*/

DWORD CImage::_GetProcAddress(PBYTE pModule, char *szFuncName)
{
    //自己实现GetProcAddress
    DWORD retAddr=0;
    DWORD *namerav,*funrav;
    DWORD cnt=0;
    DWORD max,min,mid;
    WORD *nameOrdinal;
    WORD nIndex=0;
    int cmpresult=0;
    char *ModuleBase=(char*)pModule;
    char *szMidName = NULL ;
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS pNtHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader;
    PIMAGE_EXPORT_DIRECTORY pExportDir;

    if (ModuleBase == NULL)
    {
        return 0;
    }

    pDosHeader=(PIMAGE_DOS_HEADER)ModuleBase;
    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        return 0;
    }
    pNtHeader = (PIMAGE_NT_HEADERS)(ModuleBase+pDosHeader->e_lfanew);
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
    {
        return 0 ;
    }

    pOptHeader = &pNtHeader->OptionalHeader;
    pExportDir=(PIMAGE_EXPORT_DIRECTORY)(ModuleBase+pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    namerav=(DWORD*)(ModuleBase+pExportDir->AddressOfNames);
    funrav=(DWORD*)(ModuleBase+pExportDir->AddressOfFunctions);
    nameOrdinal=(WORD*)(ModuleBase+pExportDir->AddressOfNameOrdinals);

    if ((DWORD)szFuncName < 0x0000FFFF)
    {
        retAddr=(DWORD)(ModuleBase+funrav[(WORD)szFuncName]);
    }
    else
    {
        //二分法查找
        max = pExportDir->NumberOfNames ;
        min = 0;
        mid = (max+min)/2;
        while (min < max)
        {
            szMidName = ModuleBase+namerav[mid] ;
            cmpresult=strcmp(szFuncName,szMidName);
            if (cmpresult < 0)
            {
                //比中值小,则取中值-1为最大值
                max = mid -1 ;
            }
            else if (cmpresult > 0)
            {
                //比中值大,则取中值+1为最小值
                min = mid + 1;
            }
            else
            {
                break;
            }
            mid=(max+min)/2;
            
        }
        
        if (strcmp(szFuncName,ModuleBase+namerav[mid]) == 0)
        {
            nIndex=nameOrdinal[mid];
            retAddr=(DWORD)(ModuleBase+funrav[nIndex]);
        }
    }
    return retAddr;
}

DWORD CImage::GetTotalImageSize(DWORD Alignment)
{
    DWORD TotalSize=0;
    DWORD tmp=0;
    PIMAGE_SECTION_HEADER pTmpSecHeader = m_pSecHeader;
    TotalSize+=GetAlignedSize(m_pOptHeader->SizeOfHeaders,Alignment);
    for (WORD i=0;i< m_SectionCnt;i++)
    {
        tmp=GetAlignedSize(pTmpSecHeader->Misc.VirtualSize,Alignment);
        TotalSize+=tmp;
        pTmpSecHeader++;
    }
    return TotalSize;
}

DWORD CImage::Rav2Raw(DWORD VirtualAddr)
{
    DWORD RawAddr=0;
    if (VirtualAddr < m_pOptHeader->SizeOfHeaders)
    {
        RawAddr = VirtualAddr;
        return RawAddr;
    }
    PIMAGE_SECTION_HEADER pTmpSecHeader = LocateSectionByRVA(VirtualAddr);
    if (pTmpSecHeader != NULL)
    {
        RawAddr = VirtualAddr - pTmpSecHeader->VirtualAddress + pTmpSecHeader->PointerToRawData;
    }

    return RawAddr;
}

DWORD CImage::Raw2Rav(DWORD RawAddr)
{
    DWORD RavAddr=0;
    if (RawAddr < m_pOptHeader->SizeOfHeaders)
    {
        RavAddr = RawAddr;
        return RavAddr;
    }
    PIMAGE_SECTION_HEADER pTmpSecHeader = LocateSectionByRawOffset(RawAddr);
    if (pTmpSecHeader != NULL)
    {
        RavAddr = RawAddr - pTmpSecHeader->PointerToRawData + pTmpSecHeader->VirtualAddress;
    }
    
    return RavAddr;
}


VOID CImage::FormatErrorMsg(char *szPrompt, DWORD ErrCode)
{
    LPVOID lpMsgBuf;
    FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        ErrCode,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &lpMsgBuf,
        0,
        NULL 
        );
    sprintf(m_szErrorMsg,"%s 错误代码:%d 原因:%s",szPrompt,ErrCode,(LPCTSTR)lpMsgBuf);
    LocalFree( lpMsgBuf );
}

VOID CImage::Cleanup()
{
    if (m_hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hFile);
        m_hFile = INVALID_HANDLE_VALUE ;
    }
    
    if (m_hModule != NULL)
    {
        FreePE();
    }
}

VOID CImage::InitializePEHeaders(PBYTE pBase)
{
    //解析各个PE头部结构
    m_hModule = pBase ;
    m_pDosHeader =(PIMAGE_DOS_HEADER)pBase;
    m_pNtHeaders = (PIMAGE_NT_HEADERS)(pBase + m_pDosHeader->e_lfanew);
    m_pFileHeader = &m_pNtHeaders->FileHeader;
    m_SectionCnt = m_pFileHeader->NumberOfSections;
    m_pOptHeader = &m_pNtHeaders->OptionalHeader;
    m_pRelocTable = &(m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
    m_pSecHeader = (PIMAGE_SECTION_HEADER)((BYTE*)m_pOptHeader+sizeof(IMAGE_OPTIONAL_HEADER));
    m_dwEntryPoint = m_pOptHeader->AddressOfEntryPoint;
    m_TotalImageSize = m_pOptHeader->SizeOfImage ;
    m_ImageBase = (ULONG_PTR)m_pOptHeader->ImageBase ;

    //导入表
    m_pImpDataDir = &m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    //因为导入表可能会被修改,所以先保存旧的导入表数据
    m_OldImpDir.VirtualAddress = m_pImpDataDir->VirtualAddress;
    m_OldImpDir.Size = m_pImpDataDir->Size;
    if (m_pImpDataDir->VirtualAddress != NULL)
    {
        m_pImportDesp = (PIMAGE_IMPORT_DESCRIPTOR)(pBase + m_pImpDataDir->VirtualAddress);
    }

    //导出表
    m_pExpDataDir = &m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (m_pExpDataDir->VirtualAddress != NULL)
    {
        m_pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + m_pExpDataDir->VirtualAddress);
    }
    
    
    
}

VOID CImage::ProcessRelocTable(ULONG_PTR RelocBase)
{
    WORD i = 0 ;
    PIMAGE_BASE_RELOCATION pRelocBlock = NULL ;
    if (m_pRelocTable->VirtualAddress != NULL)
    {
        pRelocBlock=(PIMAGE_BASE_RELOCATION)(m_hModule + m_pRelocTable->VirtualAddress);
        //printf("After Loaded,Reloc Table=0x%08X
",pRelocBlock);
        do
        {//处理一个接一个的重定位块,最后一个重定位块以RAV=0结束
            //需要重定位的个数,是本块的大小减去块头的大小,结果是以DWORD表示的大小
            //而重定位数据是16位的,那就得除以2
            int numofReloc=(pRelocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION))/2;
            //printf("Reloc Data num=%d
",numofReloc);
            //重定位数据是16位的
            WORD offset=0;
            WORD *pRelocData=(WORD*)((BYTE*)pRelocBlock + sizeof(IMAGE_BASE_RELOCATION));
            for (i=0;i<numofReloc;i++)//循环,或直接判断*pData是否为0也可以作为结束标记
            {
                ULONG_PTR *RelocAddress=0;//需要重定位的地址
#ifdef _WIN64
                WORD RelocFlag = IMAGE_REL_BASED_DIR64 ;
#else
                WORD RelocFlag = IMAGE_REL_BASED_HIGHLOW ;
#endif
                //IMAGE_REL_BASED_DIR64
                //重定位的高4位是重定位类型,
                if (((*pRelocData)>>12) == RelocFlag)//判断重定位类型是否为IMAGE_REL_BASED_HIGHLOW,x86
                {
                    //计算需要进行重定位的地址
                    //重定位数据的低12位再加上本重定位块头的RAV即真正需要重定位的数据的RAV
                    offset=(*pRelocData)&0xFFF;//小偏移
                    RelocAddress=(ULONG_PTR*)(m_hModule+pRelocBlock->VirtualAddress+offset);
                    //对需要重定位的数据进行修正
                    //修正方法:减去IMAGE_OPTINAL_HEADER中的基址,再加上新的基址即可
                    *RelocAddress=*RelocAddress - m_pOptHeader->ImageBase + RelocBase;
                }
                pRelocData++;
                
            }
            //指向下一个重定位块
            pRelocBlock=(PIMAGE_BASE_RELOCATION)((char*)pRelocBlock+pRelocBlock->SizeOfBlock);
            
        }while (pRelocBlock->VirtualAddress);
    }
}

BOOL CImage::ProcessImportTable()
{
    BOOL bResult = TRUE ;
    char szPreDirectory[MAX_PATH]={0};
    char szCurDirectory[MAX_PATH]={0};
    char szPrompt[256]={0};
    PIMAGE_IMPORT_DESCRIPTOR  pImportDescriptor = m_pImportDesp;
    PIMAGE_THUNK_DATA         NameThunk = NULL , AddrThunk = NULL;
    PIMAGE_IMPORT_BY_NAME      pImpName = NULL ;
    HMODULE hMod = NULL ;
    char *szImpModName = NULL ;

    if (pImportDescriptor == NULL)
    {
        //无导入表,不需要处理
        return TRUE;
    }

    //更改当前路径,否则加载某些第三方dll时将找不到模块
    GetCurrentDirectory(MAX_PATH,szPreDirectory);
    lstrcpy(szCurDirectory,m_szPEPath);
    PathRemoveFileSpec(szCurDirectory);
    SetCurrentDirectory(szCurDirectory);
    
    //
    // Walk through the IAT and snap all the thunks.
    //
    
    while (pImportDescriptor->Name && pImportDescriptor->OriginalFirstThunk)
    {
        szImpModName = (char*)m_hModule + pImportDescriptor->Name ;
        hMod = LoadLibrary(szImpModName);
        if (hMod == NULL)
        {
            sprintf(szPrompt,"加载导入表模块 %s 失败!",szImpModName);
            FormatErrorMsg(szImpModName,GetLastError());
            return FALSE;
        }
        
        //printf("处理导入表模块 : %s
",szImpModName);
        NameThunk = (PIMAGE_THUNK_DATA)(m_hModule + (ULONG)pImportDescriptor->OriginalFirstThunk);
        AddrThunk = (PIMAGE_THUNK_DATA)(m_hModule + (ULONG)pImportDescriptor->FirstThunk);
        
        while (NameThunk->u1.AddressOfData)
        {
            bResult = SnapThunk (hMod,szImpModName,m_hModule,NameThunk,AddrThunk);
            if (!bResult)
            {
                bResult = FALSE ;
                break;
            }
            NameThunk++;
            AddrThunk++;
        }
        
        if (!bResult)
        {
            break;
        }
        pImportDescriptor++;
    }
    
    SetCurrentDirectory(szPreDirectory);
    return bResult;
}

BOOL CImage::SnapThunk(HMODULE hImpMode,char *szImpModeName,PBYTE ImageBase, PIMAGE_THUNK_DATA NameThunk, PIMAGE_THUNK_DATA AddrThunk)
{
    BOOL bResult = FALSE ;
    PIMAGE_IMPORT_BY_NAME      pImpName = NULL ;
    DWORD dwFunAddr = 0 ;
    ULONG Ordinal = 0 ;

    if (NameThunk->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)
    {
        Ordinal = IMAGE_ORDINAL(NameThunk->u1.Ordinal);
        dwFunAddr = (DWORD)GetProcAddress(hImpMode,(LPCSTR)Ordinal);
        //printf("0x%08X 按序号导入 : %d
",dwFunAddr,Ordinal);
        if (dwFunAddr == 0)
        {
            sprintf(m_szErrorMsg,"无法在导入模块%s中定位导入函数:%d (序号)",szImpModeName,Ordinal);
        }
    }
    else
    {
        pImpName = (PIMAGE_IMPORT_BY_NAME)(m_hModule + (ULONG)NameThunk->u1.AddressOfData);
        dwFunAddr = (DWORD)GetProcAddress(hImpMode,(LPCSTR)pImpName->Name);
        //printf("0x%08X 按名称导入 : %s
",dwFunAddr,pImpName->Name);
        if (dwFunAddr == 0)
        {
            sprintf(m_szErrorMsg,"无法在导入模块%s中定位导入函数:%s ",szImpModeName,pImpName->Name);
        }
    }

    if (dwFunAddr != 0)
    {
        AddrThunk->u1.Function = dwFunAddr;
        bResult = TRUE;
    }

    return bResult;
}

//根据PE文件头,简单判断PE文件的有效性
BOOL CImage::VerifyImage(PVOID pBase)
{
    //解析各个PE头部结构
    m_pDosHeader =(PIMAGE_DOS_HEADER)pBase;
    if (m_pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        return FALSE;
    }
    m_pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBase + m_pDosHeader->e_lfanew);
    if (m_pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
    {
        return FALSE;
    }
    
    return TRUE;
}

LPSTR CImage::GetErrorMsg(char *szBuf, int BufSize)
{
    int len = lstrlen(m_szErrorMsg);
    if (len <= BufSize )
    {
        lstrcpy(szBuf,m_szErrorMsg);
        return szBuf;
    }
    return NULL;
}

//根据相对虚拟地址查找所在的节
PIMAGE_SECTION_HEADER CImage::LocateSectionByRVA(DWORD dwRVA)
{
    WORD i = 0;
    PIMAGE_SECTION_HEADER pTemp = m_pSecHeader;
    for (i=0;i<m_SectionCnt;i++)
    {
        if (pTemp->VirtualAddress <= dwRVA
            && dwRVA <(pTemp->VirtualAddress + pTemp->Misc.VirtualSize))
        {
            return pTemp;
        }
        pTemp++;
    }
    return NULL;
}

//根据文件偏移确定所在的节
PIMAGE_SECTION_HEADER CImage::LocateSectionByRawOffset(DWORD dwRawOffset)
{
    WORD i = 0;
    PIMAGE_SECTION_HEADER pTemp = m_pSecHeader;
    for (i=0;i<m_SectionCnt;i++)
    {
        if (pTemp->PointerToRawData <= dwRawOffset
            && dwRawOffset <(pTemp->PointerToRawData + pTemp->SizeOfRawData))
        {
            return pTemp;
        }
        pTemp++;
    }
    return NULL;
}

//计算某个节按虚拟地址对齐后的空隙大小
DWORD CImage::GetSectionVirtualPaddingSize(PIMAGE_SECTION_HEADER pSecHeader)
{
    DWORD AlignedSize = GetAlignedSize(pSecHeader->Misc.VirtualSize,m_pOptHeader->SectionAlignment);
    return AlignedSize - pSecHeader->Misc.VirtualSize;
}

//计算某个节按虚拟地址对齐后的空隙大小
//VirtualSize和RawSize不确定哪个比较大
DWORD CImage::GetSectionPhysialPaddingSize(PIMAGE_SECTION_HEADER pSecHeader)
{
    DWORD dwPaddingSize = 0 ;
    if (pSecHeader->Misc.VirtualSize < pSecHeader->SizeOfRawData)
    {
        //节的内存大小小于文件大小
        /*
        .text name
        7748 virtual size
        1000 virtual address
        7800 size of raw data
        */
        dwPaddingSize = pSecHeader->SizeOfRawData - pSecHeader->Misc.VirtualSize ;
    }
    else
    {
        //节的内存大小大于等于文件中的大小,则认为不存在空隙
        dwPaddingSize = 0 ;

    }
    return dwPaddingSize;
}

//默认情况下是以只读方式打开目标文件的,必要时转换为可写的
BOOL CImage::MakeFileHandleWritable()
{
    BOOL bResult = FALSE;
    HANDLE hNew = INVALID_HANDLE_VALUE;
    HANDLE hProc = OpenProcess(PROCESS_DUP_HANDLE,FALSE,GetCurrentProcessId());
    if (hProc == NULL)
    {
        return FALSE;
    }
    bResult = DuplicateHandle(
        hProc,
        m_hFile,
        hProc,
        &hNew,
        GENERIC_READ,
        FALSE,
        0
        );

    if (bResult)
    {
        CloseHandle(m_hFile);//关闭之前的Handle
        m_hFile = hNew;
    }
    else
    {
        FormatErrorMsg("更改句柄权限失败!",GetLastError());
    }
    
    CloseHandle(hProc);
    return bResult;
}

//解析当前内存中的PE结构
VOID CImage::AttachToMemory(PVOID pMemory)
{
    if (pMemory != NULL)
    {
        InitializePEHeaders((PBYTE)pMemory);
    }
}

//解析其它进程中的PE结构
BOOL CImage::AttachToProcess(HANDLE hProc ,PVOID ProcessImageBase)
{
    BOOL bResult = FALSE ;
    SIZE_T dwIoCnt = 0;
    m_hProc = hProc;
    m_ImageBase = (ULONG_PTR)ProcessImageBase;
    bResult = ReadProcessMemory(m_hProc,(LPVOID)m_ImageBase,m_HeaderData,0x1000,&dwIoCnt);
    if (!bResult)
    {
        FormatErrorMsg("ReadProcessMemory失败!",GetLastError());
        return FALSE;
    }

    //初始化PE结构
    InitializePEHeaders(m_HeaderData);
    return bResult;
}

//在文件中添加一个新节并返回新节的数据
PIMAGE_SECTION_HEADER CImage::AddNewSectionToFile(char *szSectionName, DWORD SectionSize)
{
    PIMAGE_SECTION_HEADER pNewSecHeader = m_pSecHeader + m_SectionCnt ;
    PIMAGE_SECTION_HEADER pLastSecHeader = m_pSecHeader + m_SectionCnt  - 1;
    DWORD dwSectionVA,dwSectionRawOffset,dwSectionSize;
    LARGE_INTEGER liFileOffset;
    BOOL bResult = FALSE ;
    DWORD dwIoCnt = 0 ;
    
    //计算新节的起始虚拟内存偏移
    dwSectionVA = pLastSecHeader->VirtualAddress + GetAlignedSize(pLastSecHeader->Misc.VirtualSize,m_pOptHeader->SectionAlignment);
    //计算新节的物理起始偏移
    dwSectionRawOffset = pLastSecHeader->PointerToRawData + GetAlignedSize(pLastSecHeader->SizeOfRawData,m_pOptHeader->FileAlignment);
    //计算新节的大小,按文件对齐粒度对齐
    dwSectionSize = GetAlignedSize(SectionSize,m_pOptHeader->FileAlignment);
    
    //设置文件指针位置
    liFileOffset.QuadPart = dwSectionRawOffset + dwSectionSize;
    bResult = SetFilePointerEx(m_hFile,liFileOffset,NULL,FILE_BEGIN);
    if (!bResult)
    {
        FormatErrorMsg("添加新节时设置文件指针错误!",GetLastError());
        return NULL;
        
    }

    bResult = SetEndOfFile(m_hFile);
    if (!bResult)
    {
        FormatErrorMsg("添加新节时设置文件结束位置错误!",GetLastError());
        return NULL;
        
    }
    
    //填充SectionHeader
    ZeroMemory(pNewSecHeader,sizeof(IMAGE_SECTION_HEADER));
    strncpy((char*)pNewSecHeader->Name,szSectionName,8);
    pNewSecHeader->Misc.VirtualSize = dwSectionSize;
    pNewSecHeader->VirtualAddress = dwSectionVA;
    pNewSecHeader->PointerToRawData = dwSectionRawOffset ;
    pNewSecHeader->SizeOfRawData = dwSectionSize;
    pNewSecHeader->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;
    
    //更新PE头中的节个数
    m_pFileHeader->NumberOfSections += 1;
    m_SectionCnt++ ;
    //更新PE头中的总映像大小
    m_pOptHeader->SizeOfImage += dwSectionSize;

    //保存PE头到文件中
    liFileOffset.QuadPart = 0;
    bResult = SetFilePointerEx(m_hFile,liFileOffset,NULL,FILE_BEGIN);
    if (!bResult)
    {
        FormatErrorMsg("添加新节保存PE时设置文件指针错误!",GetLastError());
        return NULL;
        
    }

    bResult = WriteFile(m_hFile,m_hModule,m_pOptHeader->SizeOfHeaders,&dwIoCnt,NULL);
    if (!bResult)
    {
        FormatErrorMsg("添加新节保存PE时写入文件错误!",GetLastError());
        return NULL;
        
    }
    
    FlushFileBuffers(m_hFile);
    return pNewSecHeader;
}

//在内存中添加一个新节并返回新节的数据
PIMAGE_SECTION_HEADER CImage::AddNewSectionToMemory(char *szSectionName, DWORD SectionSize)
{
    PIMAGE_SECTION_HEADER pNewSecHeader = m_pSecHeader + m_SectionCnt ;
    PIMAGE_SECTION_HEADER pLastSecHeader = m_pSecHeader + m_SectionCnt  - 1;
    DWORD dwSectionVA,dwSectionRawOffset,dwSectionSize;
    BOOL bResult = FALSE ;
    SIZE_T dwIoCnt = 0 ;
    HANDLE hProc = (m_hProc == NULL )? GetCurrentProcess():m_hProc;
    ULONG_PTR  HighestUserAddress = 0;
    BYTE PEHeader[0x1000]={0};
    
    //获取基本的地址空间信息
    SYSTEM_INFO sysinfo;
    ZeroMemory(&sysinfo,sizeof(SYSTEM_INFO));
    GetSystemInfo(&sysinfo);
    HighestUserAddress = (ULONG_PTR)sysinfo.lpMaximumApplicationAddress;
    
    //计算新节的起始虚拟内存偏移
    dwSectionVA = pLastSecHeader->VirtualAddress + ALIGN_SIZE_UP(pLastSecHeader->Misc.VirtualSize,m_pOptHeader->SectionAlignment);
    //计算新节的物理起始偏移
    dwSectionRawOffset = pLastSecHeader->PointerToRawData + GetAlignedSize(pLastSecHeader->SizeOfRawData,m_pOptHeader->FileAlignment);
    //计算新节的大小,按文件对齐粒度对齐
    dwSectionSize = GetAlignedSize(SectionSize,m_pOptHeader->FileAlignment);
    
    ULONG_PTR dwNewSectionStartAddr = m_ImageBase + dwSectionVA;
    ULONG_PTR AddressToAlloc = GetAlignedPointer(dwNewSectionStartAddr,sysinfo.dwAllocationGranularity);
    PBYTE AllocatedMem = NULL ;
    //从PE最后一个节开始,向后申请内存
    for (AddressToAlloc = dwNewSectionStartAddr; AddressToAlloc < HighestUserAddress;AddressToAlloc += sysinfo.dwAllocationGranularity)
    {
        //申请地址
        AllocatedMem = (PBYTE)VirtualAllocEx(hProc,(PVOID)AddressToAlloc,dwSectionSize,MEM_RESERVE |MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        if (AllocatedMem != NULL)
        {
            break;
        }
    }
    
    if (AllocatedMem == NULL)
    {
        FormatErrorMsg("添加新节时在目标进程中申请内存失败!",GetLastError());
        return NULL;
    }
    //printf("[*] NewSection Address = 0x%p Size = 0x%X
",AllocatedMem,dwSectionSize);
    dwSectionVA = FILED_OFFSET(AllocatedMem,m_ImageBase);

    //填充SectionHeader
    ZeroMemory(pNewSecHeader,sizeof(IMAGE_SECTION_HEADER));
    strncpy((char*)pNewSecHeader->Name,szSectionName,8);
    pNewSecHeader->Misc.VirtualSize = dwSectionSize;
    pNewSecHeader->VirtualAddress = dwSectionVA;
    pNewSecHeader->PointerToRawData = dwSectionRawOffset ;
    pNewSecHeader->SizeOfRawData = dwSectionSize;
    pNewSecHeader->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;
    
    //更新PE头中的节个数
    m_pFileHeader->NumberOfSections += 1;
    m_SectionCnt++ ;
    //更新PE头中的总映像大小
    m_pOptHeader->SizeOfImage += dwSectionSize;
    
    
    //更新目标进程的PE头
    DWORD dwOldProtect = 0 ;
    bResult = VirtualProtectEx(hProc,(LPVOID)m_ImageBase,m_pOptHeader->SizeOfHeaders,PAGE_READWRITE,&dwOldProtect);
    if (!bResult)
    {
        FormatErrorMsg("修改目标进程内存属性时失败!",GetLastError());
        return NULL;
    }
    
    bResult = WriteProcessMemory(hProc,(LPVOID)m_ImageBase,m_HeaderData,m_pOptHeader->SizeOfHeaders,&dwIoCnt);
    if (!bResult)
    {
        FormatErrorMsg("向目标进程写入PE头数据时错误!",GetLastError());
        return NULL;
        
    }

    return pNewSecHeader;
}

PIMAGE_SECTION_HEADER CImage::ExtraLastSectionSizeToFile(DWORD SectionAddSize)
{
    PIMAGE_SECTION_HEADER pLastSecHeader = m_pSecHeader + m_SectionCnt  - 1;
    DWORD dwSectionNewVirtualSize,dwSectionNewRawOffset,dwSectionNewRawSize;
    DWORD dwOldSectionVirtualSize = 0 ;
    LARGE_INTEGER liFileOffset;
    BOOL bResult = FALSE ;
    DWORD dwIoCnt = 0 ;
    
    //在扩展最后一个节的情况下,需要更改最后一个节的RawSize和VirtualSize,起始偏移均不变
    //计算新节的物理大小,按文件对齐粒度对齐
    dwSectionNewRawOffset = pLastSecHeader->PointerToRawData ;
    dwSectionNewRawSize = GetAlignedSize(pLastSecHeader->SizeOfRawData + SectionAddSize,m_pOptHeader->FileAlignment);
    dwOldSectionVirtualSize = dwSectionNewVirtualSize =  GetAlignedSize(pLastSecHeader->Misc.VirtualSize , m_pOptHeader->SectionAlignment);
    //计处新节的VirtualSize大小,仅当内存大小小于文件大小时,需要增加
    if (pLastSecHeader->Misc.VirtualSize < dwSectionNewRawSize)
    {
        dwSectionNewVirtualSize += SectionAddSize;
    }
    
    //设置文件指针位置
    liFileOffset.QuadPart = dwSectionNewRawOffset +  pLastSecHeader->SizeOfRawData + SectionAddSize;
    bResult = SetFilePointerEx(m_hFile,liFileOffset,NULL,FILE_BEGIN);
    if (!bResult)
    {
        FormatErrorMsg("添加新节时设置文件指针错误!",GetLastError());
        return NULL;
        
    }
    
    bResult = SetEndOfFile(m_hFile);
    if (!bResult)
    {
        FormatErrorMsg("添加新节时设置文件结束位置错误!",GetLastError());
        return NULL;
        
    }
    
    //填充SectionHeader
    pLastSecHeader->Misc.VirtualSize = dwSectionNewVirtualSize;
    pLastSecHeader->SizeOfRawData = dwSectionNewRawSize;
    pLastSecHeader->Characteristics |=  IMAGE_SCN_MEM_READ;
    
    //更新PE头中的总映像大小
    m_pOptHeader->SizeOfImage = m_pOptHeader->SizeOfImage - dwOldSectionVirtualSize 
        + GetAlignedSize(pLastSecHeader->Misc.VirtualSize,m_pOptHeader->SectionAlignment);
    
    //保存PE头到文件中
    liFileOffset.QuadPart = 0;
    bResult = SetFilePointerEx(m_hFile,liFileOffset,NULL,FILE_BEGIN);
    if (!bResult)
    {
        FormatErrorMsg("添加新节保存PE时设置文件指针错误!",GetLastError());
        return NULL;
        
    }
    
    bResult = WriteFile(m_hFile,m_hModule,m_pOptHeader->SizeOfHeaders,&dwIoCnt,NULL);
    if (!bResult)
    {
        FormatErrorMsg("添加新节保存PE时写入文件错误!",GetLastError());
        return NULL;
        
    }
    
    FlushFileBuffers(m_hFile);
    return pLastSecHeader;
    
}
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include "Image.h"

#define INFECT_SIG ('PE')

BOOL AddSectionTest();
BOOL InfectImport(
    IN char *szImageFilePath,
    IN char *szDllPath,
    IN char *szDllExportFunName
    );

int main(int argc, char* argv[])
{
#ifdef _WIN64
    InfectImport("F:\Program2016\DllInjection\HostProc64.exe","Msgdll64.dll","Msg");
#else
    InfectImport("F:\Program2016\DllInjection\HostProc.exe","Msgdll.dll","Msg");
#endif
    return 0;
}

BOOL InfectImport(
    IN char *szImageFilePath,
    IN char *szDllName,
    IN char *szDllExportFunName)
{
    CImage Img;   
    BOOL bResult = FALSE ;
    WORD i = 0 ;
    DWORD dwIoCnt = 0 ;
    char szErrMsg[1024]={0};
    PIMAGE_SECTION_HEADER pImpSecHeader,pNewSecHeader = NULL,pTargetSecHeader = NULL;
    DWORD dwOldIIDCnt = 0 ,dwNewIIDCnt = 0 ;
    DWORD dwOldIIDSize = 0, dwNewIIDSize = 0 ;
    DWORD dwVAToStoreNewIID = 0 ; //新IID数组的存储位置(新导入表)
    DWORD dwnewThunkDataSize = 0 ; //新IID项的ThunkData的存储位置
    DWORD dwNewThunkDataVA = 0 ;//新IID项的ThunkData的存储位置
    DWORD dwSizeNeed = 0 ;
    DWORD dwThunkDataOffsetByIID = 0 ;
    BOOL bUseNewSection = FALSE ; //是否使用了新节
    BOOL bPlaceThunkDataToOldIID = TRUE ; //表明ThunkData存放的位置是不是在原来的IID位置,如果放不下,得找新位置
    
    printf("[*] Path = %s
",szImageFilePath);
    //以读写方式打开目标文件
    HANDLE hFile = CreateFile(szImageFilePath,
        GENERIC_READ|GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    //解析PE结构
    PBYTE pNotepad = Img.LoadImage(hFile,FALSE,0,FALSE);
    printf("[*] pImageBase = 0x%p
",pNotepad);

    if (pNotepad == NULL)
    {
        printf("[-] 加载PE文件失败! %s
",Img.GetErrorMsg(szErrMsg,1024));
        return FALSE;
    }
    
    //检查是否被感染过
    if (Img.m_pDosHeader->e_csum == INFECT_SIG)
    {
        printf("[-] 文件已经被感染过!
");
        return FALSE;
    }
    printf("[*] 当前导入表信息 VA = 0x%p Size = 0x%X
",Img.m_pImpDataDir->VirtualAddress,Img.m_pImpDataDir->Size);
    dwOldIIDSize = Img.m_pImpDataDir->Size ;
    dwOldIIDCnt = dwOldIIDSize / sizeof(IMAGE_IMPORT_DESCRIPTOR) ;
    dwNewIIDCnt = dwOldIIDCnt + 1;
    dwNewIIDSize = dwNewIIDCnt * sizeof(IMAGE_IMPORT_DESCRIPTOR) ;
    printf("[*] dwOldIIDCnt = %d  Size = 0x%X
",dwOldIIDCnt,dwOldIIDSize);
    printf("[*] dwNewIIDCnt = %d  Size = 0x%X
",dwNewIIDCnt,dwNewIIDSize);
    
    dwSizeNeed = dwNewIIDSize; //所需的大小是新导入表IID结构的大小

    pImpSecHeader = Img.LocateSectionByRVA(Img.m_pImpDataDir->VirtualAddress);
    printf("[*] 导入表所在节  %s  RawOffset = 0x%X Size = 0x%X
",
        pImpSecHeader->Name,
        pImpSecHeader->PointerToRawData,
        pImpSecHeader->SizeOfRawData);
    
    DWORD dwPaddingSize = Img.GetSectionPhysialPaddingSize(pImpSecHeader);
    printf("[*] 导入表节空隙大小 = 0x%X
",dwPaddingSize);
    
    //计算填充ThunkData需要的大小,它包括了OriginalFirstThunk、FirstThunk、IMPORT_BY_NAME,以及DllName
    dwnewThunkDataSize = sizeof(ULONG_PTR)*4 + strlen(szDllName) + 1 + sizeof(WORD) + strlen(szDllExportFunName) + 1 ;
    dwnewThunkDataSize = ALIGN_SIZE_UP(dwnewThunkDataSize,sizeof(ULONG)); //对齐
    //判断原导入表位置能否写下新的ThunkData
    if (dwnewThunkDataSize > dwOldIIDSize)
    {
        //写不下,那么在寻找节隙时就要加上
        //按ULONG_PTR对齐之后再添加ThunkData,虽然不按这个对齐也可以
        dwThunkDataOffsetByIID = ALIGN_SIZE_UP(dwNewIIDSize,sizeof(ULONG_PTR)) ;
        dwSizeNeed = dwThunkDataOffsetByIID + dwnewThunkDataSize ;
        bPlaceThunkDataToOldIID = FALSE ;
    }
    printf("[*] 放置新导入表数据所需要的大小 = 0x%X
",dwSizeNeed);
    //dwPaddingSize = 0 ;//测试,强制添加新节
    if (dwPaddingSize >= dwSizeNeed)
    {
        printf("[*] 节空隙可以放下新的导入表,不需添加新节!
");
        dwVAToStoreNewIID = pImpSecHeader->VirtualAddress + Img.GetAlignedSize(pImpSecHeader->Misc.VirtualSize,sizeof(DWORD));
        pTargetSecHeader = pImpSecHeader;
    }
    else
    {
        printf("[-] 节空隙不能放下新的导入表,需要添加新节!
");
        //根据所需的空间大小添加一个新节
        pNewSecHeader = Img.AddNewSectionToFile(".Patch",dwSizeNeed);
        printf("[*] 新节添加完毕! VA = 0x%X  RawOffset = 0x%X  RawSize = 0x%X
",
            pNewSecHeader->VirtualAddress,pNewSecHeader->PointerToRawData,pNewSecHeader->SizeOfRawData);
        dwVAToStoreNewIID = pNewSecHeader->VirtualAddress ;
        pTargetSecHeader = pNewSecHeader;
        bUseNewSection = TRUE;
    }
    
    //保存原导入表
    PIMAGE_IMPORT_DESCRIPTOR pOldImpDesp = Img.m_pImportDesp;
    PIMAGE_IMPORT_DESCRIPTOR pBuildNewImpDesp = (PIMAGE_IMPORT_DESCRIPTOR)malloc(dwSizeNeed);
    ZeroMemory(pBuildNewImpDesp,dwSizeNeed);
    //保存原来的导入表部分到新的中
    memcpy(pBuildNewImpDesp,pOldImpDesp,dwOldIIDSize);
    printf("[*] 原导入表IID结构保存完毕.
");
    //指向一个新添加的IID项,稍后填充
    PIMAGE_IMPORT_DESCRIPTOR pNewImpEntry = pBuildNewImpDesp + dwOldIIDCnt - 1;


    //需要注意的是,ThunkData在32位和64位下的长度是不一样的,所以这里定义为自适应的ULONG_PTR
    PULONG_PTR pOriginalFirstThunk = NULL ;
    if (bPlaceThunkDataToOldIID)
    {
        //使用原IID的位置存放Thunk数据
        pOriginalFirstThunk = (PULONG_PTR)(Img.m_hModule + Img.m_pImpDataDir->VirtualAddress);
        dwNewThunkDataVA = Img.m_pImpDataDir->VirtualAddress ;
    }
    else
    {
        //原IID的位置存放不下,使用新位置存放
        pOriginalFirstThunk = (PULONG_PTR)((PBYTE)pBuildNewImpDesp + dwThunkDataOffsetByIID);
        dwNewThunkDataVA = dwVAToStoreNewIID + dwThunkDataOffsetByIID ; //在IID数据后面
    }
    ZeroMemory(pOriginalFirstThunk,dwnewThunkDataSize);
    //留出两项内容,第一项稍后填充,第二项填0作为结束标记
    PULONG_PTR pFirstThunk = pOriginalFirstThunk + 2 ;
    //留出两项内容,第一项稍后填充,第二项填0作为结束标记,之后作为Dll名称
    PCHAR  pDllName = (PCHAR)(pFirstThunk + 2);
    //保存dll名称
    strcpy(pDllName,szDllName);
    
    SIZE_T DllNameLen = strlen(szDllName);
    pDllName[DllNameLen] = 0;
    //接下来作为一个PIMPORT_BY_NAME结构
    PIMAGE_IMPORT_BY_NAME pImpName = (PIMAGE_IMPORT_BY_NAME)(pDllName + DllNameLen + 1);
    //填充它
    pImpName->Hint = 0;
    strcpy((char*)pImpName->Name,szDllExportFunName);
    printf("[*] 新导入表IID子结构构造完毕.
");
    
    //计算结束位置
    PCHAR pEnd = (PCHAR)pImpName + sizeof(pImpName->Hint) + strlen((char*)pImpName->Name) + 1;
    //计算总占用的空间大小
    DWORD dwNewIIDEntrySizeUsed = (DWORD)pEnd - (DWORD)pOriginalFirstThunk;
    printf("[*] 新IID成员占用的空间大小 = 0x%X
",dwNewIIDEntrySizeUsed);

    //返过来填充OriginalFirstThunk和FirstThunk
    //根据定义,OriginalFirst应指向IMAGE_IMPORT_BY_NAME结构的偏移
    pOriginalFirstThunk[0] = dwNewThunkDataVA + ((PBYTE)pImpName - (PBYTE)pOriginalFirstThunk);
    pFirstThunk[0] = pOriginalFirstThunk[0];

    //最后填充新的IID项,计算各项的RVA
    pNewImpEntry->OriginalFirstThunk = dwNewThunkDataVA;
    pNewImpEntry->Name = dwNewThunkDataVA + sizeof(ULONG_PTR)*4;//OriginalFirstThunk + FirstThunk的大小
    pNewImpEntry->FirstThunk = dwNewThunkDataVA + sizeof(ULONG_PTR)*2;
    printf("[*] 新IID填充完毕.
");

    //更新PE头中的几个值
    //新的导入表大小
    Img.m_pImpDataDir->Size = dwNewIIDSize;
    //新的导入表IID的起始偏移
    Img.m_pImpDataDir->VirtualAddress = dwVAToStoreNewIID;
    if (!bUseNewSection)
    {
        pImpSecHeader->Misc.VirtualSize += dwSizeNeed;
    }
    
    //如果ThunkData放在了原IID的位置,需要设置节为可写的
    pImpSecHeader->Characteristics |= IMAGE_SCN_MEM_WRITE;
    //清空绑定输入表,强迫加载器重新加载IAT
    Img.m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
    Img.m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
    //设置感染标记
    Img.m_pDosHeader->e_csum = INFECT_SIG;
    printf("[*] PE头更新完毕.
");

    //写入文件
    printf("[*] 开始保存文件.
");

    //开始保存内存中的修改内容到文件中
    //先写入新的PE头
    DWORD dwFileOffset = 0;
    ULONG_PTR dwVAInMemory = 0 ;
    SetFilePointer(hFile,0,NULL,FILE_BEGIN);
    bResult = WriteFile(hFile,Img.m_hModule,Img.m_pOptHeader->SizeOfHeaders,&dwIoCnt,NULL);
    if (!bResult)
    {
        Img.FormatErrorMsg("[-] 写入文件失败!",GetLastError());
        return FALSE;
    }

    printf("[*] PE头写入完毕. Offset = 0x%X Size = 0x%x
",dwFileOffset,dwIoCnt);

    //写入新IID的子结构信息,位置在原导入表的开始处
    dwVAInMemory = dwNewThunkDataVA ;
    dwFileOffset = Img.Rav2Raw(dwVAInMemory);
    SetFilePointer(hFile,dwFileOffset,NULL,FILE_BEGIN);
    bResult = WriteFile(hFile,pOriginalFirstThunk,dwNewIIDEntrySizeUsed,&dwIoCnt,NULL);
    if (!bResult)
    {
        Img.FormatErrorMsg("[-] 写入文件失败!",GetLastError());
        return FALSE;
    }
    printf("[*] 新IID项的子结构写入完毕. Offset = 0x%X Size = 0x%x
",dwFileOffset,dwIoCnt);

    //写入新的IID结构
    dwVAInMemory = (ULONG_PTR)Img.m_pImpDataDir->VirtualAddress;
    dwFileOffset = Img.Rav2Raw(dwVAInMemory);
    SetFilePointer(hFile,dwFileOffset,NULL,FILE_BEGIN);
    bResult = WriteFile(hFile,pBuildNewImpDesp,dwNewIIDSize,&dwIoCnt,NULL);
    if (!bResult)
    {
        Img.FormatErrorMsg("[-] 写入文件失败!",GetLastError());
        return FALSE;
    }
    printf("[*] 新导入表整体写入完毕. Offset = 0x%X Size = 0x%x
",dwFileOffset,dwIoCnt);
    printf("[*] 导入表感染完毕.
");

    return TRUE;
}

注:此代码出自《加密与解密(第四版)》第12章  注入技术

 

原文地址:https://www.cnblogs.com/hanhandaren/p/11453513.html