《计算机病毒及其防范技术》第三章 计算机病毒结构分析B_第1页
《计算机病毒及其防范技术》第三章 计算机病毒结构分析B_第2页
《计算机病毒及其防范技术》第三章 计算机病毒结构分析B_第3页
《计算机病毒及其防范技术》第三章 计算机病毒结构分析B_第4页
《计算机病毒及其防范技术》第三章 计算机病毒结构分析B_第5页
已阅读5页,还剩39页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第三章计算机病毒结构分析(2)(6)引入表(ImportTable)两个概念:引入函数:是被某模块调用的但又不在调用者模块中的函数,因而命名为Import(引入)函数。引入函数实际位于一个或者更多的DLL里。调用者模块里只保留一些函数信息,包括函数名及其驻留的DLL名。DataDirectory:OptionalHeader最后一个成员就是DataDirectory,它是一个IMAGE_DATA_DIRECTORY结构数组,共有15个成员。DataDirectory包含了PE文件中各重要数据结构的位置和尺寸信息。每个成员包含了一个重要数据结构的信息。DataDirectory的每个成员都是IMAGE_DATA_DIRECTORY结构类型,其定义如下所示:IMAGE_DATA_DIRECTORYSTRUCTVirtualAddressdd//数据结构的相对虚拟地址(RVA)isizedd//VirtualAddress所指向数据结构的字节数IMAGE_DATA_DIRECTORYENDS15个成员序号便移包含信息简介096Exportsymbols导出表1104Importsymbols导入表2112Resources资源3120Exception异常4128Security安全5136Baserelocation重定位表6144Debug调试信息7152Copyrightstring版权信息8160GlobalPTR全局指针相对虚拟地址表9168Threadlocalstorage(TLS)本地线程存储10172Loadconfiguration装载配置表11180BoundImport具体资料不祥12188ImportAddressTable引入函数的地址(宿主程序中的地址)表13192DelayImport具体资料不祥14200COMdescriptorCOM描述子15208Reserved未使用引入表:实际上是一个IMAGE_IMPORT_DESCRIPTOR结构数组。每个结构包含PE文件引入函数的一个相关DLL的信息。如果该PE文件从10个不同的DLL中引入函数,那么这个数组就有10个成员。该数组以一个全0的成员结尾。IMAGE_IMPORT_DESCRIPTOR结构的定义如下:IMAGE_IMPORT_DESCRIPTORSTRUCTUnionCharacteristicsddOriginalFirstThunkddEndsTimeDateStampddForwarderChainddNameddFirstThunkddIMAGE_IMPORT_DESCRIPTORENDSOriginalFirstThunk含有指向一个IMAGE_THUNK_DATA结构数组的RVA。FirstThunk与OriginalFirstThunk的结构相同,只是作用不同。IMAGE_THUNK_DATA是一个指向IMAGE_IMPORT_BY_NAME结构的指针。注意IMAGE_THUNK_DATA包含了指向一个IMAGE_IMPORT_BY_NAME结构的指针,而不是结构本身。IMAGE_IMPORT_BY_NAME结构保存着一个引入函数的相关信息。IMAGE_IMPORT_BY_NAME结构的定义:IMAGE_IMPORT_BY_NAMESTRUCTHintdw//引入函数在原dll中的索引号Namedb//引入函数在原dll中的名字IMAGE_IMPORT_BY_NAMEENDS假设有几个IMAGE_IMPORT_BY_NAME结构,我们收集起这些结构的RVA(=IMAGE_THUNK_DATAs)组成一个数组,并以0结尾,然后再将数组的RVA放入OriginalFirstThunk。FirstThunk与OriginalFirstThunk区别OriginalFirstThunkIMAGE_IMPORT_BY_NAMEFirstThunk||IMAGE_THUNK_DATA--->Function1<---IMAGE_THUNK_DATAIMAGE_THUNK_DATA--->Function2<---IMAGE_THUNK_DATAIMAGE_THUNK_DATA--->Function3<---IMAGE_THUNK_DATAIMAGE_THUNK_DATA--->Function4<---IMAGE_THUNK_DATA...--->...<---...IMAGE_THUNK_DATA--->Functionn<---IMAGE_THUNK_DATAPE文件执行前PE文件执行时OriginalFirstThunkIMAGE_IMPORT_BY_NAMEFirstThunk||IMAGE_THUNK_DATA--->Function1AddressofFunction1IMAGE_THUNK_DATA--->Function2AddressofFunction2IMAGE_THUNK_DATA--->Function3AddressofFunction3IMAGE_THUNK_DATA--->Function4AddressofFunction4...--->......IMAGE_THUNK_DATA--->FunctionnAddressofFunctionn用函数名调用:IMAGE_IMPORT_BY_NAME用序号调用:IMAGE_THUNK_DATA值的低位字指示函数序数,而最高二进制位(MSB)设为1。例如,如果一个函数只由序数引出且其序数是1234H,那么对应该函数的IMAGE_THUNK_DATA值是80001234H。列出某个PE文件的所有引入函数的步骤:第一,校验文件是否是有效的PE。第二,从DOSheader定位到PEheader。第三,获取位于OptionalHeader的数据目录(DataDirectory)的地址。第四,转至数据目录的第二个成员提取其VirtualAddress值。第五,利用上值定位第一个IMAGE_IMPORT_DESCRIPTOR结构。第六,检查OriginalFirstThunk值。若不为0,则顺着OriginalFirstThunk里的RVA值转入那个RVA数组。若OriginalFirstThunk为0,就改用FirstThunk值。有些连接器生成PE文件时会置OriginalFirstThunk值为0,这应该算是个bug。不过为了安全起见,我们还是先检查OriginalFirstThunk的值。第七,对于每个数组元素,我们用IMAGE_ORDINAL_FLAG32来检查该元素的最高位。如果该元素值的最高二进位为1,那么函数是由序数引入的,可以从该值的低字节提取序数。如果元素值的最高二进位为0,就可将该值作为RVA转入IMAGE_IMPORT_BY_NAME数组,跳过Hint就是函数名字了。第八,再跳至下一个数组元素提取函数名,一直到数组底部(它以null结尾)。现在我们已遍历完一个DLL的引入函数,接下去处理下一个DLL。第九,即跳转到下一个IMAGE_IMPORT_DESCRIPTOR并处理之。依次循环直到数组结尾(IMAGE_IMPORT_DESCRIPTOR数组以一个全0域元素结尾)。(7)引出表(ExportTable)附加概念:引出函数,数据目录引出表是数据目录的第一个成员,又可称为IMAGE_EXPORT_DIRECTORY。IMAGE_EXPORT_DIRECTORY共有11个成员,部分介绍如下:域名含义nName模块的真实名称。该域是必须的,因为文件名可能会改变。这种情况下,PE装载器将使用这个内部名字。nBase基数,(引出序数-nBase)就是函数地址数组的索引值了。NumberOfFunctions模块引出的函数/符号总数。NumberOfNames通过名字引出的函数/符号数目。该值不是模块引出的函数/符号总数,这是由上面的NumberOfFunctions给出。本域可以为0,表示模块可能仅仅通过序数引出。如果模块根本不引出任何函数/符号,那么数据目录中引出表的RVA为0。AddressOfFunctions模块中有一个指向所有函数/符号的RVAs数组,本域就是指向该RVAs数组的RVA。简言之,模块中所有函数的RVAs都保存在一个数组里,本域就指向这个数组的首地址。AddressOfNames类似上个域,模块中有一个指向所有函数名的RVAs数组,本域就是指向该RVAs数组的RVA。AddressOfNameOrdinalsRVA,指向包含上述AddressOfNames数组中相关函数之序数的16位数组。两类输出方式:AddressOfFunctions和NumberOfFunctionsAddressOfNames和NumberOfNamesAddressOfNameOrdinals是用来统一上述两类输出方式的。这就相当于一个别名。它们的关联方式如下:AddressOfNamesAddressOfNameOrdinals||RVAofName1<-->IndexofName1RVAofName2<-->IndexofName2RVAofName3<-->IndexofName3RVAofName4<-->IndexofName4.........RVAofNameN<-->IndexofNameN根据引出函数名,怎样来获取其地址呢?1.定位到PEheader。2.从数据目录读取引出表的虚拟地址。3.定位引出表获取名字数目(NumberOfNames)。4.并行遍历AddressOfNames和AddressOfNameOrdinals指向的数组匹配名字。如果在AddressOfNames指向的数组中找到匹配名字,从AddressOfNameOrdinals指向的数组中提取索引值。例如,若发现匹配名字的RVA存放在AddressOfNames数组的第77个元素,那就提取AddressOfNameOrdinals数组的第77个元素作为索引值。如果遍历完NumberOfNames个元素,说明当前模块没有所要的名字。5.从AddressOfNameOrdinals数组提取的数值作为AddressOfFunctions数组的索引。也就是说,如果值是5,就必须读取AddressOfFunctions数组的第5个元素,此值就是所要函数的RVA。IMAGE_EXPORT_DIRECTORY结构的nBase成员

nBase是一个基数。(引出序数-nBase)就是函数地址数组的索引值了。如果程序员在def文件中设定起始序数号为200,这意味着AddressOfFunctions数组至少有200个元素。即使这前面200个元素并没使用,但它们必须存在。有了nBase,就节约了200个空元素。注意nBase并不影响AddressOfNameOrdinals数组的值。根据函数序数获取函数地址的步骤

1.定位到PEheader。2.从数据目录读取引出表的虚拟地址。3.定位引出表获取nBase值。4.减掉nBase值得到指向AddressOfFunctions数组的索引。5.将该值与NumberOfFunctions作比较,大于等于后者则序数无效。6.通过上面的索引就可以获取AddressOfFunctions数组中的RVA了。通过名字和序号访问的比较从序数获取函数地址比函数名快捷容易,因为这种方法不需要遍历AddressOfNames和AddressOfNameOrdinals这两个数组。虽然GetProcAddress支持两种调用方式,但是,用序号调用的时候比较复杂(要构造一个特殊的数值)。另外,在管理上,有名字会更好。WinPE察看器演示ExeDll源代码级PE察看器演示四、Win32文件型病毒编制技术Ring-3病毒的兼容性较好Ring-3病毒需要API的支持公开的未公开的1病毒的重定位技术为什么需要重定位?正常程序的变量和函数的相对地址都是预先计算好的。病毒是附加在宿主程序中的程序段,其问题在于:病毒变量和病毒函数的相对地址很难计算。解决方法:动态找一个参照点,然后再根据参照点的地址确定病毒函数和病毒变量的地址。calldeltadelta: popebp…leaeax,[ebp+(offsetvar1-offsetdelta)]参照量delta在内存中的地址+变量var1与参考量之间的距离=变量var1在内存中的真正地址2获取API函数为什么要获得API函数?正常程序用引入表获得病毒只是一个依附在正常程序中的代码段,没有自己的引入表思路:去动态连接库中寻找---〉找相应连接库(kernel32,user32等)在执行时的基地址。寻找基地址的方法包括(以kernel32为例):a)利用程序的返回地址,在其附近搜索Kernel32的基地址

Kernel32的push,在应用程序中用esp在堆栈中获取。CallCreateProcessCreatePrcess入口Push返回地址Jmp应用程序…PusheaxCallExitThread程序入口retOSkernel32.dll应用程序为什么能够从

4GB

的内存中得到

Kernel32.dll

的基地址呢?其实是这样的,Dll

有一个非常特殊的特性:当有别的程序调用它的时候,它的文件映象就会动态地映射到调用进程的内存地址空间。一般情况下,一个程序在运行的时候,

Kernel32.dll

这个

Dll

都会被映射到该程序的内存地址空间,成为它的一部分——这样一来,我们就可以在宿主的内存地址空间中搜索到

Kernel32.dll

的基地址了.b)对相应操作系统分别给出固定的Kernel32模块的基地址对于不同的windows操作系统来说,Kernel32模块的地址是固定的,甚至一些API函数的大概位置都是固定的。Windows98为BFF70000Windows2000为77E80000 WindowsXP为77E60000缺点是兼容性差GetAPI在得到了Kernel32的模块地址以后,我们就可以在该模块中搜索我们所需要的API地址。对于给定的API,可以通过直接搜索Kernel32.dll导出表的方法来获得其地址.同样我们也可以先搜索出GetProcAddress和LoadLibrary两个API函数的地址,然后利用这两个API函数得到我们所需要的API函数地址。3文件搜索

FindFirstFile:该函数根据文件名查找文件;FindNextFile:该函数根据调用FindFirstFile函数时指定的一个文件名查找下一个文件;FindClose:该函数用来关闭由FindFirstFile函数创建的一个搜索句柄;WIN32_FIND_DATA:该结构中存放着找到文件的详细信息。FindFileProca)

指定找到的目录为当前工作目录b)

开始搜索文件(*.*)c)

该目录搜索完毕?是则返回,否则继续d)

找到文件还是目录?是目录则调用自身函数FindFile,否则继续e)

是文件,如符合感染条件,则调用感染模块,否则继续f)

搜索下一个文件(FindNextFile),转到C继续FindFileEndp4内存映射文件内存映射文件提供了一组独立的函数,这些函数使应用程序能够像访问内存一样对磁盘上的文件进行访问。这组内存映射文件函数将磁盘上的文件的全部或者部分映射到进程虚拟地址空间的某个位置,以后对文件内容的访问就如同在该地址区域内直接对内存访问一样简单。这样,对文件中数据的操作便是直接对内存进行操作,大大地提高了访问的速度。应用步骤a)调用CreateFile函数打开想要映射的HOST程序,返回文件句柄hFile。b)调用CreateFileMapping函数生成一个建立基于HOST文件句柄hFile的内存映射对象,返回内存映射对象句柄hMap。c)调用MapViewOfFile函数将整个文件(一般还要加上病毒体的大小)映射到内存中。得到指向映射到内存的第一个字节的指针(pMem)。d)用刚才得到的指针pMem对整个HOST文件进行操作,对HOST程序进行病毒感染。e)调用UnmapViewFile函数解除文件映射,传入参数是pMem。f)调用CloseHandle来关闭内存映射文件,传入参数是hMap。g)调用CloseHandle来关闭HOST文件,传入参数是hFile。5病毒如何感染其他文件PE病毒感染其他文件的常见方法是在文件中添加一个新节,然后,把病毒代码和病毒执行后返回宿主程序的代码写入新添加的节中,同时修改PE文件头中入口点(AddressOfEntryPoint),使其指向新添加的病毒代码入口。这样,当程序运行时,首先执行病毒代码,当病毒代码执行完成后才转向执行宿主程序。病毒感染其他文件的步骤1.判断目标文件开始的两个字节是否为“MZ”。2.判断PE文件标记“PE”。3.判断感染标记,如果已被感染过则跳出继续执行宿主程序,否则继续。4.获得DataDirectory(数据目录)的个数,(每个数据目录信息占8个字节)。5.得到节表起始位置。(数据目录的偏移地址+数据目录占用的字节数=节表起始位置)6.得到节表的末尾偏移(紧接其后用于写入一个新的病毒节信息)节表起始位置+节的个数*

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论