滴水三期:day39.1-IAT表和导入表

《Windows PE》4.1.3 IAT函数地址表 如果导入表描述符的TimeDateStamp和ForwarderChain字段是一个特殊的标志值,如0xFFFFFFFF,表示导入函数已绑定,则IAT函数地址表中存储的是真实的导入函数地址。在程序加载时,操作系统会根据导入描述符中的INT表和IAT表进行动态链接,将导入函数的实际入口地址填充到IAT表中的函数指针对应的位置。IAT表就是用来存储这些实际的函数入口地址。首先找到数据目录项的第12项,获取IAT函数地址表的RVA地址,加上基址后,得到并输出加载到内存中的IAT函数地址表的VA地址。 阅读详情

一、引入IAT表

1.调用自己写的函数

1)在内存中(ImageBuffer)
  • 编写一个C程序,调用自己写好的函数

    #include "stdafx.h"
    void my_method(){
         
         
    	int a = 1;
    	a++;
    	printf("%d",a);
    }
    int main(int argc, char* argv[]){
         
         
    	my_method();
    	return 0;
    }
    
  • 可以通过OD查看程序再运行时的数据,也可以直接在VC上运行查看反汇编(这里使用VC查看方便一些)。发现:这里调用函数时,call后面跟的是0xFFFF3B7D(前面说过硬编码为E8的call后面跟的地址是相对地址:0x40100a - (0x40D488 + 5) = 0xFFFF3B7D),即实际上call的是0x40100a这个地址;再按F11跳到0x40100a,发现这是一个跳板:jmp 0x401010(硬编码是E9 01 00 00 00),最终0x401010就是自己写的函数开始的地址

    212658 image-20230413225753048
2)在硬盘上(FileBuffer)
  • 先通过PETool查看程序的ImageBase和文件、内存对齐粒度,可以算出0x40100a对应的文件偏移地址为0x100a

    213323
  • 接着我们用Winhex打开程序,查看一下程序未运行时0x100a地址处和运行时跳板处硬编码一样:都是E9 01 00 00 00,那么jmp真正要跳转的地址为:0x00000001 + 0x0000100F = 0x00001010(跟E8后面算地址的方法一样)

    image-20230413230251715
  • 最后看一下0x1010地址处刚好就是自己写的函数的硬编码

    230656
3)结论
  • 一个程序调用了自己写的函数:无论运行前还是运行时,call后面的地址已经“写死”,就是一个跳板的绝对地址,而跳板跳的就是自己写的函数地址;故程序在编译后,call后面的地址就写死了

  • 敢写死是因为,自己写的函数存储地址都是在程序本身的.exe中,而程序运行时,.exe装载到程序的虚拟内存中是不会有别的PE文件和它争抢位置的,所以.exe会按照程序的ImageBase正常装载,进而这些写死的地址值就是有效的

    和调用系统DLL中的函数结论做一个对比

2.调用DLL中的函数

1)在内存中(ImageBuffer)
  • MessageBox()函数位于user32.dll中,属于系统提供的API

  • 现在编写一个C程序,调用MessageBox()函数;

    #include "stdafx.h"
    #include <windows.h>
    int main(int argc, char* argv[]){
         
         
    	MessageBox(0,0,0,0); 
    	ExitProcess(0); //这是一个退出进程函数,也是系统API
    	return 0;
    }
    
  • 编译成Release版后,使用OD打开这个.exe程序,找到程序OEP,回车进去(前面学过,一般情况下,找三个连续的push,后面这个call就是程序入口)

    183811
  • 找到程序调用MessageBox()函数这里,发现call后面跟的是一个间接寻址:[0040509C],即真正call的实际上是0x40509C地址中的值!所以我们输入db 40509C,去看看0x40509C这个地址中存的什么:发现存的是MessageBox函数的内存中的绝对地址:0x77D36476,这个函数地址不是.exe程序本身的地址,而是使用的一个DLL中的地址,我们可以再打开工具栏的E查看一下

    小提示:这里call的硬编码是FF15(不是E8),FF15的call后面跟的地址是内存的绝对地址(E8的是相对地址)

    4049 84316
2)在硬盘上(FileBuffer)
  • 现在我们看看0x40509C内存绝对地址转换成程序没运行时的地址,这个地址存的是什么?所以首先需要先用PETools查看程序的ImageBase和文件、内存偏移粒度,将0x40509C转化成文件地址为:0 + (0x40509C - 0x400000) = 0x509C

    182736
  • 接着打开Winhex查看程序的0x509C地址中存的是:0x00005538,这是一个文件==偏移地址==

    image-20230413185007455
  • 最后查看0x5538地址中存的是:MessageBoxA的函数名称字符串的ASCII码。会发现这里有很多函数名字符串!(这里就是函数名称表IMAGE_IMPORT_BY_NAME)

    image-20230413185255835
3)结论并引入IAT表
  • 发现一个使用了系统DLL函数的程序,在调用DLL函数时都使用了间接寻址,但是

    • 在运行时:call [0x40509C],0x40509C地址存储的是0x77D36476(这是MessageBox()函数的内存绝对地址),即实际上call的是0x77D36476
    • 运行前:call [0x509C],0x509C地址存储的是0x5538(这里没有直接写MessageBox函数的内存绝对地址),即实际上call的是0x5538
  • 所以使用了系统DLL函数的程序,编译后(运行前)并没有直接把函数在DLL中的绝对地址(DLL的ImageBase + RVA = 0x77D36476)写到call后面,而是写了程序自己的.exe空间中的一个文件偏移地址(0x5538),这个文件偏移地址指向了此程序使用的DLL中的函数名称

    • 一个程序会使用很多别的DLL中的函数,所以==运行前:会有很多个上述的文件偏移地址,这些指向了函数名称的文件偏移地址就构成了IAT表!==
  • 但是程序运行时,程序先装载自己的.exe到程序的虚拟内存中,再依次装载使用到的DLL到程序的虚拟内存中,此时DLL在虚拟内存中的位置已经固定了,所以操作系统会把call后面的值改为函数在DLL中的绝对地址(此时DLL的ImageBase + RVA)

    • 运行时:操作系统会把IAT表中的文件偏移地址改成此时对应函数的绝对地址,即运行后==IAT表中的值就全变成了使用的DLL函数在内存中的绝对地址==

    注意:如果发生了DLL装载地址冲突,DLL会往后找空闲内存存放,此时操作系统会先修正重定位表,接着把修正后的函数在DLL中的绝对地址放到call后面

  • 所以IAT表的内容在程序执行前和执行后是不一样的!

4)问题
  • 那为什么编译时不直接把MessageBox函数在DLL中的绝对地址放到call后面?
  • 就是因为day37.1-重定位表中学过的:MessageBox所在的DLL在装载时并不一定会按照其指定的ImageBase装载,可能会出现DLL装载地址冲突,导致该DLL最终装载的地址和其ImageBase不一致!那么所有写死的绝对地址都失效了!
  • 但是我们自己写的函数在程序的.exe中,而一个程序运行时它本身的.exe装载地址就是自己的ImageBase,一般不会有其他的PE文件和它抢占,所以直接把函数的内存绝对地址放到call后面即可

3.易错误区

  • 调用函数的地址问题和修复重定位表关系:二者没有任何关系!!!!

  • 程序调用DLL的函数:编译时,call后面的地址值不会写死,而是间接寻址,指向一个DLL中函数名字字符串的地方;但是在运行时,会直接把此时DLL装载到内存后的函数绝对地址写到call后面(间接指向)!所以这里修改的是程序自己的.exe中调用函数的call语句中的地址值

  • 而重定位表修复:修复的地址是DLL中的地址,而不会像上述一样还会帮使用自己函数的程序修复程序调用函数时call后面的地址!所以重定位表修复的是DLL自身当中的一些地址(比如DLL中的函数绝对地址、DLL中的全局变量等),因为可能会遇到DLL装载时没有按照指定的ImageBase正常装载,所以要修改DLL自身中写死的地址

二、导入表

1.导入表是什么

  • 前面的IAT表,运行前和运行时的内容是怎么确定下来的,会在导入表的讲解中迎刃而解,所以导入表和IAT表有很大的关系

  • 导入表:即一个PE文件(.dll/.exe等),它如果需要使用使用别的PE文件的函数,就需要一个清单来记录使用的别的PE文件的函数相关信息(使用了哪些DLL、使用了这个DLL里的哪些函数、叫什么名、去哪找等)

  • 所以一般而言:程序通过导入表中的信息来装载DLL到虚拟内存中(比如通过导入表的所有Name成员,知道要装载哪些DLL;或者如果导入表的某个结构中OriginalFirstThunk和FirstThunk都为0,系统就不会加载这个导入表结构对应的DLL,因为操作系统知道你没有使用这个DLL中的任何函数)

    上面内容复习的时候再看。程序需要导入表中的一些信息来加载DLL,但修复导入表中的某些信息是在DLL装载完成后(比如修复IAT表),所以这些都是有过程的,不要混为一谈

  • .exe一般没有导出表、有导入表;.dll一般既有导出表、又有导入表(因为一个.dll可能也需要使用别的.dll提供的函数,且.dll也会提供函数给别的PE文件用)

2.定位导入表

  • 一个PE文件的导入表数据目录在PE文件第二个数据目录结构体

  • 再根据导入表数据目录的VirtualAddress成员找到导出表(RVA转FOA)

    190056

3.导入表结构

  • 导入表中的结构:

    struct _IMAGE_IMPORT_DESCRIPTOR{
         
         							
        union{
         
         							
            DWORD Characteristics;           							
            DWORD OriginalFirstThunk;  //RVA,指向IMAGE_THUNK_DATA结构数组(INT表)
        };							
        DWORD TimeDateStamp;  //时间戳	(用于判断是否有绑定导入表/IAT表中是否已经绑定绝对地址)
        DWORD ForwarderChain;              							
        DWORD Name;  //RVA,指向dll名字字符串存储地址
        DWORD FirstThunk;  //RVA,指向IMAGE_THUNK_DATA结构数组(IAT表)
    };
    //导入表有很多个这种结构(成员全为0,表示结束)
    
  • 注意:导入表不可能就一个这种结构,因为一个PE文件可能会使用多个DLL中的多个函数,一个这样的结构只表示一个DLL中的函数;所以PE文件使用了多少个DLL中的函数,就有多少个这种结构

  • 当遇到有sizeof(_IMAGE_IMPORT_DESCRIPTOR)个0x00时,表示导出表结束(相当于一个导入表结构中的成员全为0x00000000,就表示导入表结束)

  • 由于IAT表在程序运行前后是不一样的,所以导入表和IAT表的具体关系结构图示如下:

    1. PE文件运行前:

      image-20230416183556587
    2. PE文件运行后:

      image-20230416183626572
1)OriginalFirstThunk
  • RVA,指向INT表(导入名称表),如果想得到内存地址,即用ImageBase + RVA即可;如果想得到文件地址,即把RVA转FOA即可
2)Name
  • RVA,指向DLL名字字符串所在地址;比如Name指向的DLL名称为“user32.dll\0”,那么这个结构中记录的就是user32.dll中函数的相关信息;所以一个PE文件的导入表有多少中这种结构,就使用了多少个其他DLL中的函数
3)FirstThunk
  • RVA,指向IAT表(导入地址表)
4)TimeDataStamp

时间戳字段看不懂没事,详情在day40.1-绑定导入表

  • 为0(即0x00000000):表示这个导入表结构对应的DLL中的函数绝对地址没有绑定到IAT表中
  • 为**-1**(即0xFFFFFFFF):表示这个导入表结构对应的DLL中函数绝对地址已经绑定到IAT
手把手教你用Scylla修复Mp3Resizer.exe的IAT表(附脱壳完整流程) 本文详细解析了使用Scylla工具修复Mp3Resizer.exe导入地址表(IAT表)的完整脱壳流程。针对新手在修复IAT时常见的“未知库文件”难题,提供了从定位OEP、内存转储到精准修复IAT的清晰步骤,并重点讲解了如何识别并移除无效导入项,确保脱壳后的程序可稳定运行。 阅读详情

相关推荐

Windows上python的spark环境搭建

1.安装好JDK 下载并安装好jdk-12.0.1_windows-x64_bin.exe,配置环境变量: 新建系统变量JAVA_HOME,值为Java安装路径 新建系统变量CLASSPATH,值为 .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;(注意最前面的圆点) 配置系统变量PATH,添加 %JAVA_HOME%bin;%JAVA_HOME%jrebin 在CMD中输入:java或者java -version,不显示不是内部命令等,说明安装成功。

weixin_43915303的博客 651

手把手教你用8位单片机IO口直接驱动WS2812灯带(附完整代码和避坑指南)

本文详细介绍了如何使用8位单片机IO口直接驱动WS2812灯带,包括硬件准备、时序生成、色彩引擎与特效算法的实现,以及调试技巧与性能优化。通过汇编语言优化和精确时序控制,实现高效的LED驱动和炫酷的彩虹灯效,适合嵌入式开发者和硬件爱好者参考。

weixin_42696271的博客 304

STM32G474+Simulink+HIL硬件在环仿真

硬件:STM32G474RET6(官方的NUCLEO板)软件:STM32Cube_FW_G4 V1.6.1芯片包;KeilMATLAB 2023B(本文已成功)STM32嵌入式支持包。

qq_44701116的博客 1779

【自动驾驶端到端5:VADv2: End-to-End Vectorized Autonomous Driving via Probabilistic Planning】

从大规模人类驾驶演示中学习类人驾驶策略是有希望的。但是,规划的不确定性使得从驾驶演示中提取驾驶知识是有挑战性的。从统计学习的角度来看,动作(包括实际和速度)是高度随机的,受许多无法建模的潜在因素影响。

weixin_40000560的博客 1404

基于fme和python-docx第三方库批量更新不动产调查表

最近应朋友需要批量更新在不动产成果中提交的各种word报表中的指定数据,根据初步的了解,我做了一个基于fme软件和python-docx第三方库进行一个批量的更新方案。

weixin_43229258的博客 1240

《Python Web项目集锦》-专栏介绍和目录

本专栏专注于Python Web项目,涉及内容包含FastAPI、Django、Flask等,所有项目代码完整且可直接运行。不管是零基础还是有经验的开发,相信都能从本专栏获益。有任何问题欢迎私聊博主,专栏持续更新中...

数据知道的博客 5212

一张微服务地图的十年演进:Spring Cloud 核心、扩展与工具组件的定位、原理与选型

本文把 Spring Cloud 当作一张微服务地图来读:先说明它是由 Commons 抽象层、实现层和 Boot 装配层构成的发行版,再沿一次请求梳理注册发现、负载均衡、声明式调用、容错限流、网关、配置、追踪与总线七类核心组件的原理与换代,并扩展到云平台、服务网格、消息流、批处理、安全与周边生态。仿真实测:默认参数下实例宕机平均 242 秒才被感知,优雅下线降到 47 秒;一个慢实例让轮询的 P99 达 49 秒,P2C 仅 203 毫秒。最后给出退场替代表、技术栈组合、升级路线和决策树。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 169万+

【学习系列】SAP RAP 29:如何创建Application Jobs(下)

本文介绍了在SAP系统中通过API创建和使用Application Jobs的方法。主要内容包括:1) 创建RAP应用并通过Action调用API创建Jobs;2) 使用事件驱动方式异步更新Job状态;3) 通过URL跳转到标准日志页面;4) 使用Custom Entity获取Job日志;5) 演示遗留接口IF_APJ_DT_EXEC_OBJECT和IF_APJ_RT_EXEC_OBJECT的使用。文章提供了详细的代码示例,展示了如何通过CL_APJ_RT_API=>SCHEDULE_JOB调度作业,

DeveloperMrMeng的博客 376

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

上一篇: 滴水三期:day38.1-移动导出表和重定位表
下一篇: 滴水三期:day40.1-绑定导入表
EdimadeZhou
博客等级 码龄5年 241粉丝 128原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值