56星座屋
当前位置: 首页 星座百科

如何删除windows的pe选择(PE数据目录表和导出表解析)

时间:2023-07-11 作者: 小编 阅读量: 1 栏目名: 星座百科

起因五一(2020年,迁移文章)假期没有回家,是因为在四月份已经向公司提了离职.估计五月中旬应该就能办理离职手续,在公司呆了6年多,合同由3(2次)年换成无固定期限劳动合同.因为家庭原因,最终还是要回去的.不知不觉间北漂了8年,在公司工作期间经历我人生的大事(结婚和生子).关于PE相关的内容,在前面也写过<<学习PE文件结构>>和<<在解析PE遇到的问题>>,这里也不多的介绍了.在看正文之前,先看

起因

五一(2020年,迁移文章)假期没有回家,是因为在四月份已经向公司提了离职.估计五月中旬应该就能办理离职手续,在公司呆了6年多,合同由3(2次)年换成无固定期限劳动合同.因为家庭原因,最终还是要回去的.不知不觉间北漂了8年,在公司工作期间经历我人生的大事(结婚和生子).

关于PE相关的内容,在前面也写过 <<学习PE文件结构>> 和 <<在解析PE遇到的问题>>,这里也不多的介绍了.

在看正文之前,先看看图,对下边具体要做的事情,有一个大概的认知.

关系图(省略节表部分)

从PE中解析导出表,根据导出表的地址,进行获取函数调用的地址

数据目录表

在 <<学习PE文件结构>> 这边博文中,已经对可选PE头进行了解析. 在IMAGE_OPTIONAL_HEADER结构体中这个字段DataDirectory,就是我们通常所说的数据目录表.重新看看这个可选PE头结构.

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES16//数据目标表的长度//可选PE头typedef struct _IMAGE_OPTIONAL_HEADER {//// Standard fields.//WORDMagic;BYTEMajorLinkerVersion;BYTEMinorLinkerVersion;DWORDSizeOfCode;DWORDSizeOfInitializedData;DWORDSizeOfUninitializedData;DWORDAddressOfEntryPoint;DWORDBaseOfCode;DWORDBaseOfData;//// NT additional fields.//DWORDImageBase;DWORDSectionAlignment;DWORDFileAlignment;WORDMajorOperatingSystemVersion;WORDMinorOperatingSystemVersion;WORDMajorImageVersion;WORDMinorImageVersion;WORDMajorSubsystemVersion;WORDMinorSubsystemVersion;DWORDWin32VersionValue;DWORDSizeOfImage;DWORDSizeOfHeaders;DWORDCheckSum;WORDSubsystem;WORDDllCharacteristics;DWORDSizeOfStackReserve;DWORDSizeOfStackCommit;DWORDSizeOfHeapReserve;DWORDSizeOfHeapCommit;DWORDLoaderFlags;DWORDNumberOfRvaAndSizes;//数据目录表IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32//数据目录表结构typedef struct _IMAGE_DATA_DIRECTORY {DWORDVirtualAddress;//在内存中的相对地址DWORDSize;} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

先将数据目录表信息打印出来,在用PETool进行对比,看看是否是有问题.

//打印出 数据目录表 信息void print_data_dir(char* base){PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base;PIMAGE_NT_HEADERS nt_header =(PIMAGE_NT_HEADERS)((unsigned long)basedos_header->e_lfanew);PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header4);//4为nt头中Signature DWORDPIMAGE_OPTIONAL_HEADER optional_header =(PIMAGE_OPTIONAL_HEADER)((unsigned long)file_headerIMAGE_SIZEOF_FILE_HEADER);for (int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i){IMAGE_DATA_DIRECTORY data_dir = optional_header->DataDirectory[i];printf("%d data:x size:%x\n", i, data_dir.VirtualAddress, data_dir.Size);}}int main(int argc, char* argv[]){char* filename = "DllExportTable.dll"; //以动态库为例int size = file_size(filename);if (size > 0){char* buf;file_to_memory(filename, size, &buf);print_data_dir(buf);}else{printf("file not found!\n");}return 0;}

PE中的数据目录表

导出表

1

导入表

2

资源表

3

异常表

4

安全证书表

5

重定位表

6

调试信息表

7

版权所有表

8

全局指针表

9

TLS(线程本地存储表)

10

加载配置表

11

绑定导入表

12

IAT(导入地址表)

13

延迟导入表

14

COM表

15

保留(这个暂时没用)

正常我们只需要关注重点表: 导出表/导入表/重定位/IAT

导出表

从数据目录表中,知道了第一个表是导出表,并且导出表的VirtualAddress.这个时候要了解这两个RVA和FOA知识点.

RVA(相对虚拟地址):VirtualAddress就是RVA.

FOA(文件偏移地址)

如果解析PE时候,如果不进行内存拉伸,VirtualAddress就需要用RVA转FOA的转换,这个转换是拿导出表的VirtualAddress在节表中查找每个节的VirtualAddress的区域中.然后根据节中的PointerToRelocation(文件中的偏移)

//相对虚拟地址转换为文件偏移地址int rva_to_foa(char* base, unsigned long* va){unsigned long rva = *va;PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base;PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)basedos_header->e_lfanew);PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header4);PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_headerIMAGE_SIZEOF_FILE_HEADER);//1. 获取节表PIMAGE_SECTION_HEADER section_headersection_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_headerfile_header->SizeOfOptionalHeader);DWORD alignment = optional_header->SectionAlignment;//内存对齐大小 4096//2. 获取节表的个数int sections = nt_header->FileHeader.NumberOfSections;int offset = -1;for (int i = 0; i < sections; i){PIMAGE_SECTION_HEADER psection = section_headersection_headeri;//3. 获取节的RVADWORD section_start = psection->VirtualAddress;if (rva < section_start){offset = rva;return offset;}int block_count = psection->SizeOfRawData / alignment;block_count= psection->SizeOfRawData % alignment ? 1 : 0;//4. 判断导出表的相对虚拟地址rva 是否在这个节中if (rva >= section_start && rva < section_startblock_count * alignment){//5. 获取节的文件中偏移加上导出表的rva减去节的rva (真正在文件中的偏移地址)offset = psection->PointerToRawDatarva - section_start;return offset;}}return offset;}

//打印出 数据目录表void print_data_dir(char* base){PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base;PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)basedos_header->e_lfanew);PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header4);//4为nt头中Signature DWORDPIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_headerIMAGE_SIZEOF_FILE_HEADER);for (int i=0;i< IMAGE_NUMBEROF_DIRECTORY_ENTRIES;i){IMAGE_DATA_DIRECTORY data_dir = optional_header->DataDirectory[i];printf("%d data:x size:%x\n", i, data_dir.VirtualAddress, data_dir.Size);}IMAGE_DATA_DIRECTORY export_dir = optional_header->DataDirectory[0];int offset = rva_to_foa(base, &export_dir.VirtualAddress);//根据导出表的rva,进行foa转换//获取导出表PIMAGE_EXPORT_DIRECTORY export_table = (PIMAGE_EXPORT_DIRECTORY)(baseoffset);}

//IMAGE_EXPORT_DIRECTORY结构体 主要的字段//DWORDName;//DWORDBase;//IMAGE_EXPORT_DIRECTORY 导出表//DWORDNumberOfFunctions;//导出函数的个数//DWORDNumberOfNames;//函数名称的函数个数//DWORDAddressOfFunctions;导出函数的地址表 rva个数用NumberOfFunctions//DWORDAddressOfNames;// 导出函数名称表 rva个数用NumberOfNames//DWORDAddressOfNameOrdinals;// 导出序号表 rva个数用NumberOfNames//函数名字 通过AddressOfNames(将地址rva到foa) 查到之后,去AddressOfNameOrdinals 获取序号,然后在根据序号去AddressOfFunctions表找到函数的地址//序号通过序号 减去base 得到的下标,直接去AddressOfFunctions

上面拿到导出表,通过导出表解析,可以实现GetProcAddress功能.

动态库头文件:

#ifdef __cplusplusextern "C"{#endifint add(int a, int b);int sub(int a, int b);#ifdef __cplusplus}#endif

动态库源文件:

_declspec (dllexport)intadd(int a, int b){return ab;}_declspec (dllexport) intsub(int a, int b){return a - b;}

看看如何实现:

//获取函数的调用地址void* my_proc_address(char* base, char* func_name){PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base;PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)basedos_header->e_lfanew);PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header4);PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_headerIMAGE_SIZEOF_FILE_HEADER);PIMAGE_SECTION_HEADER section_headersection_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_headerfile_header->SizeOfOptionalHeader);IMAGE_DATA_DIRECTORY export_table = optional_header->DataDirectory[0];PIMAGE_EXPORT_DIRECTORYexport = (PIMAGE_EXPORT_DIRECTORY)(baseexport_table.VirtualAddress);unsigned long names = export->NumberOfNames;unsigned long* address_names = (unsigned long*)(baseexport->AddressOfNames);unsigned short* address_name_ordinals = (unsigned short*)(baseexport->AddressOfNameOrdinals);unsigned long* address_fuctions = (unsigned long*)(baseexport->AddressOfFunctions);for (int i = 0; i < names; i){char* name = (char*)(baseaddress_names[i]);if (strcmp(name, func_name) == 0){int ordinal = address_name_ordinals[i];void* func_addr = (void*)(baseaddress_fuctions[ordinal]);return func_addr;}}return NULL;}int main(int argc, char* argv[]){typedef int(*Add)(int a, int b);HMODULE hmodule = LoadLibraryEx("DllExportTable.dll", NULL, DONT_RESOLVE_DLL_REFERENCES);//my_proc_address 实现GetProcAddress()功能Add add = (Add)my_proc_address(hmodule, "add");int sum = add(100, 100);printf("sum=%d\n", sum);}

通过导出表获取函数的调用地址,实现GetProAddress功能

上面实现GetProcAddress函数的代码,为什么没有进行RVA到FOA的转换呢? 因为LoadLibraryEx函数已经dll文件加载到内存上进行了拉伸操作.所以不需要进行转换.

通过序号查找函数的调用地址

//通过序号,查找函数在内存中地址//1. 获取导出表在内存中的位置//2. 序号减去导出表中起始函数序号(Base),得到的下标就是void* my_proc_ordinal(char* base, int ordinal){if (ordinal > 0){PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base;PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)basedos_header->e_lfanew);PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header4);PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_headerIMAGE_SIZEOF_FILE_HEADER);PIMAGE_SECTION_HEADER section_headersection_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_headerfile_header->SizeOfOptionalHeader);IMAGE_DATA_DIRECTORY export_table = optional_header->DataDirectory[0];//导出表 内存中相对地址PIMAGE_EXPORT_DIRECTORYexport = (PIMAGE_EXPORT_DIRECTORY)(baseexport_table.VirtualAddress);//导出表 在内存的地址//这里获取函数个数,要特殊处理一下//正常情况下,用NumberOfFunctions就能获取到//其他情况下,如在def文件,让个别函数没有名称或者指定序号unsigned long numbers = export->Baseexport->NumberOfFunctions - 1;//起始序号加个数 在减一if (ordinal > numbers){return NULL;}unsigned long* address_fuctions = (unsigned long*)(baseexport->AddressOfFunctions);unsigned long base_count = export->Base;return (void*)(baseaddress_fuctions[ordinal - base_count]);}return NULL;}

个人能力有限,如果您发现有什么不对,请私信我

如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流

    推荐阅读
  • 形成酸雨的主要气体是什么(形成酸雨的主要气体)

    以下内容大家不妨参考一二希望能帮到您!形成酸雨的主要气体是什么酸雨是指PH小于5.6的雨雪或其他形式的降水,形成的主要气体有二氧化硫、三氧化硫、硫化氢、二氧化氮。酸雨主要是人为的向大气中排放大量酸性物质所造成的。酸雨又分硝酸型酸雨和硫酸型酸雨。

  • 木棉花的花语是什么(木棉花的意义)

    接下来我们就一起去了解一下吧!珍惜眼前的幸福,珍惜身边的人给他们快乐与幸福。它的花期通常在3月或者4月份,在这一段时间盛开,而传说中四月的第十一天,是木棉花盛开的日子,所以4月11被定为木棉花的日子。

  • 炒凉皮不碎技巧(炒凉皮不碎有什么技巧)

    以下内容大家不妨参考一二希望能帮到您!炒凉皮不碎技巧炒凉皮不碎技巧:就是在做凉皮时不能炒太久,变软会失去筋度。胡萝卜切丝,蒜薹切段,葱切花,猪肉切丝,大蒜拍扁。成品凉皮一张张卷起切粗条,抖散备用。生抽,白糖,盐,鸡精,醋,胡椒粉调成汁备用。热锅倒适量食用油烧热加入大蒜,肉丝翻炒至金黄,加入胡萝卜丝和蒜薹炒熟,凉皮翻炒均匀后随即淋入调好的汁儿翻炒均匀。

  • 近几年灭绝的鱼(瞭望在长江源寻鱼)

    长江被誉为我国淡水渔业的摇篮、鱼类基因的宝库。据青海省渔业部门统计,长江流域青海段分布有土著鱼类21种。因此,严格意义上长江源的关键鱼类指的是裂腹鱼中的小头裸裂尻鱼。2019年,李伟带领团队参加长江源科考时,将小头裸裂尻鱼列为长江源鱼类研究的代表对象。2019年4月,科考小组五个人,两台车,开始了沿河寻觅之旅。“全球平均气温上升已是科学界的共识,位于青藏高原的长江源是全球气候变化的敏感区。”科考发现,江源地区

  • 鹧鸪在什么时候季节鸣叫(鹧鸪的孵化期有多长)

    鹧鸪在什么时候季节鸣叫鹧鸪一般会在繁殖季节鸣叫,繁殖期为3-6月,3-4月间开始求偶交配。求偶期间鸣叫更为频繁,常在山岩、树桩、灌木或乔木枝上鸣叫,尤以黎明和黄昏时更甚,往往是一鸟先鸣叫,其他雄鸟一起跟随,此起彼伏。鹧鸪的孵化期在21天左右,雏鸟出壳后不久即可跟随亲鸟活动。鹧鸪的繁殖期为每年的3-6月,3-4月间开始求偶交配,每窝产卵3-6枚,多时可达8枚,卵为椭圆形或梨形,颜色为淡皮黄色至黄褐色。

  • 秋天的诗词(这些都是关于秋天的诗句)

    迢迢新秋夕,亭亭月将圆《戊申岁六月中遇火》,今天小编就来说说关于秋天的诗词?《戊申岁六月中遇火》自古逢秋悲寂寥,我言秋日胜春朝。《秋词》是处红衰翠减,苒苒物华休。惟有长江水,无语东流。宋·柳永《八声甘州》落时西风时候,人共青山都瘦。《昭君怨》雨色秋来寒,风严清江爽。《酬裴侍御对雨感时见赠》秋声万户竹,寒色五陵松。唐·李颀《望秦川》秋色无远近,出门尽寒山。宋·苏轼《九日次韵王巩》

  • 广州有几种车牌(广州车牌你有吗)

    在广州的普通上班族,有房贷还想拥有一辆车,已经不容易了。但有车想让个广州牌,那更是难上加难,再加之限行,参与摇号,竞价的人是越来越多,那中标的机会更是渺茫了!截止日期是8日24时止。9月拟配置的中小客车增量指标共16313个,是这样分配的:1.以摇号方式向单位和个人配置节能车增量指标7285个,其中,单位指标100个,个人指标7185个。

  • qq注销账号有哪几个步骤(QQ将开注销帐号功能)

    1999年2月10日,一个名为OICQ、只有几百K的软件正式上线。当时,腾讯方面表示,这是QQ团队对帐号注销功能的灰度测试。网友截图出于安全考虑,也有网友表示支持有人说,QQ不推出注销服务有自己的考虑,这是为了防止用户QQ密码被他人知道后恶意注销,给用户带来无法挽回的损失。腾讯2018年第三季度财报显示,QQ智能终端月活跃账户同比增长6.9%至6.979亿。

  • 高跟鞋不合脚怎么办(穿高跟鞋不合脚怎么办)

    4、合理利用袜子,如果不喜欢垫各种鞋垫的朋友,可以穿一双船袜,再穿高跟鞋,那样既不影响穿着效果,也不影响美观,也是比较简单和实用的方法。

  • 年四旺名字打分104分 年四旺事迹

    文章目录:一、年四旺相关名字打分113二、年四旺相关名字评分115三、年四旺相关名字推荐四、年四旺相关名字大全五、其他人还看了一、年四旺相关名字打分113年灯石志明年橘纪红兵武尊道后书法孔多塞年贷款孙敬媛年立秋里蓝业珍冯景华年见朱诗词林于思冯桂年粤日林格孟昭毅年家薛邑马布鱼鲁初雪苏沫沫卜庆中年上年掌柜秦源达刘登龙严学锋国韵酒年线高成江裘梦年维泗红沙日年周王克斌王翔千毛淑红龙威信李万和年神范小慧王大