LoveCpp 发表于 2021-9-24 22:41

[原创源码][VC] 导入表注入实现

### 前言

2020年8月的时候就看了一下滴水的视频,后来太枯燥就没看下去,期间换了一份工作。2021年06月17重新开始捡起来学习,平时每天下班比较晚,十点多到家学一两个小时,生活琐事也比较多,基础也不是很扎实一直到09月21号才学完PE。文章也是对之前所学的知识串起来引用。

### 导入表注入原理

关于导入表的知识可以看滴水三期的视频或者论坛大佬的帖子:
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1413220&highlight=%B5%BC%C8%EB%B1%ED
       
当Exe被加载时,系统会根据Exe导入表信息来加载需要用到的DLL,导入表注入的原理就是修改exe导入表,将自己的DLL添加到exe的导入表中,这样exe运行时可以将自己的DLL加载到exe的进程空间.
        ![](https://attach.52pojie.cn/forum/202109/24/000519xgwcoookg2qzye29.png)
       上图是一个导入表的结构,存放着user32这个dll里面用到了那些函数,程序运行时候就会找到user32.dll然后把函数加载,我们需要做的就是复制一个一样的结构,然后把我们自己的dll放到复制的结构中。
### 导入表注入的实现

第一步:定位导出表

根据目录项(第二个就是导入表)得到导入表信息,代码如下:
```
      //定义PE头的信息
      PIMAGE_DOS_HEADER pDosHeader = NULL;
      PIMAGE_NT_HEADERS pNTHeader = NULL;
      PIMAGE_FILE_HEADER pPEHeader = NULL;
      PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
      PIMAGE_SECTION_HEADER pSectionHeader = NULL;
      LPVOID pTempBuffer = NULL;
      bool Ssize = false;
      if(!pFilebuff)
      {
                printf("读取到内存的pfilebuffer无效!\n");
                return 0;
      }
      //判断是不是exe文件
      if(*((PWORD)pFilebuff) != IMAGE_DOS_SIGNATURE)
      {
                printf("不含MZ标志,不是exe文件!\n");
                return 0;
      }
      pDosHeader = (PIMAGE_DOS_HEADER)pFilebuff;
      if(*((PDWORD)((BYTE *)pFilebuff + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
                printf("无有效的PE标志\n");
                return 0;
      }
      
      //读取pFileBuffer 获取DOS头,PE头,节表等信息
      pDosHeader =(PIMAGE_DOS_HEADER)pFilebuff;
      pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFilebuff + pDosHeader->e_lfanew);
      //打印NT头      
      pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//加4个字节到了标准PE头
      pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //标准PE头+标准PE头的大小 20
      pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
      
      printf("===导出表====\n");
      
      printf("内存地址%x\n",pOptionHeader->DataDirectory.VirtualAddress);
      
                printf("内存大小%x\n",pOptionHeader->DataDirectory.Size);
```
第二步:移动导出表

然后我们需要把这个移动到一个地方,海哥的步骤是先计算原先的导出表的大小和要新增的导出表的大小一共有多大,看放在那里,我这就直接偷懒新增一个节来存放
```
      PIMAGE_DOS_HEADER pDosHeader = NULL;
      PIMAGE_NT_HEADERS pNTHeader = NULL;
      PIMAGE_FILE_HEADER pPEHeader = NULL;
      PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
      PIMAGE_SECTION_HEADER pSectionHeader = NULL;
      LPVOID pTempBuffer = NULL;
      bool Ssize = false;
      if(!pFilebuff)
      {
                printf("读取到内存的pfilebuffer无效!\n");
                return 0;
      }
      //判断是不是exe文件
      if(*((PWORD)pFilebuff) != IMAGE_DOS_SIGNATURE)
      {
                printf("不含MZ标志,不是exe文件!\n");
                return 0;
      }
      pDosHeader = (PIMAGE_DOS_HEADER)pFilebuff;
      if(*((PDWORD)((BYTE *)pFilebuff + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
                printf("无有效的PE标志\n");
                return 0;
      }
      
      //读取pFileBuffer 获取DOS头,PE头,节表等信息
      pDosHeader =(PIMAGE_DOS_HEADER)pFilebuff;
      pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFilebuff + pDosHeader->e_lfanew);
      //打印NT头      
      pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//加4个字节到了标准PE头
      pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //标准PE头+标准PE头的大小 20
      pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

      //移动导入表
      
      //获取剩余大小 //SizeOfHeaders(总的大小)4096 -   ( pSectionHeader(一个地址,文件开始的地方偏移了pSectionHeader的大小的字节)-    (文件偏移+pPEHeader->NumberOfSections * 40)一个地址 文件开始的地方偏移了节的大小的字节 并不是节在文件中的偏移地址,所以会比 pSectionHeader小)
      DWORD whiteSpaceSize = pOptionHeader->SizeOfHeaders - ((DWORD)pSectionHeader - (DWORD)pFilebuff + pPEHeader->NumberOfSections * 40);
      
      if (whiteSpaceSize < 80)
      {
                printf("空间不足");
                return false;
    }
      //新增一个节来存放导入表
      //1.修改SizeOfImage大小
      pOptionHeader->SizeOfImage +=0x1000;

      
      LPVOID NewBuffer = malloc(pOptionHeader->SizeOfImage);//申请内存
      memset(NewBuffer, 0, pOptionHeader->SizeOfImage);//初始化内存
      //修改节表NumberOfSection数量
      
      for (int i=0;i<80;i++)
      {
                if (*((char*)pFilebuff +whiteSpaceSize + i) !=0 )
                {
                        
                        pTempBuffer=malloc(pOptionHeader->SizeOfImage);
                        
                        //如果空间足,但是节最后的80个字节不等于0 就需要将PE头往上移动,把垃圾数据清空
                        memset(pTempBuffer, 0, pOptionHeader->SizeOfImage);
                        
                        //把整个原来的pImageBuffer 复制到新的imagefile
                        memcpy(pTempBuffer,pFilebuff,pOptionHeader->SizeOfImage);

                        DWORD e_lfanew = sizeof(IMAGE_DOS_HEADER);
                        //计算PE-到节表有多少个字节
                        DWORD moveSize = pOptionHeader->SizeOfHeaders - whiteSpaceSize -pDosHeader->e_lfanew;
                        //printf("垃圾数据开始的地方%x\n",(char*)pTempImageBuffer+e_lfanew);
                        //printf("PE开始的地方%x\n",(char*)pTempImageBuffer+pDosHeader->e_lfanew);
                        //把PE到节的数据移动到DOSSUB区域
                        memcpy((char*)pTempBuffer+e_lfanew,(char*)pTempBuffer+pDosHeader->e_lfanew,moveSize);
                        //然后将PE-到节表然后到之前的数据清0 //计算大小 总大小 = pOptionHeader->SizeOfHeaders- whiteSpaceSize - DOS头的大小 - PE到节表的大小
                        DWORD setZeroSzie = pOptionHeader->SizeOfHeaders - whiteSpaceSize - e_lfanew - moveSize;
                        //之前的数据清0
                        memset((char*)pTempBuffer+e_lfanew+moveSize,0,setZeroSzie);
                        //修正e_lfanew
                        memcpy((char*)pTempBuffer+sizeof(IMAGE_DOS_HEADER)-4,&e_lfanew,1);
                        
                        //重新从pTempImageBuffer 获取DOS头,PE头,节表等信息
                        pDosHeader =(PIMAGE_DOS_HEADER)pTempBuffer;
                        pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pTempBuffer + pDosHeader->e_lfanew);
                        //打印NT头      
                        pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//加4个字节到了标准PE头
                        pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //标准PE头+标准PE头的大小 20
                        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
                        
                        pFilebuff = pTempBuffer;

                }      
      }
      


      PIMAGE_SECTION_HEADER pNewSec = (PIMAGE_SECTION_HEADER)(pSectionHeader + pPEHeader->NumberOfSections);
      
    memcpy(pNewSec->Name,".addSec",8);//修改节表名
      
      //获取最后一个节
      PIMAGE_SECTION_HEADER EndSection = (PIMAGE_SECTION_HEADER)(pSectionHeader + pPEHeader->NumberOfSections-1);
      
      //内存对齐
      pNewSec->VirtualAddress = EndSection->VirtualAddress + (DWORD)(ceil(max(EndSection->Misc.VirtualSize, EndSection->SizeOfRawData)/ pOptionHeader->SectionAlignment) * pOptionHeader->SectionAlignment);
      //内存大小
      pNewSec->Misc.VirtualSize = 0x1000;
      //文件大小
      pNewSec->SizeOfRawData = 0x1000;//新增的节区的大小
      //文件偏移
      pNewSec->PointerToRawData = EndSection->PointerToRawData + EndSection->SizeOfRawData;
      //pNewSec->Characteristics = 0xC0000040;//修改属性(可执行)
      pNewSec->Characteristics |= IMAGE_SCN_MEM_EXECUTE |IMAGE_SCN_MEM_WRITE|IMAGE_SCN_MEM_READ;
      pPEHeader->NumberOfSections += 1;
      //在新增节表后增加40个字节的空白区
      memset(pNewSec+1, 0, 40);
```
新增节之后我们可以获取到新增节的地址,然后把导出表直接复制到新增节的起始位置:

```
memcpy(NewBuffer, pFilebuff,pOptionHeader->SizeOfImage);//复制内存
   if (!pOptionHeader->DataDirectory.VirtualAddress)
   {
             printf("(DLLInject)This program has no import table.\n");
             return 0;
   }
   //定位新节表的地址
   LPVOID pNewSecAddr = (LPVOID)((DWORD)NewBuffer+pNewSec->PointerToRawData);
   
   pDosHeader =(PIMAGE_DOS_HEADER)NewBuffer;
   pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)NewBuffer + pDosHeader->e_lfanew);
   //打印NT头      
   pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//加4个字节到了标准PE头
   pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //标准PE头+标准PE头的大小 20
   pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

   //定位导入表      
   PIMAGE_IMPORT_DESCRIPTOR ImportExtable = (PIMAGE_IMPORT_DESCRIPTOR)((char*)NewBuffer + RvaToFileOffset(NewBuffer,pOptionHeader->DataDirectory.VirtualAddress));

   //复制新的导入表
   memcpy(pNewSecAddr,ImportExtable,pOptionHeader->DataDirectory.Size);
```
复制完之后的的PE文件在内存的样子:
![](https://attach.52pojie.cn/forum/202109/24/000609cll353vsz6ns3ql3.png)

我们要在原先的导出表之后追加一个导入表,但是这个表需要在一堆0000之前加上,所以我们需要计算pNewSecAddr到000之前的大小,因为导入表的结构是以0结束的。
```
//复制完在循环获取大小,这个大小就是有多少个导出表,然后乘以导出表的大小
   int j =0;
   while(ImportExtable->FirstThunk !=0 &&ImportExtable->OriginalFirstThunk !=0){
             ImportExtable++;
             j++;
   }
//新的导入表结构的地址,因为是新增节的方式,复制filebuff的时候把新的内存都设置为0了,所以整个绿色块到不是很绿的拿一块区域里面的内存都是0,我们可以直接使用一个PIMAGE_IMPORT_DESCRIPTOR结构
   PIMAGE_IMPORT_DESCRIPTOR NewImportExtable= (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pNewSecAddr + j * (sizeof(IMAGE_IMPORT_DESCRIPTOR)));
```

因为每个导出表对应一张IAT表和INT表,这两个表又同时指向一个存放函数命的结构,PIMAGE_IMPORT_BY_NAME所以我们为这两个表和名字表申请内存空间:
```
//设置IAT表 IAT表的地址 = 自己新增的导入表的地址 + 2个自己结构的大小第一个结构存放新增的导入表,第二个结构做为导入表的结束标记
   PIMAGE_THUNK_DATA32 IatTable = (PIMAGE_THUNK_DATA32)(NewImportExtable +2);
   //设置InT表
   PIMAGE_THUNK_DATA32 IntTable = (PIMAGE_THUNK_DATA32)(IatTable+2);   
   //设置IMAGE_IMPORT_BY_NAME
   PIMAGE_IMPORT_BY_NAME ImageName = (PIMAGE_IMPORT_BY_NAME)(IntTable+2);
```
此时的结构如下:
![](https://attach.52pojie.cn/forum/202109/24/000650nnz9m4zu100sxmm4.png)
对应海哥的图,A部分为NEWimporEXtable,B部分为iAT,INAT表    D部分为名字表,C部分为DLL名称
        ![](https://attach.52pojie.cn/forum/202109/24/000709ezedxds9rv66xiri.png)
        然后我们需要这些地址对应起来,也就是把地址修正
       
```
DWORD FuncNameSzie = strlen("ExportFunction") + 1;

memcpy(ImageName->Name,"ExportFunction",FuncNameSzie);

//获取存放DLL名称的地址      这个地址保证在PIMAGE_IMPORT_BY_NAME的结构后面就行

DWORD* DllName= (DWORD*)((DWORD)ImageName + FuncNameSzie+10);//

memcpy(DllName,"InjectDll.dll",strlen("InjectDll.dll")+1);
//修正IAT,INT表地址
DWORD InAtFoa = (FoaToImageOffset(NewBuffer,(DWORD)ImageName -(DWORD)NewBuffer));
memcpy(IatTable,&InAtFoa,4);
memcpy(IntTable,&InAtFoa,4);
```

然后在修正目录中的地址,然后存盘
```
//修导出表地址
NewImportExtable->FirstThunk = FoaToImageOffset(NewBuffer,(DWORD)IntTable - (DWORD)NewBuffer);
NewImportExtable->OriginalFirstThunk = FoaToImageOffset(NewBuffer,(DWORD)IatTable- (DWORD)NewBuffer);
//修正dll名称的地址
NewImportExtable->Name =FoaToImageOffset(NewBuffer,(DWORD)DllName - (DWORD)NewBuffer);
//新导出表的地址 新导出表的地址= NewExport_Directory 内存地址 - 文件开始的位置 = 偏移
pOptionHeader->DataDirectory.VirtualAddress = FoaToImageOffset(NewBuffer,(DWORD)pNewSecAddr-(DWORD)NewBuffer);
pOptionHeader->DataDirectory.Size = pOptionHeader->DataDirectory.Size + 40;

FILE* fp = fopen("C://project/notepadiat1.exe","wb+");
size_t n =         fwrite(NewBuffer,pOptionHeader->SizeOfImage,1,fp);
if(!n){
printf("fwrite数据写入失败...\n");
fclose(fp);
return ERROR;
}

free(pFilebuff);
free(NewBuffer);

printf("存盘成功...\n");

fclose(fp);
```

使用ipmsg.exe正常能注入,但是使用notepad发现无法正常注入,查看输入表也加载了dll,但是打开notepad就是无法弹出dll加载时候的函数
        ![](https://attach.52pojie.cn/forum/202109/24/000800d3kj6fm366oo7n7m.png)
       
后来用工具添加和代码添加的结构对比了一下,发现工具将原先的绑定表请了,加了两行代码:
```
pOptionHeader->DataDirectory.VirtualAddress= 0;
pOptionHeader->DataDirectory.Size= 0;
```
最后整体代码如下:
```
//导入表注入
DWORD InjectDll(LPVOID pFilebuff){
      //定义PE头的信息
      PIMAGE_DOS_HEADER pDosHeader = NULL;
      PIMAGE_NT_HEADERS pNTHeader = NULL;
      PIMAGE_FILE_HEADER pPEHeader = NULL;
      PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
      PIMAGE_SECTION_HEADER pSectionHeader = NULL;
      LPVOID pTempBuffer = NULL;
      bool Ssize = false;
      if(!pFilebuff)
      {
                printf("读取到内存的pfilebuffer无效!\n");
                return 0;
      }
      //判断是不是exe文件
      if(*((PWORD)pFilebuff) != IMAGE_DOS_SIGNATURE)
      {
                printf("不含MZ标志,不是exe文件!\n");
                return 0;
      }
      pDosHeader = (PIMAGE_DOS_HEADER)pFilebuff;
      if(*((PDWORD)((BYTE *)pFilebuff + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
                printf("无有效的PE标志\n");
                return 0;
      }
      
      //读取pFileBuffer 获取DOS头,PE头,节表等信息
      pDosHeader =(PIMAGE_DOS_HEADER)pFilebuff;
      pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFilebuff + pDosHeader->e_lfanew);
      //打印NT头      
      pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//加4个字节到了标准PE头
      pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //标准PE头+标准PE头的大小 20
      pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

      //移动导入表
      
      //获取剩余大小 //SizeOfHeaders(总的大小)4096 -   ( pSectionHeader(一个地址,文件开始的地方偏移了pSectionHeader的大小的字节)-    (文件偏移+pPEHeader->NumberOfSections * 40)一个地址 文件开始的地方偏移了节的大小的字节 并不是节在文件中的偏移地址,所以会比 pSectionHeader小)
      DWORD whiteSpaceSize = pOptionHeader->SizeOfHeaders - ((DWORD)pSectionHeader - (DWORD)pFilebuff + pPEHeader->NumberOfSections * 40);
      
      if (whiteSpaceSize < 80)
      {
                printf("空间不足");
                return false;
    }
      //新增一个节来存放导入表
      //1.修改SizeOfImage大小
      pOptionHeader->SizeOfImage +=0x1000;

      
      LPVOID NewBuffer = malloc(pOptionHeader->SizeOfImage);//申请内存
      memset(NewBuffer, 0, pOptionHeader->SizeOfImage);//初始化内存
      //修改节表NumberOfSection数量
      
      for (int i=0;i<80;i++)
      {
                if (*((char*)pFilebuff +whiteSpaceSize + i) !=0 )
                {
                        
                        pTempBuffer=malloc(pOptionHeader->SizeOfImage);
                        
                        //如果空间足,但是节最后的80个字节不等于0 就需要将PE头往上移动,把垃圾数据清空
                        memset(pTempBuffer, 0, pOptionHeader->SizeOfImage);
                        
                        //把整个原来的pImageBuffer 复制到新的imagefile
                        memcpy(pTempBuffer,pFilebuff,pOptionHeader->SizeOfImage);

                        DWORD e_lfanew = sizeof(IMAGE_DOS_HEADER);
                        //计算PE-到节表有多少个字节
                        DWORD moveSize = pOptionHeader->SizeOfHeaders - whiteSpaceSize -pDosHeader->e_lfanew;
                        //printf("垃圾数据开始的地方%x\n",(char*)pTempImageBuffer+e_lfanew);
                        //printf("PE开始的地方%x\n",(char*)pTempImageBuffer+pDosHeader->e_lfanew);
                        //把PE到节的数据移动到DOSSUB区域
                        memcpy((char*)pTempBuffer+e_lfanew,(char*)pTempBuffer+pDosHeader->e_lfanew,moveSize);
                        //然后将PE-到节表然后到之前的数据清0 //计算大小 总大小 = pOptionHeader->SizeOfHeaders- whiteSpaceSize - DOS头的大小 - PE到节表的大小
                        DWORD setZeroSzie = pOptionHeader->SizeOfHeaders - whiteSpaceSize - e_lfanew - moveSize;
                        //之前的数据清0
                        memset((char*)pTempBuffer+e_lfanew+moveSize,0,setZeroSzie);
                        //修正e_lfanew
                        memcpy((char*)pTempBuffer+sizeof(IMAGE_DOS_HEADER)-4,&e_lfanew,1);
                        
                        //重新从pTempImageBuffer 获取DOS头,PE头,节表等信息
                        pDosHeader =(PIMAGE_DOS_HEADER)pTempBuffer;
                        pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pTempBuffer + pDosHeader->e_lfanew);
                        //打印NT头      
                        pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//加4个字节到了标准PE头
                        pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //标准PE头+标准PE头的大小 20
                        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
                        
                        pFilebuff = pTempBuffer;

                }      
      }
      


      PIMAGE_SECTION_HEADER pNewSec = (PIMAGE_SECTION_HEADER)(pSectionHeader + pPEHeader->NumberOfSections);
      
    memcpy(pNewSec->Name,".addSec",8);//修改节表名
      
      //获取最后一个节
      PIMAGE_SECTION_HEADER EndSection = (PIMAGE_SECTION_HEADER)(pSectionHeader + pPEHeader->NumberOfSections-1);
      
      //内存对齐
      pNewSec->VirtualAddress = EndSection->VirtualAddress + (DWORD)(ceil(max(EndSection->Misc.VirtualSize, EndSection->SizeOfRawData)/ pOptionHeader->SectionAlignment) * pOptionHeader->SectionAlignment);
      //内存大小
      pNewSec->Misc.VirtualSize = 0x1000;
      //文件大小
      pNewSec->SizeOfRawData = 0x1000;//新增的节区的大小
      //文件偏移
      pNewSec->PointerToRawData = EndSection->PointerToRawData + EndSection->SizeOfRawData;
      //pNewSec->Characteristics = 0xC0000040;//修改属性(可执行)
      pNewSec->Characteristics |= IMAGE_SCN_MEM_EXECUTE |IMAGE_SCN_MEM_WRITE|IMAGE_SCN_MEM_READ;
      pPEHeader->NumberOfSections += 1;
      //在新增节表后增加40个字节的空白区
    memset(pNewSec+1, 0, 40);
      


      memcpy(NewBuffer, pFilebuff,pOptionHeader->SizeOfImage);//复制内存

      
      if (!pOptionHeader->DataDirectory.VirtualAddress)
      {
                printf("(DLLInject)This program has no import table.\n");
                return 0;
      }
      
      
      //定位新节表的地址
      LPVOID pNewSecAddr = (LPVOID)((DWORD)NewBuffer+pNewSec->PointerToRawData);
      
      pDosHeader =(PIMAGE_DOS_HEADER)NewBuffer;
      pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)NewBuffer + pDosHeader->e_lfanew);
      //打印NT头      
      pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//加4个字节到了标准PE头
      pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //标准PE头+标准PE头的大小 20
      pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
      


      //定位导入表      
      PIMAGE_IMPORT_DESCRIPTOR ImportExtable = (PIMAGE_IMPORT_DESCRIPTOR)((char*)NewBuffer + RvaToFileOffset(NewBuffer,pOptionHeader->DataDirectory.VirtualAddress));

      //复制新的导入表
      memcpy(pNewSecAddr,ImportExtable,pOptionHeader->DataDirectory.Size);

      //复制完在循环获取大小

      int j =0;
      while(ImportExtable->FirstThunk !=0 &&ImportExtable->OriginalFirstThunk !=0){
                ImportExtable++;
                j++;
      }      
      
      //新的导入表结构的地址,因为是新增节的方式,所以移动完导出表之后这个地方会为0,可以直接使用一个PIMAGE_IMPORT_DESCRIPTOR结构
      PIMAGE_IMPORT_DESCRIPTOR NewImportExtable= (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pNewSecAddr + j * (sizeof(IMAGE_IMPORT_DESCRIPTOR)));

      
      //设置IAT表 IAT表的地址 = 自己新增的导入表的地址 + 2个自己结构的大小第一个结构存放新增的导入表,第二个结构做为导入表的结束标记
      PIMAGE_THUNK_DATA32 IatTable = (PIMAGE_THUNK_DATA32)(NewImportExtable +2);

      //设置InT表
      PIMAGE_THUNK_DATA32 IntTable = (PIMAGE_THUNK_DATA32)(IatTable+2);

      //设置IMAGE_IMPORT_BY_NAME

      PIMAGE_IMPORT_BY_NAME ImageName = (PIMAGE_IMPORT_BY_NAME)(IntTable+2);
      

      DWORD FuncNameSzie = strlen("ExportFunction") + 1;

      memcpy(ImageName->Name,"ExportFunction",FuncNameSzie);

      //获取存放DLL名称的地址      这个地址保证在PIMAGE_IMPORT_BY_NAME的结构后面就行
      
      DWORD* DllName= (DWORD*)((DWORD)ImageName + FuncNameSzie+10);//
      
      memcpy(DllName,"InjectDll.dll",strlen("InjectDll.dll")+1);


      //修正IAT表地址
      DWORD InAtFoa = (FoaToImageOffset(NewBuffer,(DWORD)ImageName -(DWORD)NewBuffer));
      memcpy(IatTable,&InAtFoa,4);
      memcpy(IntTable,&InAtFoa,4);
      

      //修导出表地址
      NewImportExtable->FirstThunk = FoaToImageOffset(NewBuffer,(DWORD)IntTable - (DWORD)NewBuffer);
      NewImportExtable->OriginalFirstThunk = FoaToImageOffset(NewBuffer,(DWORD)IatTable- (DWORD)NewBuffer);
      //修正dll名称的地址
      NewImportExtable->Name =FoaToImageOffset(NewBuffer,(DWORD)DllName - (DWORD)NewBuffer);
      //新导出表的地址 新导出表的地址= NewExport_Directory 内存地址 - 文件开始的位置 = 偏移
      pOptionHeader->DataDirectory.VirtualAddress = FoaToImageOffset(NewBuffer,(DWORD)pNewSecAddr-(DWORD)NewBuffer);
      pOptionHeader->DataDirectory.Size = pOptionHeader->DataDirectory.Size + 40;


      pOptionHeader->DataDirectory.VirtualAddress= 0;
      pOptionHeader->DataDirectory.Size= 0;

      FILE* fp = fopen("C://project/notepadiat1.exe","wb+");
      size_t n =         fwrite(NewBuffer,pOptionHeader->SizeOfImage,1,fp);
      if(!n){
                printf("fwrite数据写入失败...\n");
                fclose(fp);
                return ERROR;
      }

      free(pFilebuff);
      free(NewBuffer);

      printf("存盘成功...\n");

      fclose(fp);


      return 1;
}
```
实现效果,启动:
![](https://attach.52pojie.cn/forum/202109/24/000855s834ktnt5tn1v55k.png)
关闭:
![](https://attach.52pojie.cn/forum/202109/24/000921af5m16biespgypqq.png)
页: [1]
查看完整版本: [原创源码][VC] 导入表注入实现