2026/3/26 4:04:20
网站建设
项目流程
网站建设工作室起名,广州商城型网站建设,最新新闻事件今天报道,wordpress建站seo上一课我们已经学习了动态联接中关于引入表那部分知识#xff0c;现在继续另外一部分#xff0c;那就是引出表。下载 范例。理论:当PE装载器执行一个程序#xff0c;它将相关DLLs都装入该进程的地址空间。然后根据主程序的引入函数信息#xff0c;查找相关DLLs中的真实函数…上一课我们已经学习了动态联接中关于引入表那部分知识现在继续另外一部分那就是引出表。下载 范例。理论:当PE装载器执行一个程序它将相关DLLs都装入该进程的地址空间。然后根据主程序的引入函数信息查找相关DLLs中的真实函数地址来修正主程序。PE装载器搜寻的是DLLs中的引出函数。DLL/EXE要引出一个函数给其他DLL/EXE使用有两种实现方法: 通过函数名引出或者仅仅通过序数引出。比如某个DLL要引出名为GetSysConfig的函数如果它以函数名引出那么其他DLLs/EXEs若要调用这个函数必须通过函数名就是GetSysConfig。另外一个办法就是通过序数引出。什么是序数呢 序数是唯一指定DLL中某个函数的16位数字在所指向的DLL里是独一无二的。例如在上例中DLL可以选择通过序数引出假设是16那么其他DLLs/EXEs若要调用这个函数必须以该值作为GetProcAddress调用参数。这就是所谓的仅仅靠序数引出。我们不提倡仅仅通过序数引出函数这种方法这会带来DLL维护上的问题。一旦DLL升级/修改程序员无法改变函数的序数否则调用该DLL的其他程序都将无法工作。现在我们开始学习引出结构。象引出表一样可以通过数据目录找到引出表的位置。这儿引出表是数据目录的第一个成员又可称为IMAGE_EXPORT_DIRECTORY。该结构中共有11 个成员常用的列于下表。Field NameMeaningnName模块的真实名称。本域是必须的因为文件名可能会改变。这种情况下PE装载器将使用这个内部名字。nBase基数加上序数就是函数地址数组的索引值了。NumberOfFunctions模块引出的函数/符号总数。NumberOfNames通过名字引出的函数/符号数目。该值不是模块引出的函数/符号总数这是由上面的NumberOfFunctions给出。本域可以为0表示模块可能仅仅通过序数引出。如果模块根本不引出任何函数/符号那么数据目录中引出表的RVA为0。AddressOfFunctions模块中有一个指向所有函数/符号的RVAs数组本域就是指向该RVAs数组的RVA。简言之模块中所有函数的RVAs都保存在一个数组里本域就指向这个数组的首地址。AddressOfNames类似上个域模块中有一个指向所有函数名的RVAs数组本域就是指向该RVAs数组的RVA。AddressOfNameOrdinalsRVA指向包含上述 AddressOfNames数组中相关函数之序数的16位数组。上面也许无法让您完全理解引出表下面的简述将助您一臂之力。引出表的设计是为了方便PE装载器工作。首先模块必须保存所有引出函数的地址以供PE装载器查询。模块将这些信息保存在AddressOfFunctions域指向的数组中而数组元素数目存放在NumberOfFunctions域中。 因此如果模块引出40个函数则AddressOfFunctions指向的数组必定有40个元素而NumberOfFunctions值为40。现在如果有一些函数是通过名字引出的那么模块必定也在文件中保留了这些信息。这些 名字的RVAs存放在一数组中以供PE装载器查询。该数组由AddressOfNames指向NumberOfNames包含名字数目。考虑一下PE装载器的工作机制它知道函数名并想以此获取这些函数的地址。至今为止模块已有两个模块: 名字数组和地址数组但两者之间还没有联系的纽带。因此我们还需要一些联系函数名及其地址的东东。PE参考指出使用到地址数组的索引作为联接因此PE装载器在名字数组中找到匹配名字的同时它也获取了 指向地址表中对应元素的索引。 而这些索引保存在由AddressOfNameOrdinals域指向的另一个数组(最后一个)中。由于该数组是起了联系名字和地址的作用所以其元素数目必定和名字数组相同比如每个名字有且仅有一个相关地址反过来则不一定: 每个地址可以有好几个名字来对应。因此我们给同一个地址取别名。为了起到连接作用名字数组和索引数组必须并行地成对使用譬如索引数组的第一个元素必定含有第一个名字的索引以此类推。AddressOfNamesAddressOfNameOrdinals||RVA of Name 1RVA of Name 2RVA of Name 3RVA of Name 4...RVA of Name N--------...--Index of Name 1Index of Name 2Index of Name 3Index of Name 4...Index of Name N下面举一两个例子说明问题。如果我们有了引出函数名并想以此获取地址可以这么做:定位到PE header。从数据目录读取引出表的虚拟地址。定位引出表获取名字数目(NumberOfNames)。并行遍历AddressOfNames和AddressOfNameOrdinals指向的数组匹配名字。如果在AddressOfNames 指向的数组中找到匹配名字从AddressOfNameOrdinals 指向的数组中提取索引值。例如若发现匹配名字的RVA存放在AddressOfNames 数组的第77个元素那就提取AddressOfNameOrdinals数组的第77个元素作为索引值。如果遍历完NumberOfNames 个元素说明当前模块没有所要的名字。从AddressOfNameOrdinals 数组提取的数值作为AddressOfFunctions 数组的索引。也就是说如果值是5就必须读取AddressOfFunctions 数组的第5个元素此值就是所要函数的RVA。现在我们在把注意力转向IMAGE_EXPORT_DIRECTORY 结构的nBase成员。您已经知道AddressOfFunctions 数组包含了模块中所有引出符号的地址。当PE装载器索引该数组查询函数地址时让我们设想这样一种情况如果程序员在.def文件中设定起始序数号为200这意味着AddressOfFunctions 数组至少有200个元素甚至这前面200个元素并没使用但它们必须存在因为PE装载器这样才能索引到正确的地址。这种方法很不好所以又设计了nBase 域解决这个问题。如果程序员指定起始序数号为200nBase 值也就是200。当PE装载器读取nBase域时它知道开始200个元素并不存在这样减掉一个nBase值后就可以正确地索引AddressOfFunctions 数组了。有了nBase就节约了200个空元素。注意nBase并不影响AddressOfNameOrdinals数组的值。尽管取名AddressOfNameOrdinals该数组实际包含的是指向AddressOfFunctions 数组的索引而不是什么序数啦。讨论完nBase的作用我们继续下一个例子。假设我们只有函数的序数那么怎样获取函数地址呢可以这么做:定位到PE header。从数据目录读取引出表的虚拟地址。定位引出表获取nBase值。减掉nBase值得到指向AddressOfFunctions 数组的索引。将该值与NumberOfFunctions作比较大于等于后者则序数无效。通过上面的索引就可以获取AddressOfFunctions 数组中的RVA了。可以看出从序数获取函数地址比函数名快捷容易。不需要遍历AddressOfNames 和 AddressOfNameOrdinals 这两个数组。然而综合性能必须与模块维护的简易程度作一平衡。总之如果想通过名字获取函数地址需要遍历AddressOfNames 和 AddressOfNameOrdinals 这两个数组。如果使用函数序数减掉nBase值后就可直接索引AddressOfFunctions 数组。如果一函数通过名字引出那在GetProcAddress中可以使用名字或序数。但函数仅由序数引出情况又怎样呢? 现在就来看看。一个函数仅由序数引出意味着函数在AddressOfNames 和 AddressOfNameOrdinals 数组中不存在相关项。记住两个域NumberOfFunctions 和 NumberOfNames。这两个域可以清楚地显示有时某些函数没有名字的。函数数目至少等同于名字数目没有名字的函数通过序数引出。比如如果存在70个函数但AddressOfNames数组中只有40项这就意味着模块中有30个函数是仅通过序数引出的。现在我们怎样找出那些仅通过序数引出的函数呢?这不容易必须通过排除法比如AddressOfFunctions 的数组项在AddressOfNameOrdinals 数组中不存在相关指向这就说明该函数RVA只通过序数引出。示例:本例类似上课的范例。然而在显示IMAGE_EXPORT_DIRECTORY 结构一些成员信息的同时也列出了引出函数的RVAs序数和名字。注意本例没有列出仅由序数引出的函数。.386.model flat,stdcalloption casemap:noneinclude \masm32\include\windows.incinclude \masm32\include\kernel32.incinclude \masm32\include\comdlg32.incinclude \masm32\include\user32.incincludelib \masm32\lib\user32.libincludelib \masm32\lib\kernel32.libincludelib \masm32\lib\comdlg32.libIDD_MAINDLG equ 101IDC_EDIT equ 1000IDM_OPEN equ 40001IDM_EXIT equ 40003DlgProc proto :DWORD,:DWORD,:DWORD,:DWORDShowExportFunctions proto :DWORDShowTheFunctions proto :DWORD,:DWORDAppendText proto :DWORD,:DWORDSEH structPrevLink dd ?CurrentHandler dd ?SafeOffset dd ?PrevEsp dd ?PrevEbp dd ?SEH ends.dataAppName db PE tutorial no.7,0ofn OPENFILENAME FilterString db Executable Files (*.exe, *.dll),0,*.exe;*.dll,0db All Files,0,*.*,0,0FileOpenError db Cannot open the file for reading,0FileOpenMappingError db Cannot open the file for memory mapping,0FileMappingError db Cannot map the file into memory,0NotValidPE db This file is not a valid PE,0NoExportTable db No export information in this file,0CRLF db 0Dh,0Ah,0ExportTable db 0Dh,0Ah,[ IMAGE_EXPORT_DIRECTORY ],0Dh,0Ahdb Name of the module: %s,0Dh,0Ahdb nBase: %lu,0Dh,0Ahdb NumberOfFunctions: %lu,0Dh,0Ahdb NumberOfNames: %lu,0Dh,0Ahdb AddressOfFunctions: %lX,0Dh,0Ahdb AddressOfNames: %lX,0Dh,0Ahdb AddressOfNameOrdinals: %lX,0Dh,0Ah,0Header db RVA Ord. Name,0Dh,0Ahdb ----------------------------------------------,0template db %lX %u %s,0.data?buffer db 512 dup(?)hFile dd ?hMapping dd ?pMapping dd ?ValidPE dd ?.codestart:invoke GetModuleHandle,NULLinvoke DialogBoxParam, eax, IDD_MAINDLG,NULL,addr DlgProc, 0invoke ExitProcess, 0DlgProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD.if uMsgWM_INITDIALOGinvoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_SETLIMITTEXT,0,0.elseif uMsgWM_CLOSEinvoke EndDialog,hDlg,0.elseif uMsgWM_COMMAND.if lParam0mov eax,wParam.if axIDM_OPENinvoke ShowExportFunctions,hDlg.else ; IDM_EXITinvoke SendMessage,hDlg,WM_CLOSE,0,0.endif.endif.elsemov eax,FALSEret.endifmov eax,TRUEretDlgProc endpSEHHandler proc uses edx pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORDmov edx,pFrameassume edx:ptr SEHmov eax,pContextassume eax:ptr CONTEXTpush [edx].SafeOffsetpop [eax].regEippush [edx].PrevEsppop [eax].regEsppush [edx].PrevEbppop [eax].regEbpmov ValidPE, FALSEmov eax,ExceptionContinueExecutionretSEHHandler endpShowExportFunctions proc uses edi hDlg:DWORDLOCAL seh:SEHmov ofn.lStructSize,SIZEOF ofnmov ofn.lpstrFilter, OFFSET FilterStringmov ofn.lpstrFile, OFFSET buffermov ofn.nMaxFile,512mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLYinvoke GetOpenFileName, ADDR ofn.if