这节与后面的给感染驱动都是新添加的章节,并且把第2部分的关于变形方面的章节合成为一节 "变形引擎的构建"。变形引擎可能要慢些。争取打造一份实用的东西出来。过了年在写,呵呵。这节主要讲解输出表的结构,输出表的HOOK,并实现了一个HOOK输出的小程序。
<目录>
1.什么是输出表
2.输出表的结构
3.输出表的HOOK
4.分析ExportHandler代码分析
<正文>
1.什么是输出表
用于定制一些函数,并提供给其他程序使用,建立一个函数与调用地址的映射。这种表一般存在与DLL文件当中。EXE文件中少有,不过不是不可以存在与EXE文件中。
2.输出表的结构
首先说明一下输出表的定位,输出表的目录存在于PE->可选头->数据目录的第一项中。可以通过此
目录获取输出表的RVA以及输出表的长度。通过RVA获取到它在文件中的偏移。读取偏移的第一个
结构为输出目录表,如下:

代码:
IMAGE_EXPORT_DIRECTORY STRUCT
  ;; 总是为0
  Characteristics           DWORD      ?
  ;; 文件创建的时间
  TimeDateStamp             DWORD      ?
  ;; 主版本号,一般为0
  MajorVersion              WORD       ?
  ;; 次版本号,一般为0
  MinorVersion              WORD       ?
  ;; DLL名称的RVA
  nName                     DWORD      ?
  ;; 序号基值, 一般为0
  nBase                     DWORD      ?
  ;; 输出函数的数量
  NumberOfFunctions         DWORD      ?
  ;; 以名字输出的数量
  NumberOfNames             DWORD      ?
  ;; 地址表的RVA
  AddressOfFunctions        DWORD      ?
  ;; 名字表的RVA
  AddressOfNames            DWORD      ?
  ;; 名字序号表的RVA
  AddressOfNameOrdinals     DWORD      ?
IMAGE_EXPORT_DIRECTORY ENDS
这里我们详细解释一下这个结构,输出表的结构很简单.
Characteristics;TimeDateStamp;MajorVersion;MinorVersion 都不重要,这里就不解释了。
nName:标示这个DLL的名字,通过这个值获取到文件的偏移可以看到是一个形如xxx.dll的字符串
nBase:这个值大多数情况下为1,他的作用是,如果通过序数引出的函数必须减去这个值后再通过
地址表取出地址.
NumberOfFunctions:总共输出表函数的数量.
NumberOfNames:以名字输出函数的数量.
AddressOfFunctions:这个值就是地址表的RVA了,是一个4字节的数组。
AddressOfNames:这个值的指向一个存放函数名字RVA的数组。通过对它的遍历输出所有函数。
AddressOfNameOrdinals:这个值指向一个存放名字索引以2字节为单位的数组。此表与AddressOfNames表同步使用.当从索引为3的AddressOfNames中对比名字,并从3的AddressOfNameOrdinals中获取序号X,在从AddressOfFunction[X]取出此函数的地址.
3.输出表的HOOK
输出表的HOOK很简单.只需要把位于AddressOfFunctions对应的RVA替换为你自己函数的RVA
就可以了。运行完毕后直接记的JMP回去,不过也可以不用...
4.分析ExportHandler代码分析
下面分析一下代码了.程序毕竟是工程还是写出点程序才实在.这个小工具使用很简单就一条命令
usage:ExportHandle dllname <scriptname>
当ExportHandler dllname 的时候会遍历函数名字表并且打印所有输出表的函数
当ExportHandler dllname scriptname 的时候会对前面的DLL使用后面一个简单的配置脚本进行
HOOK.脚本大体内容如下:
代码:
// export handlder script
// 一个简单的配置脚本

// 新节属性
$section_size = 1024;
$section_name = logic;
$section_characteristics = W|R|X;

// 新函数
$hostname = 53 55 56 57 8B F9;
这里做一下简单的解释
// 是注释
$section_size,$section_name,$section_characteristics是这个脚本专有的变量名对应的新节
大小,名称,还有属性.W=可写.R=可读.X=可执行.
$hostname = 53 55 56 57 8B F9; 是这样的hostname是这个DLL或者EXE的一个输出函数的名字
后面是要HOOK新函数的SHELLCODE. 这个小程序会添加一个新节并且把这些SHELLCODE记录到
新节并在后添加JMP 原函数的偏移 这样的语句. 然后修改输出表的地址。可以添加多个这样的变量
HOOK不同的函数.要说明的是这个脚本的SHELLCODE必须是大写,而且读脚本也没有做什么错误
处理(汇编不太适合做词法分析,写起来太麻烦了)
这里解释几个程序中重要的函数.
显示输出函数名字
代码:
ShowExportTable proc uses ebx ecx edx esi edi, szFilePath : LPSTR
  LOCAL pMem : LPVOID
  LOCAL hFile : HANDLE
  LOCAL hMap : HANDLE
  LOCAL PeHeader : LPVOID
  LOCAL pName : LPVOID
  LOCAL dwRVA : DWORD
  LOCAL dwOffset : DWORD
  LOCAL dwCount : DWORD
  ;; 映射文件
  invoke MapFile2MemNotClose, szFilePath, 0, addr hFile, addr hMap, addr pMem, 1
  mov esi, pMem
        ;; 获取PE头
  add esi, dword ptr [esi+03ch]
  assume esi : ptr IMAGE_NT_HEADERS
  mov PeHeader, esi
        ;; 获取输出表的位置
  lea edi, [esi].OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT * sizeof IMAGE_DATA_DIRECTORY]
  assume edi : ptr IMAGE_DATA_DIRECTORY
        ;; 确定输出表是否存在
  cmp [edi].VirtualAddress, 0
  jz Err_NoExportTable
  mov eax, dword ptr [edi].VirtualAddress
  invoke RVA2Offset, pMem, eax
  add eax, pMem
  xchg eax, edi
        ;; 打印输出表目录
  assume edi : ptr IMAGE_EXPORT_DIRECTORY
  invoke crt_printf, offset g_szOutFormat, offset g_szExportDirectory
  mov eax, dword ptr [edi].Characteristics
  invoke crt_printf, offset g_szCharacteristics, eax
  mov eax, dword ptr [edi].TimeDateStamp
  invoke crt_printf, offset g_szTimeDateStamp, eax
  movzx eax, word ptr [edi].MajorVersion
  invoke crt_printf, offset g_szMajorVersion, eax
  movzx eax, word ptr [edi].MinorVersion
  invoke crt_printf, offset g_szMinorVersion, eax
  mov eax, dword ptr [edi].nName
  invoke crt_printf, offset g_szName, eax
  mov eax, dword ptr [edi].nBase
  invoke crt_printf, offset g_szBase, eax
  mov eax, dword ptr [edi].NumberOfFunctions
  invoke crt_printf, offset g_szNumberOfFunctions, eax
  mov eax, dword ptr [edi].NumberOfNames
  invoke crt_printf, offset g_szNumberOfNames, eax
  mov eax, dword ptr [edi].AddressOfFunctions
  invoke crt_printf, offset g_szAddressOfFunctions, eax
  mov eax, dword ptr [edi].AddressOfNames
  invoke crt_printf, offset g_szAddressOfNames, eax
  mov eax, dword ptr [edi].AddressOfNameOrdinals
  invoke crt_printf, offset g_szAddressOfNameOrdinals, eax
  invoke crt_printf, offset g_szOutLine
  mov eax, dword ptr [edi].nName
  invoke RVA2Offset, pMem, eax
  add eax, pMem
  invoke crt_printf, offset g_szDllName, eax 
  invoke crt_printf, offset g_szListFunName
        ;; 遍历输出函数名字表 
  mov ecx, dword ptr [edi].NumberOfNames
  mov edx, dword ptr [edi].AddressOfFunctions
  invoke RVA2Offset, pMem, edx
  add eax, pMem
  mov edx, eax
        ;; ebx = 输出名字表在文件中的位置
        ;; edx = 输出函数表在文件中的位置
        ;; esi = 输出名字表索引表在文件中的位置
        ;; ecx = 输出名字表的个数 
  mov ebx, dword ptr [edi].AddressOfNames
  invoke RVA2Offset, pMem, ebx
  add eax, pMem
  mov ebx, eax
  mov esi, dword ptr [edi].AddressOfNameOrdinals
  invoke RVA2Offset, pMem, esi
  add eax, pMem
  mov esi, eax
  ;; zero ?
  test ecx, ecx
  jz Exit_ShowExportTable
  xor eax, eax
  mov dwCount, eax
  
Loop_NameTable:
        ;; 获取函数名称
  mov eax, dword ptr [ebx]
  invoke RVA2Offset, pMem, eax
  add eax, pMem
  mov pName, eax
        ;; 从序号表中获取名称的序号
  push esi
  mov eax, dwCount
  imul eax, eax, 02h
  add esi, eax
  movzx eax, word ptr [esi]
  imul eax, eax, 04h
  mov esi, edx
  add esi, eax
  mov eax, dword ptr [esi]
        ;; 从地址表中获取函数的RVA
  mov dwRVA, eax
  invoke RVA2Offset, pMem, eax
  mov dwOffset, eax
  pop esi
  ;; printf result
  push ecx
  push edx
        ;; 打印结果
  invoke crt_printf, offset g_szFunInfo, pName, dwRVA, dwOffset
  pop edx
  pop ecx
  
  add ebx, 04h
  inc dwCount
  loop Loop_NameTable

Exit_ShowExportTable:
  invoke FreeFileMemory, addr hFile, addr hMap, addr pMem
  assume esi : nothing
  assume edi : nothing
  ret
Err_NoExportTable:
  lea eax, g_szNoExportTbl
  invoke crt_printf, offset g_szOutFormat, eax
  xor eax, eax
  jmp Exit_ShowExportTable
ShowExportTable endp
HOOK输出函数
代码:
FixExportFunction proc uses ebx ecx edx esi edi, pMem : LPVOID, pShellCodeTo : LPVOID, pNewSectionRVA : LPVOID
  LOCAL pExportFunctionPointer : LPVOID
  LOCAL dwExportFunctionRVA : DWORD
  LOCAL dwShellCodeRVA : DWORD
  
  ;; 获取新节在文件中的位置
  mov edi, pNewSectionRVA
  invoke RVA2Offset, pMem, edi
  add eax, pMem
  mov edx, eax
  
  mov ebx, pShellCodeTo
  assume ebx : ptr SHELLCODETO
  
  ;; 查找要HOOK函数的RVA
  lea eax, [ebx].szFunctionName
  invoke FindExportFunction, pMem, eax
  test eax, eax
  jz Error_FixExportFunction
  mov pExportFunctionPointer, eax
  mov eax, dword ptr [eax]
  mov dwExportFunctionRVA, eax
  
  ;; 获取shellcode的RVA
  mov eax, pNewSectionRVA
  add eax, dword ptr [ebx].dwShellCodeOffset
  mov dwShellCodeRVA, eax
  mov eax, pExportFunctionPointer
  push dwShellCodeRVA
  pop dword ptr [eax]
  
  ;; 制作SHELLCODE,在末尾添加jmp 原函数偏移的5字节指令
  invoke MakeShellCode, ebx, dwExportFunctionRVA, dwShellCodeRVA
  
  ;; 写入shellcode
  mov edi, edx
  add edi, dword ptr [ebx].dwShellCodeOffset
  lea esi, [ebx].pShellCode
  mov ecx, dword ptr [ebx].dwShellCodeSize
  cld
  push edi
  rep movsb
  pop eax    ; set ret value -> shellcode location in file
Exit_FixExportFunction:
  assume ebx : nothing
  ret
Error_FixExportFunction:
  xor eax, eax
  jmp Exit_FixExportFunction
FixExportFunction endp
制作shellcode
代码:
MakeShellCode proc uses ebx esi edi, pShellCodeTo : LPVOID, dwExportFunctionRVA : DWORD, dwShellCodeRVA : DWORD
  mov ebx, pShellCodeTo
  assume ebx : ptr SHELLCODETO
  ;; 获取JMP的偏移,由于新节在代码的后面,所以想减后取补码并减去0E9h占用的
        ;; 1个字节就等于要跳转的偏移.
  mov esi, dwShellCodeRVA
  sub esi, dwExportFunctionRVA
  neg esi
  dec esi
  lea edi, [ebx].pShellCode
  add edi, dword ptr [ebx].dwShellCodeSize
  mov al, 0E9h
  mov byte ptr [edi], al
  inc edi
  mov dword ptr [edi], esi
  add [ebx].dwShellCodeSize, 05h
  assume ebx : nothing
  ret
MakeShellCode endp
最后要说明一下 这个小程序分析脚本没有做什么错误处理。请按照正确的格式编写 呵呵。。 也没有备份原文件等功能,使用前请做好备份工作。do.txt是脚本例子后面的shellcode是随便写的
没有真正的用途... 附件代码中还有一个支持库的代码如果要编译程序请先把Support编译为lib的库.然后在与ExportHandler进行链接。希望大家喜欢。。。
上传的附件 ExportHandler_bin.rar
ExportHandler_src.rar
do.txt