WindowsPE文件格式入门08.导出表
https://bpsend.net/thread-377-1-1.html
通过cff , depends灯等软件可以看到dll,导出函数的信息,因为dll中本身就存了这些信息,存了dll中有哪些导出函数,导出函数的序号是什么,名字是什么,以及他们的地址是什么,这些东西都存在导出表里面
导出表发展历程
最开始的时候只有序号导出,没有名称导出,使用的时候都是通过序号
| 序号 | 地址 |
|---|---|
| 1 | 1005 |
| 2 | 2005 |
| 3 | 3005 |
| 4 | 4005 |
| 5 | 5005 |
| 6 | 6005 |
| 7 | 7005 |
| 8 | 8005 |
但是上面的时间复杂度比较高是线性阶,但是对数阶占的体积比较大,所以只能折中,用常量阶.把地址作为数组,序号作为索引,只存地址,这样不仅速度更加快了,而且体积更小了
但是所以是从0开始的,因此可以数组首项加一个空

这样可以通过序号,直接去找对应数组对应的索引的值
序号不从1开始
但是上面有一个缺陷,序号不一定从0开始,例如 从 1001开始,这样数组不能前面加1000项0,这样不仅浪费空间,而且还用不上,解决办法是加一个 基址 (最小序号), 后面序号就根据基址来取偏移

例如上面,要拿到 序号1006 的函数地址, 可以 获取 1006对 1001 的偏移 5 ,再去数组取 索引为5的地址
序号不连续
解决了上面基本不从0开始的情况,还有一个问题,就是 序号不连续 ,例如 1001 , 1002 , 1020,1021,1050
这种情况下,数组中间对应的序号就需要填充0,因此会使体积变大,微软并没有解决这个问题,因为这是写dll的人自己的问题
例如
- 不指定序号(这样序号连续)


可以看出大小是38kb
- 部分指定序号使序号不连续


可以看到此时dll大小变成了 346KB
名称导出

序号跟成名2个表是拆开的,并不在一起,因此 基数数 ,导出地址表 ,导出名称表 ,导出序号表 构成了导出表
导出表结构
导出表结构
名称和序号是一一对应的,因此导出名称个数 和导出序号个数 只需要保存一个就可以了
IMAGE_EXPORT_DIRECTORY
// IMAGE_EXPORT_DIRECTORY 导出表结构体,40B
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 无用
DWORD TimeDateStamp; // 时间戳
WORD MajorVersion; // 无用
WORD MinorVersion; // 无用
DWORD Name; // 描述性字段:'模块的名字'
//上面20字节没用,说明性的 DWORD Base; // * 序号base基数,序号导出函数的索引值值从Base开始递增
DWORD NumberOfFunctions; // * 导出地址的个数
DWORD NumberOfNames; // * 导出名称的个数 DWORD AddressOfFunctions; // * 导出地址表的地址RVA
DWORD AddressOfNames; // * 导出名称表的地址RVA,按照ASCII码固定排序
DWORD AddressOfNameOrdinals; // * 导出序号表的地址RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
定位导出表:
数据目录第一项指针指向导出表。(第二项是导入表),该项只能说明导出表所在地址,导出表有多大并不是该Size来决定的(**但是它是导出函数是否转发的判断条件之一**)。



导出地址表


导出名称表



可以看出跟我们导出的顺序是不一样的,他进行了 函数名 ascii 码排序
导出序号表

解析
正常情况

在通过cff去看

- 像上面如果我们找到处序号为4的函数信息
- 拿到下标索引 序号 - base = 4 - 1 = 3
- 根据索引取导出地址表拿到地址 11195
- 用OD验证


- 拿到函数 foo 的地址
- 在导出名称表遍历,根据该函数名在导出名称表的索引 2
- 在根据上面索引在导出序号表拿到函数导出序号 2
- 在根据序号在 导出地址表拿到导出函数地址 110cd

导出表序号不从0开始的情况



此时情况是


导出表序号不连续的情况





- 例如要找序号为110的函数地址
- 获取在导出地址指表的索引 110 -100 = 10
- 根据索引在导出地址指表数组取值 110cd
- OD验证


- 寻找test的函数地址
- 先到导出名称表找到 test 在该数组索引 5
- 再到 导出序号表找到对应索引的导出地址的索引
- 再根据导出地址表的索引取导出地址表 获取 地址 1132f

- 找序号108 的 函数地址
- 获取序号108对应的导出地址的索引 108-100 = 8
- 再根据导出地址表的索引取导出地址表 获取 地址 0000 ,说明没有这个序号的导出函数
导出函数没有名称只有序号导出





这种情况OD可以看到 函数名是通过pdb文件知道的,删掉之后就不知道了
函数转发








可以看出转发的话,导出地址表存的是一个字符串的地址,而系统需要解析这个字符串拿出dll名和函数名,继续去查
而且该地址和其他地址不一样, 该地址只需要位于 导出表地址 和 导出表地址+导出表大小 中间 那么他就是一个转出函数,如果没有位于这中间,那么他就不是一个转发函数
如何判断转发函数,即地址指向应为字符串而不是实现?
- 如果是指向的字符串(表示为转发函数),是与导出函数名放在一起的,所以应该先借助正常遍历获取到地址,然后借助导出表的地址和大小判断地址的范围是否在导出表的范围之内:
- 不是则为正常函数,直接返回地址节课;
- 是则为转发函数,进行转发函数的处理。
遇到转发函数应该如何处理?
- 1.字符串为dll.函数名,所以要先分割解析,分别拿到dll名和函数名;
- 2.调用LoadLibrary;
- 3.再GetProcAddress递归遍历拿到地址。
模拟GetProcAddress
.586
.model flat,stdcall
option casemap:none include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc includelib user32.lib
includelib kernel32.lib
includelib msvcrt.lib WinMain proto :DWORD,:DWORD,:DWORD,:DWORD .data
g_szDll db "user32.dll",0
g_szFunc db "MessageBoxA",0 .code ;参数: 句柄 导出函数名
MyGetProcAddress proc hMod:HMODULE, lpProcName:LPCSTR LOCAL @pDosHdr:ptr IMAGE_DOS_HEADER ;dos头
LOCAL @pNTHdr:ptr IMAGE_NT_HEADERS ;Nt头
LOCAL @pExpDir:ptr IMAGE_EXPORT_DIRECTORY ;导出表 LOCAL @pAddrTbl:DWORD ;导出地址表地址
LOCAL @pNameTbl:DWORD ;导出名称表地址
LOCAL @pOrdTbl:DWORD ;导出序号表地址 ;解析
;dos 头
mov eax, hMod
mov @pDosHdr, eax ;nt头
mov esi, @pDosHdr
assume esi:ptr IMAGE_DOS_HEADER
mov eax, hMod
add eax, [esi].e_lfanew
mov @pNTHdr, eax mov esi, @pNTHdr
assume esi:ptr IMAGE_NT_HEADERS ;获取导出表
mov esi, @pNTHdr
assume esi:ptr IMAGE_NT_HEADERS mov eax, [esi].OptionalHeader.DataDirectory[0].VirtualAddress
add eax, hMod
mov @pExpDir, eax mov esi, @pExpDir
assume esi:ptr IMAGE_EXPORT_DIRECTORY ;导出函数地址表
mov eax, [esi].AddressOfFunctions
add eax, hMod
mov @pAddrTbl, eax ;导出函数名称表
mov eax, [esi].AddressOfNames
add eax, hMod
mov @pNameTbl, eax ;导入序号表
mov eax, [esi].AddressOfNameOrdinals
add eax, hMod
mov @pOrdTbl, eax ;判断是序号还是名称 (序号是一个 word,对于 dword来说高位都是0)
.if lpProcName & ffff0000h
;名称
mov ebx, @pNameTbl
xor ecx, ecx
.while ecx < [esi].NumberOfNames
;获取名称地址
mov eax, [ebx+ecx*4]
add eax, hMod ;字符串比较
push ecx
invoke crt_strcmp, lpProcName, eax
pop ecx
.if eax == 0
;找到了, 从导出序号表取出函数地址下标
mov edi, @pOrdTbl
movzx eax, word ptr [edi+ecx*2] ;从导入地址表,下标寻址,获取导出函数地址
mov ebx, @pAddrTbl
mov eax, [ebx+eax*4] ;判断转发 。。。。解析函数名,递归判断 ;返回地址
.if eax != NULL
add eax, hMod
ret
.endif .endif inc ecx
.endw .else
;序号
mov eax, lpProcName
sub eax, [esi].nBase ;获取索引值 ;从导入地址表,下标寻址,获取导出函数地址
mov ebx, @pAddrTbl
mov eax, [ebx+eax*4] .if eax != NULL
add eax, hMod
ret
.endif .endif xor eax, eax
ret MyGetProcAddress endp start:
invoke LoadLibrary, offset g_szDll
invoke MyGetProcAddress,eax, offset g_szFunc push MB_OK
push offset g_szDll
push offset g_szFunc
push NULL
call eax invoke ExitProcess,0
end start
WindowsPE文件格式入门08.导出表的更多相关文章
- JavaScript基础入门08
目录 JavaScript 基础入门08 DOM 介绍 绑定事件 给一组元素绑定事件 节点 节点树 节点类型 选取文档内容 通过id选取元素 通过指定的标签名选取元素 用指定的css类来选取元素 通过 ...
- YAML文件格式入门
YAML快速入门 https://www.jianshu.com/p/97222440cd08 https://yaml.org/spec/1.2/spec.pdf http://nodeca.git ...
- 【机器学习】机器学习入门08 - 聚类与聚类算法K-Means
时间过得很快,这篇文章已经是机器学习入门系列的最后一篇了.短短八周的时间里,虽然对机器学习并没有太多应用和熟悉的机会,但对于机器学习一些基本概念已经差不多有了一个提纲挈领的了解,如分类和回归,损失函数 ...
- WindowsPE 第五章 导出表编程-1(枚举导出表)
导出表编程-1-枚举导出表 开始前先回忆一下导出表: 1.枚举dll函数的导出函数名字: 思路: (1)加载dll到内存里. (2)获取PE头,逐步找到可选头部. (3)然后找到里面的第一个结构(导出 ...
- WindowsPE 第五章 导出表
导出表 PE中的导出表存在于动态链接库文件里.导出表的主要作用是将PE中存在的函数导出到外部,以便其他人可以使用这些函数,实现代码重用. 5.1导出表的作用 代码重用机制提供了重用代码的动态链接库,它 ...
- 机器学习入门08 - 表示法 (Representation)
原文链接:https://developers.google.com/machine-learning/crash-course/representation/ 机器学习模型不能直接看到.听到或感知输 ...
- Shell - 简明Shell入门08 - 函数(Function)
示例脚本及注释 #!/bin/bash function Check() # 使用function定义函数 { Say # 通过函数名直接调用函数 if test $1 then return 0 # ...
- C#零基础入门08:代码规范
一:前言 没有规矩,不成方圆.在代码的世界中,尤其这样.作为程序员,我们不想让我们的代码写出去之后被人耻笑:看,连个换行都换的这么不专业.作为开发主管,我们则不想我们的组员写出来的代码各类风格都有,五 ...
- springMVC入门-08
这一讲介绍用户登录实现以及两种异常处理controller控制器的方法,最后提一下在springMVC中对静态资源访问的实现方法. 用户登录需要一个登录页面login.jsp,对应代码如下所示: &l ...
- 【Asp.net入门08】第一个Asp.net应用程序-创建窗体并设置其样式
本节内容: 添加一个aspx窗体并设计窗体内容 为aspx窗体添加样式 前面我们为PartyInvites应用程序项目添加了两个c#文件:GuestResponse.cs和ResponseReposi ...
随机推荐
- Zookeeper - 本地模式部署
本地模式部署 zoo.cfg 参数解析 本地模式部署 1.上传zookeeper的安装包并解压 tar -zxvf zookeeper-x.x.x.tar.gz -c /xxx/xxx/ 2.将 zo ...
- Go红队开发—文件操作
目录 文件操作 创建目录 创建文件 获取File信息 文件重命名 删除文件 打开关闭文件 判断文件是否存在 判断文件是否有读取权限 复制文件 Read读取 ReadFull读取 ReadAtLeast ...
- abaqus建模时突发意外,软件闪退怎么才能找回操作?
abaqus/CAE 建模的时候可能经常由于各种各样的原因闪退(中断.卡住.未响应等等.) 这是很让人崩溃的时候,一个良好的习惯就是经常Ctrl+S,并且操作的时候不要太急,否则abaqus容易反应不 ...
- 墨者学院SQL注入(MySQL)的总结:
我们打开靶机 在这个界面我们可以看见在密码的下面有一个停机公告,我们点进去,会发现可以发现这个公告是存在id的,又可以会出现漏洞.所以我们尝试着在id=1的后面加上and 1=1 我们可以 ...
- Win系统重装备忘
蒙德,致态的盘坏块激增,似乎损坏到了系统文件:屏幕截屏会卡,关机后直接该块硬盘内的文件内容回滚,出现驱动报错要求重启... 然后尝试了DiskGenuis迁移系统,PE模式不能用,热迁移后似乎正常分区 ...
- 质数测试——Fermat素数测试和MillerRabin素数测试
质数测试 今天我来填坑了,之前我在数学基础算法--质数篇这篇文章中提到我要单独讲一下MillerRabin算法,最近已经有许多粉丝在催了,所以我马不停蹄的来出这篇文章了,顺便把Fermat素数测试也讲 ...
- FastAPI性能优化指南:参数解析与惰性加载
扫描二维码关注或者微信搜一搜:编程智域 前端至全栈交流与成长 探索数千个预构建的 AI 应用,开启你的下一个伟大创意 第一章:参数解析性能原理 1.1 FastAPI请求处理管线 async def ...
- Go 1.20更新了那些内容
PGO的引入 Go 1.20 发布了配置文件引导优化(PGO)的预览版,使编译器能够根据运行时配置文件信息,执行应用程序和工作负载的特定性优化.提供要构建的配置文件,使编译器能够将应用程序的速度提高大 ...
- linux部署go项目
直接部署: 1.将程序所需要的文件如配置文件和生成的可执行文件拷贝到linux中 2.直接执行./main命令,启动程序 (main是go编译生成的可执行文件) 如果报Permission denie ...
- Hack The Box-代理连接及靶机-Meow-喵呜
前言 在第一层,您将获得网络安全渗透测试领域的基本技能.您将首先学习如何匿名连接到各种服务,例如 FTP.SMB.Telnet.Rsync 和 RDP.接下来,您将发现 Nmap 的强大功能,这是 ...