• PE 移动导入表/注入


    前言:

    到目前的话,自己已经把滴水的PE要写的全写完了,供大家参考!

    项目地址:https://github.com/adezz/MyPe

    导入表注入原理:

    当exe文件被加载时,系统会根据exe导入表信息来加载需要用到的DLL,导入表注入的原理就是修改exe导入表,将自己的DLL添加到exe的导入表中,这样exe运行时可以将自己的DLL加载到exe的进程空间

    导入表移动的步骤:

    第一步:新增节

    第二步:复制原来的导入表到新的节数据中

    第三步:追加一个新的导入表

    第四步:新的导入表后面继续追加8个字节的INT表 8个字节的IAT表,为什么是两个表都是八个字节呢?因为还有4个字节是作为结束标识符的

    第五步:继续在后面追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串

    第六步:因为RVA和FOA还有区别,所以需要在导入表中的三个属性 INT和IAT和导入表相关的DLL名称中记录的地址都需要转换为RVA再存储进去

    第七步:修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size

    根据目录项(第二个就是导入表)得到导入表信息:

    typedef struct _IMAGE_DATA_DIRECTORY {					
        DWORD   VirtualAddress;					
        DWORD   Size;					
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;							
    

    实现代码:

    void MoveAndInjectImportTable(PVOID pFileBuffer,PDWORD OldBufferSize,PVOID* pNewBuffer){
    
    	PIMAGE_DOS_HEADER pImageDosHeader = NULL;
    	PIMAGE_FILE_HEADER pImageFileHeader = NULL;
    	PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
    	PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;
    	PIMAGE_SECTION_HEADER NewSec = NULL;
    	PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR = NULL;
    	PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR_Temp = NULL;
    	
    	DWORD RVA = 0;
    	DWORD FOA = 0;
    	DWORD isOk;
    	DWORD NewLength=0;
    	PVOID LastSection = NULL;
    	PVOID CodeSection = NULL;
    	PVOID SectionOfNew= NULL;
    	PVOID SectionOfNewTemp = NULL;
    
    	pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    	pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
    	pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + sizeof(IMAGE_FILE_HEADER));
    	pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
    	
    	//判断是否可以容纳相应的节表
    	isOk = (DWORD)pImageOptionalHeader->SizeOfHeaders - ((DWORD)pImageDosHeader->e_lfanew + IMAGE_SIZEOF_FILE_HEADER + pImageFileHeader->SizeOfOptionalHeader + 40*pImageFileHeader->NumberOfSections);
    	if(isOk < 80){
    		printf("空间太小 无法进行添加!");
    		return;
    	}
    	
    	//生成对应的内存大小的空间
    	NewLength += *OldBufferSize + 0x1000;
    	*pNewBuffer = (PVOID)malloc(NewLength);
    	ZeroMemory(*pNewBuffer,NewLength);
    	
    	//拷贝之前内存空间 到 当前新生成的内存空间
    	memcpy(*pNewBuffer,pFileBuffer,*OldBufferSize);
    	
    	//获取新的结构体
    	pImageDosHeader = (PIMAGE_DOS_HEADER)(*pNewBuffer);
    	pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
    	pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + sizeof(IMAGE_FILE_HEADER));
    	pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
    	
    	// pImageFileHeader->NumberOfSections修改
    	pImageFileHeader->NumberOfSections = pImageFileHeader->NumberOfSections + 1;
    	
    	// pImageOptionalHeader->SizeOfImage修改
    	pImageOptionalHeader->SizeOfImage = (DWORD)pImageOptionalHeader->SizeOfImage + 0x1000;
    	
    	// 复制代码段的节数据到 当前最后一个节数据后面
    	CodeSection = (PVOID)(&pImageSectionHeaderGroup[0]);
    	
    	//新增节的位置
    	LastSection = (PVOID)(DWORD)(&pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-1]);
    	memcpy(LastSection,CodeSection,40);
    	
    	//修正相关属性
    	NewSec = (PIMAGE_SECTION_HEADER)LastSection;
    	strcpy(NewSec,".NewSec");
    	NewSec->Misc.VirtualSize = 0x1000;
    	NewSec->SizeOfRawData = 0x1000;
    	NewSec->VirtualAddress = pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].VirtualAddress + pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].SizeOfRawData;
    	NewSec->PointerToRawData = pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].PointerToRawData + pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].SizeOfRawData;
    	
    	//修改大小长度
    	*OldBufferSize = NewLength;
    
    	//这里得到新节位置的指针
    	SectionOfNew = (PVOID)((DWORD)*pNewBuffer + (DWORD)NewSec->PointerToRawData);
    
    	//先获取导入表的地址
    	RVA_TO_FOA(*pNewBuffer,pImageOptionalHeader->DataDirectory[1].VirtualAddress,&FOA);
    	pIMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)*pNewBuffer + (DWORD)FOA);
    	//printf("start:%x
    ", pIMPORT_DESCRIPTOR);
    
    	/*
    	第三步:			
    	将原导入表全部Copy到空白区			
    	*/
    
    	SectionOfNewTemp = SectionOfNew;
    
    	while (pIMPORT_DESCRIPTOR->OriginalFirstThunk && pIMPORT_DESCRIPTOR->FirstThunk)
    	{
    		//printf("%x
    ", (DWORD)SectionOfNewTemp - (DWORD)*pNewBuffer);
    		memcpy(SectionOfNewTemp,pIMPORT_DESCRIPTOR,20);
    		pIMPORT_DESCRIPTOR++;
    		SectionOfNewTemp = (PVOID)((DWORD)SectionOfNewTemp + 20);
    	}
    	
    	//保存复制完导入表之后的地址
    	pIMPORT_DESCRIPTOR_Temp = SectionOfNewTemp;
    	printf("开始添加自己的导入表的地址:%x
    ",(DWORD)SectionOfNewTemp-(DWORD)*pNewBuffer);
    
    
    	/*
    	第四步:				
    	在新的导入表后面,追加一个导入表.
    	  typedef struct _IMAGE_IMPORT_DESCRIPTOR {							
    	  union {							
    	  DWORD   Characteristics;           							
    	  DWORD   OriginalFirstThunk;         							
    	  };							
    	  DWORD   TimeDateStamp;               							
    	  DWORD   ForwarderChain;              							
    	  DWORD   Name;							
    	  DWORD   FirstThunk;                 							
    	  } IMAGE_IMPORT_DESCRIPTOR;							
    	  typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;							
    	*/
    
    	pIMPORT_DESCRIPTOR->TimeDateStamp = 0;
    	
    	pIMPORT_DESCRIPTOR->ForwarderChain = -1;
    
    	FOA_TO_RVA(*pNewBuffer,(DWORD)pIMPORT_DESCRIPTOR_Temp + 40 - (DWORD)*pNewBuffer,&RVA); // INT表占8个字节
    	pIMPORT_DESCRIPTOR_Temp->OriginalFirstThunk = RVA;  //这个是指向导入表相关INT表 存的是RVA,所以前面还需要转换下
    
    	FOA_TO_RVA(*pNewBuffer,(DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 8 - (DWORD)*pNewBuffer,&RVA); // IAT表占8个字节
    	pIMPORT_DESCRIPTOR_Temp->FirstThunk = RVA;// 这个是指向导入表相关的IAT 存的是RVA,所以前面还需要转换下
    
    	FOA_TO_RVA(*pNewBuffer,(DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 16 - (DWORD)*pNewBuffer,&RVA); // dll函数名占8个字节,这里自己就模拟 dll名称为abc.dll 长度为7个字节 最后一个字节为
    	pIMPORT_DESCRIPTOR_Temp->Name = RVA; // 这个是指向导入表相关的DLL名称 存的是RVA 所以前面还需要转换下
    
    	strcpy((PVOID)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 16),"abc.dll");
    
    	
    	/*
    	第五步:			
    	追加8个字节的INT表  8个字节的IAT表	,一个_IMAGE_THUNK_DATA32结构是4个字节 但是还需要4个字节来作为结束的标识符	所以这里总共是占16个字节	
    	*/
    	
    	FOA_TO_RVA(*pNewBuffer, ((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 24 - (DWORD)*pNewBuffer),&RVA);
    
    	*(PDWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40) = RVA; //_IMAGE_THUNK_DATA32结构中的属性指向PIMAGE_IMPORT_BY_NAME 存的是RVA 所以前面需要转换下
    
    	FOA_TO_RVA(*pNewBuffer, ((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 24 - (DWORD)*pNewBuffer),&RVA);
    	
    	*(PDWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 8) = RVA; //指向PIMAGE_IMPORT_BY_NAME 存的是RVA 所以前面需要转换下
    
    	/*
    	第六步:							
    	  追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串							
    	*/
    
    	*(PWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 26) = 0;
    	strcpy((PVOID)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 26),"myFun");//这里写死了,函数的名称为myFun
    
    	/*
    	第七步:								
    	  修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
    	*/
    	FOA_TO_RVA(*pNewBuffer,(DWORD)SectionOfNew - (DWORD)*pNewBuffer,&RVA);
    	pImageOptionalHeader->DataDirectory[1].VirtualAddress = RVA;
    	pImageOptionalHeader->DataDirectory[1].Size = (DWORD)pImageOptionalHeader->DataDirectory[1].Size + 20;
    
    	//最后进行存盘操作
    	MyWriteFile(*pNewBuffer, NewLength);
    }
    
  • 相关阅读:
    随机生成300道四则运算
    练习
    电梯演说模板练习
    敏捷开发
    团队模式
    思考
    build to win 观后感
    四则运算
    Code review
    Mutual review
  • 原文地址:https://www.cnblogs.com/zpchcbd/p/13072480.html
Copyright © 2020-2023  润新知