病毒编写教程---Win32篇(一)

【译者声明】

~~~~~~~~~~~

    这是一篇关于病毒基础知识的教程,作者Billy Belceb,西班牙人,在16岁时写的这篇教程,曾创建了病毒组织DDT。翻译这篇教程的目的是想揭开病毒的神秘面纱,从编写病毒的角度来学习病毒,希望对大家有用。由于原文为西班牙人写的英文,译者翻译教程也不多,英语只是凑合,错误之处还请大家原谅,如果大家发现翻译有什么不当之处,欢迎改正,大家也可对照原文学习。(原文在29A#4)。大家都知道,我们脱一个壳经常见到某某壳用了某某病毒技术,到底病毒技术是那些呢?比较经典而全面的Win32病毒教程就是Billy Belceb写的本教程,可惜一直没有人翻译成中文,我作为一个大傻鸟,就决定翻译了。谨以此翻译献给所有的Cracker和所有对Win32汇编感兴趣的人。下面为原文译文,祝你好运!

 

【声明】

~~~~~~~

    作者对因对此文档使用不当而造成的任何损失概不负责。这篇教程的目的是教会人们编写病毒和防护一些破坏力大的病毒的破坏。这篇教程仅作为教学目的。所以,如果有人利用这篇文章编写了破坏力很大的病毒,我可不负责任。如果通过这篇文章你看到我鼓励人们破坏数据的字眼,先去买副眼镜再说。

 

【介绍】

~~~~~~~

    亲爱的同志们,大家好,你还记得Billy Belceb的病毒编写教程吗?那是一篇关于过时的MS-DOS病毒的教程。在那篇教程中,我一步一步地介绍了很多有名的DOS病毒技术的知识,而且它是为初学者写的,使他们尽快地入门。现在,我又写了一篇很酷(我希望是)的教程,但是这一次我将介绍现在计算机的新威胁,Win32病毒,毫无疑问,所有的东西都是和那个有关了。我发现现在一个完整的教程很缺,所以我曾问自己...为什么我不写一篇关于这个的教程?所以我又写出来了:)真正的在Win32病毒的先驱是VLAD组织,而用这种方式来写教程的作者是Lord Julus。但是我不会忘记那些写了很多有趣教程的人,和在Lord Julus的教程之前的所有相关东西,当然我在说JHB啦。有趣的技术是由Murkry研究的,后来Jacky Qwerty...我希望我没有忘记在Win32病毒编写(很短)史上的重要的人。注意我从来没有忘本。象在我的病毒编写教程系列里一样,我要谢谢一些音乐组织,如Blind Guardian,HammerFall,Stratovarius,Rhapsody,Marilyn Manson,Iron Maiden,Metallica,Iced Earth,RAMMS+EIN,Mago De Oz,Avalanch,Fear Factory,Korn,Hamlet Def Con Dos。所有这些东西营造了写一篇巨大的教程和代码的完美的氛围。

    嗨,我的教程的结构已经有了很大的改变,现在我给出一个索引,几乎所有给出的代码都是我编写的,或者基于其他人的但是被我改编了的,或者有一点删改的;)但是,嗨,我已经努力的解决在我的现在已经绝种了的MS-DOS(RIP)VWG中遇到的所有问题。

    我必须向Super/29A问好,是他帮助了这篇教程的一些方面的东西,他是我的beta测试人之一,而且他已经对这篇教程贡献了一些东西。

 

说明:英语不是我的母语(西班牙语才是)【译者注:所以这篇西班牙式的病毒教程很难翻译,不当之处还请原谅】,所以原谅我的许多拼写错误,请告知我,我会修正的。我已经引用了已经在一些独立的病毒杂志里发表了的文章,但是它们仍然值得一读因为我已经修改了,进行了语法检查,并加入了一些额外的信息。记住:这篇文章并不完美,所以原谅在这篇教程中的错误。

 

----------跟我联系

 -E-mail        [email protected]                [email protected]

 -ICQ #           22290500

个人主页                  http://members.xoom.com/billy_bel&n...sp;    http://www.cryogen.com/billy_belcebu

组织主页                  http://sourceofkaos.com/homes/ddt

IRC [Billy_Bel]            Undernet #vir, Irc-Hispano #virus

祝玩得快乐!

Billy Belceb

 

    美梦从这里开始...

 

(c) 1999 Billy Belcebu/iKX

 

【索引】

~~~~~~~

    有人(Hi Qozah!)已经告诉我,当他读这篇教程的beta版本时,它有一点混乱,因为容易迷失在各章之间。无论如何,我已经对这个重新组织了,我仍然很混乱,而且我的教程也是:)

 

01.声明

02.介绍

03.索引

04.病毒编写中的有用的东西

05.简单介绍

06.PE 文件头

07.Ring-3,用户级编码

08.Ring-0,系统级编码

09.Per-Process residency

10.Win32优化

11.Win32反调试

12.Win32多态

13.高级Win32技术

14.附录一:病毒发作

15.附录二:关于作者

16.结束语

 

【病毒编写中的有用的东西】

~~~~~~~~~~~~~~~~~~~~~~~~~

    在开始编写病毒之前,你需要一些东西。下面是我给你推荐的程序(如果你没有足够的金钱来买它们...下载!)   :)

   

    Windows 95 Window NT Windows 98 Windows 3.x + Win32s :)

    TASM 5.0 (包括TASM32 TLINK 32)

    SoftICE 3.23+(或更好) for Win9X for WinNT

    API 列表(Win32.HLP)

    Windows95 DDKWindows98 DDKWindows2000 DDK...即所有的微软DDKSDK

    强烈推荐Matt Pietrek关于PE文件头的文章。

    Jacky QwertyPEWRSEC工具(在你在'.code'里添加代码时用)

    一些hash...shit!它是我想要的! :)

    一些电子杂志如29A(#2,#3),Xine(#2,#3,#4),VLAD(#6),DDT(#1)...

    一些Windows病毒Win32.Cabanas,Win95.Padania,Win32.Legacy...

    一些Windows病毒查杀工具(强烈推荐NODICE32)->www.eset.sk

    Neuromancer,by William Gibson,它是一本好书。

    毫无疑问这篇教程

   

    我希望没有忘掉任何重要的东西。

 

 

【简要介绍】

~~~~~~~~~~~

    好了,开始把你的大脑中的16MS-DOS编码概念,迷人的16位偏移地址,中断,驻留内存的方法...都清除掉。所有这些我们已经使用了很多年的东西,现在已经再也不用了。是的,它们确实现在用不到了。在这篇教程里面,当我说Win32,我的意思是Windows 95(normal,OSR1,OSR2)Windows 98Windows NTWindows 3.x+Win32s。最最明显的变化,至少在我看来是由中断变成了API,在这之前是由16位寄存器和偏移地址变到了32位的。Windows给我们开了使用其它语言代替ASM(C)的方便之门,但是我仍然对ASM情有独钟:利用它能更好的理解一些东西和更容易的优化(hi Super!)。正如我在上面所说的,你必须使用一种新东西叫做API。你必须知道这些参数必须在堆栈中,而且调用这些API是使用的CALL

    注:在上面我把上面所有提到的平台叫做Win32,我把Win95(它的所有版本)Win98叫做Win9x,把Windows 2000叫做Win2k。请注意这一点。

 

%16位到32位编程的改变%

~~~~~~~~~~~~~~~~~~~~~~~~

     我们现在将会使用双字(DWORD)而不是单字(WORD)了,而这个改变将会给我们一个全新的世界。在已知的CS,DS,ESSS:FS,GS之外,我们又多了两个段。而且我们有32位寄存器如EAX,EBX,ECX,EDX,ESI,EDI,EBPESP。让我们来看看对这些寄存器怎么操作:假如我们要使用EAXless significant word(简称LSW),我们该怎么做呢?这个部分可以使用AX来访问,即处理LSW。假如EAX=0000000,我们想要在它的LSW放置1234h。我们必须简单地使用一个"mov ax,1234h"就可以了。但是如果我们想要访问EAXMSW(Most Significant Word),该怎么做呢?为了达到这个目的我们不能使用一个寄存器了:我们必须使用ROL。问题并不是在这里,它是把MSW值移到了LSW

    当我们得到一个新语言的时候,我们总是要试的一个经典的例子:"Hello world!"  :)

 

%Win32中的Hello World%

~~~~~~~~~~~~~~~~~~~~~~

    它很简单,我们必须使用"MessageBoxA"这个API,所以我们用大家已经知道的"extrn"命令来定义它,把参数压栈然后调用这个API。注意这个字符串必须为ASCIIZ(ASCII,0),记住参数必须以相反的顺序压栈。

 

;-------从这里开始剪切----------------------------------------------------

 

                .386                            ; Processor (386+)

                .model flat                     ; Uses 32 bit registers

 

 extrn          ExitProcess:proc                ; The API it uses

 extrn          MessageBoxA:proc

 

;-

;利用"extrn"我们把在程序中要用到的所有API列出来。ExitProcess是我们用来把

;控制权交给操作系统的APIMessageBoxA用来显示一个经典的Windows消息框。

;-

 

 .data

 szMessage       db      "Hello World!",0       ; Message for MsgBox

 szTitle         db      "Win32 rocks!",0       ; Title of that MsgBox

 

;------------------------------------------------------------------------------------------------------------------------------

;这里我们不能把真正病毒的数据放这里了,因为这是一个例子,我们不能

;使用它,而且又因为如果我们不在这里放置一些数据,TASM将会拒绝汇编。

;无论如何...在第一次产生你的病毒主体的时候用它放置数据。

;-

 

                .code                           ; Here we go!

 

 HelloWorld:

                push    00000000h               ; Sytle of MessageBox

                push    offset szTitle          ; Title of MessageBox

                push    offset szMessage        ; The message itself

                push    00000000h               ; Handle of owner

 

                call    MessageBoxA             ; The API call itself

 

;------------------------------------------- ; int MessageBox(                                                       

;   HWND hWnd,          // handle of owner window                      

;   LPCTSTR lpText,     // address of text in message box              

;   LPCTSTR lpCaption,  // address of title of message box           

;   UINT uType          // style of message box                      

;  );                                                                 

;                                                                    

;在调用这个API之前,我们把参数压栈,如果你还记得,堆栈使用那个迷人的

;东西叫做LIFO(后进先出Last In First Out),所以我们要按相反的顺序来

;压参数。让我们看看这个函数的每个参数的简要描述:

;

; hWnd:标志将要被创建的消息框的宿主窗口(owner window)

;      如果这个参数是NULL,这个消息框没有宿主窗口。

; lpText:指向以空字符结尾的包含将要显示消息的字符串的指针。

; lpCaption:指向一个以空字符结尾的字符串的指针,这个字符串是这个

;        对话框的标题。如果这个参数是一个NULL,缺省的标题Error被使用。

; uType:以一些位标志来确定对话框的样式和行为。这个参数可为一些标志的组合。

;-

 

                push    00000000h

                call    ExitProcess

 

;--------------------------------------------------------------------------------------------------------------------

; VOID ExitProcess(

;   UINT uExitCode      // exit code for all threads

; );

; 这个函数在Win32环境下相当于著名的Int 20h,和Int 21h00,4C功能等等。

; 它是关闭当前进程的简单方式,即结束程序执行。下面给出唯一的一个参数:

;

; uExitCode:标志进程退出的代码,并作为所有线程终止时的代码。使用

; GetExitCodeProcess函数来刷新这个进程的退出值。使用GetExitCodeThread

; 函数来刷新一个线程的退出值。

;-

 

 end HelloWorld

 

;-----到这里为止剪切-------------------------------------------------------

 

    正如你看到的,编写代码很简单。可能没有16位环境下那么简单,但是如果你考虑到32位所带给我们的优点确实很简单了。现在,既然你已经知道怎么来编写"Hello World",你就有能力来感染整个世界了;)

 

%Rings%

~~~~~~~

    我知道你对下面的东西很害怕,但是,正如我所演示的,它看起来没有那么难。让我们记住你必须清楚的东西:处理器有4个特权级别:Ring-0,Ring-1,Ring-2Ring-3,越往后就有越多的限制,而病毒要是用第一个特权级别,几乎编码时没有任何限制。只要记住在迷人的DOS下面,我们总是处于Ring-0...现在想到在Win32平台下你还可以做相同的事情...好了,停止幻想了,让我们开始工作。

    Ring-3还表示"用户"级,在这个级别下,我们有很多的限制,那确实不能我们的需要。Microsoft程序员在他们发行Win95的时候犯了个错误,声称它是"无法感染"的,正如在这个操作系统卖出去之前所表明的,利用可怕的Bizatch(后来改名为Boza,但那是另外一段历史了)。他们认为这些API不能被一个病毒访问和使用,但是他们没有想到病毒编写者们的超级智慧,所以...我们可以在用户级下编写病毒,毫无疑问,你只要看看大量近期发布的新的Win32运行期病毒,它们都是Ring-3级下的...它们不差,不要误解了我,Ring-3病毒是现在有可能感染所有Win32环境下文件的病毒。它们是未来...主要是因为即将发布的Windows NT 5.0(或者Windows 2000)。我们不得不寻找能使我们的病毒(Bizatch生成的病毒传播很差,因为它对API地址"harcoded",并且它们可能会因Windows版本的改变而改变)存活的API,而且我们可以用其它不同的方法来实现,正如我后面解释到的。

    Ring-0是另外一段历史了,和Ring-3有着很大的区别。在这个级别下我们拥有内核编码的级别,"内核(kernel)级别"。是不是很迷人啊?我们可以访问端口,放置我们还没有梦想过的代码...和原先的汇编最接近的东西。我们不使用一些已知的花招是不能直接访问一些东西的,如IDT修改,SoPinKy/29A29A#3里发表的"Call Gate"技术,或者VMM插入,在Padania或者Fuck Harry病毒里见到的技术。当我们直接利用VxD的服务时,我们不需要API,而且它们的地址在所有Win9x系统中是被假设为相同的,所以我们"hardcode"它们。我将在fully dedicated to Ring-0这一章里面深入讨论。

 

%重要的东西%

~~~~~~~~~~~~

    我想无论如何我应该在这篇教程的开头放上这些,然而我知道知道总比不知道好啊:)好了,让我们来讨论Win32操作系统内部的东西。

    首先,你必须清楚一些概念。让我们从selector开始。什么是一个selector?相当简单,它是一个非常大的段,而且它组成了Win32的内存,也叫做平坦内存。我们可以用4G内存(4,294,967,295字节),仅仅通过使用32位地址。那所有这些内存是怎么组织的呢?看看下面的示意图:

 __________________________

|                          |<-------OFFSET=00000000h <-> 3FFFFFFFh

|    应用程序代码和数据    |

|__________________________|

|                          |<-------OFFSET=40000000h <-> 7FFFFFFFh

|         共享内存         |

|__________________________|

|                          |<-------OFFSET=80000000h <-> BFFFFFFFh

|           内核           |

|__________________________|

|                          |<-------OFFSET=C0000000h <-> FFFFFFFFh

|         设备驱动         |

|__________________________|

                            结果:我们拥有4G可用内存。是不是很迷人啊?

 

    注意一件事情:WinNT的后两段是分开的。现在我将给出你必须知道的一些定义,其它的一些本文之外的一些概念,我假设你已经知道了。

 

    VA:

 

    VA表示Virtual Address,即某些程序的地址,但是在内存中(记住在Windows中在内存中和在磁盘上是不一样的)

 

    RVA:

 

    RVA表示Relative Virtual Address。清楚这个概念很重要,RVA是文件在内存映射(由你或由系统)时的偏移地址。

 

    RAW Data:

 

    RAW Data是我们用来表示数据物理的存储的,也就是说,在磁盘(磁盘上的数据!=内存中的数据)上的存储。

 

    Virtual Data:

 

    Virtual Data是指那些已经被系统载入内存的数据。

 

    File Mapping:

 

    一种技术,在所有的Win32环境下都有,由一种快速的(并使用更少内存)文件操作方法和比DOS更容易理解的方法组成。所有我们在内存中改变的东西,在磁盘上也会改变。文件映射还是所有Win32环境(甚至NT)下内存之间交换信息的唯一方法。

 

%怎么来编译东西%

~~~~~~~~~~~~~~~~

    该死,我几乎忘记了这个:)编译一个Win32 ASM程序的通常参数是,至少在这篇教程的所有例子中,按如下(ASM文件的名字为'program',但是没有任何扩展名)

 

        tasm32 /m3 /ml program,,;

        tlink32 /Tpe /aa program,program,,import32.lib

        pewrsec program.exe

 

    我希望足够清晰了。你还可以使用makefiles或者建立一个bat文件来使它自动完成(就象我做的!)

 

 

PE文件头】

 ~~~~~~~~~~

    这是这篇文件的最重要的一章。仔细读!

 

%介绍%

~~~~~~

    PE头的结构很清晰在写我们的Windows病毒很重要。下面我将给出我认为重要的东西,但是并不是关于PE文件的所有的信息,想要知道更多的东西,看看我在上面关于PE文件推荐的资料,在"有用的东西..."这一章。

 _______________________________

|                               |<-----OFFSET=00000000h

|          DOS stub             |

|_______________________________|

|                               |<-----OFFSET=[DOS Stub+3Ch]

|          PE stuff             |

|_______________________________|

 

    让我们对这两大部分进行深入的分析让我们看看Micheal J. O'Leary的示意图

 

 __________________________________

|                                  |<----Base of Image Header

|    DOS compatible EXE header     |--|

|__________________________________|  |

|                                  |  |

|             Unused               |  |

|__________________________________|  |

|                                  |  |

|         OEM identifier           |  |

|__________________________________|  |

|                                  |  |

|            OEM info              |  |-->Uninteresting(DOS Compatibility)

|__________________________________|  |

|                                  |  |

|        Offset to PE Header       |----->Very interesting

|__________________________________|  |

|                                  |  |

| DOS Stub program and reloc table |  |

|__________________________________|  |

|                                  |  |

|              Unused              |__|

|__________________________________|

|                                  |

|   PE header(IMAGE_FILE_HEADER)   |--|

|__________________________________|  |

|                                  |  |

| PE header(IMAGE_OPTIONAL_HEADER) |  |

|__________________________________|  |-->Very very interesting :)

|                                  |  |

|          Section Table           |  |

|__________________________________|  |

|                                  |  |

|            Sections              |__|

|__________________________________|

 

    现在你已经对PE文件头已经有了一个大体的了解,确实很新奇(但也很复杂),我们的新目标。Ok,ok,你对那些东西有了一个"大体"的了解,但是,你仍然需要知道PE文件头中IMAGE_FILE_HEADER本身的内部结构。勒紧你的裤腰带!

 

IMAGE_FILE_HEADER

^^^^^^^^^^^^^^^^^

 ________________________________

|            "PE/0/0"            |<----+00000000h

|________________________________|       Size:1 DWORD

|            Machine             |<----+00000004h

|________________________________|       Size:1 WORD

|       Number Of Section        |<----+00000006h

|________________________________|       Size:1 WORD

|        Time Date Stamp         |<----+00000008h

|________________________________|       Size:1 DWORD

|    pointer To Symbol Table     |<----+0000000Ch

|________________________________|       Size:1 DWORD

|       Number Of Symbols        |<----+00000010h

|________________________________|       Size:1 DWORD

|    Size Of Optional Header     |<----+000000014h

|________________________________|       Size:1 WORD

|        Characteristics         |<----+000000016h

|________________________________|       Size:1 WORD

                                  Total Size:18h BYTES

    我将继续对IMAGE_FILE_HEADER的各个域给出简要的描述。

 

    PE/0/0:

 

    它是每个PE文件都有的标志只要在编写你的感染程序的时候检查它是否存在。如果它在那儿,它就不是一个PE文件,ok?

 

    Machine:

 

    因为我们所使用的计算机的理想可以是一个非PC兼容的(NT对这些东西有一个开放等级,你知道的),又因为PE文件是普遍的,在这个域中是这个应用程序所编写的代码的机器类型,可以为下面的值:

 

 IMAGE_FILE_MACHINE_I386    equ  14Ch    ; Intel 386.

 IMAGE_FILE_MACHINE_R3000   equ  162h    ; MIPS little-endian,160h big-endian

 IMAGE_FILE_MACHINE_R4000   equ  166h    ; MIPS little-endian

 IMAGE_FILE_MACHINE_R10000  equ  168h    ; MIPS little-endian

 IMAGE_FILE_MACHINE_ALPHA   equ  184h    ; Alpha_AXP

 IMAGE_FILE_MACHINE_POWERPC equ  1F0h    ; IBM PowerPC Little-Endian

 

    Number Of Sections:

 

    我们的感染程序的非常重要的域,它告诉我们这个文件的节(section)的个数。

 

    Time Date Stamp:

 

    保存了从1969.10.31 4:00到文件连结时所过的秒数。

 

    Pointer To Symbol Table:

 

    没意思,因为它仅仅被OBJ文件使用。

 

    Number Of Symbols:

 

    没意思,因为它仅仅被OBJ文件使用。

 

    Size Of Optional header:

 

    保存了IMAGE_OPTIONAL_HEADER域的字节数(看下面IMAGE_OPTIONAL_HEADER的描述)

 

    Characteristics:

 

    这些标志给了我们关于这个文件的更多信息对于我们所有人都没意思。

 

IMAGE_OPTIONAL_HEADER

^^^^^^^^^^^^^^^^^^^^^

 ________________________________

|             Magic              |<----+00000018h

|________________________________|        Size:1 WORD

|      Major Linker Version      |<----+0000001Ah

|________________________________|        Size:1 BYTE

|      Minor Linker Version      |<----+0000001Bh

|________________________________|        Size:1 BYTE

|          Size Of Code          |<----+0000001Ch

|________________________________|        Size:1 DWORD

|    Size Of Initialized Data    |<----+00000020h

|________________________________|        Size:1 DWORD

|   Size of UnInitialized Data   |<----+00000024h

|________________________________|        Size:1 DWORD

|     Address Of Entry Point     |<----+00000028h

|________________________________|        Size:1 DWORD

|         Base Of Code           |<----+0000002Ch

|________________________________|        Size:1 DWORD

|         Base Of Data           |<----+00000030h

|________________________________|        Size:1 DWORD

|          Image Base            |<----+00000034h

|________________________________|        Size:1 DWORD

|      Section ALignment         |<----+00000038h

|________________________________|        Size:1 DWORD

|        File Alignment          |<----+0000003Ch

|________________________________|        Size:1 DWORD

| Major Operating System Version |<----+00000040h

|________________________________|        Size:1 WORD

| Minor Operating System Version |<----+00000042h

|________________________________|        Size:1 WORD

|      Major Image Version       |<----+00000044h

|________________________________|        Size:1 WORD

|      Minor Image Version       |<----+00000046h

|________________________________|        Size:1 WORD

|    Major Subsystem Version     |<----+00000048h

|________________________________|        Size:1 WORD

|    Minor Subsystem Version     |<----+0000004Ah

|________________________________|        Size:1 WORD

|           Reserved1            |<----+0000004Ch

|________________________________|        Size:1 DWORD

|        Size Of Headers         |<----+00000050h

|________________________________|        Size:1 DWORD

|            CheckSum            |<----+00000054h

|________________________________|        Size:1 DWORD

|            SubSystem           |<----+00000058h

|________________________________|        Size:1 DWORD

|       Dll Characteristics      |<----+0000005Eh

|________________________________|        Size:1 WORD

|      Size Of Stack Reserve     |<----+00000060h

|________________________________|        Size:1 DWORD

|      Size Of Stack Commit      |<----+00000064h

|________________________________|        Size:1 DWORD

|      Size OF Heap Reserve      |<----+00000068h

|________________________________|        Size:1 DWORD

|      Size Of Heap Commit       |<----+0000006Ch

|________________________________|        Size:1 DWORD

|          Loader Flags          |<----+00000070h

|________________________________|        Size:1 DWORD

|    Number Of Rva And Sizes     |<----+00000074h

|________________________________|        Size:1 DWORD

                                  Total Size:78h BYTES

                      (加上IMAGE_FILE_HEADER ^^^^^^^^^)

 

   Magic:

 

   看起来总为010Bh,实际上会使我们认为它是一种签名,没有意思。

 

   Major Linker Version and Minor Linker Version:

 

   产生这个文件的连结器的版本,没有意思。

 

   Size of Code:

 

   它是所有包含可执行代码的段的总字节数。

 

   Size of Initialized Data:

 

   它是所有包含初始数据的段的总大小。

 

   Size of Uninitialized data

 

   未初始数据不占磁盘空间,但是当系统装载这个文件的时候,它会分配一些内存(实际上是虚拟内存)

 

   Address of EntryPoint:

 

   是装载器开始执行代码的地方。它是一个RVA,当系统装载这个文件的时候和image base相关。非常有意思。

 

   Base Of Code:

 

   是文件的code段开始的RVAcode段在内存中通常在data段之前PE文件头之后。这个RVA在用Microsoft连结器产生的EXE文件中通常为0x1000BorlandTLINK32看起来把image base加到了第一个code段的RVA处,并把结果存储在这个域中。

 

   Base Of Data:

 

   是文件的data段开始的RVAdata段通常在内存中处于最后PE文件头和code段之后。

 

   Image Base(基址):

 

   当连结器创造一个可执行文件的时候,它会假定将会内存映射到内存的某个地址当中。这个地址被保存在这个域中,假定的一个装载地址来允许连结器进行优化。如果这个文件确实被装载器内存映射到那个地址,在它可以运行之前代码就不需要任何补丁了。在为Windows NT产生的可执行文件中,缺省的Image Base0x10000。对DLL来说,缺省的为0x400000。在Win9X中,地址0x10000不能被用来装载EXE文件因为它在被所有进程的共享地址中。因为这个,Microsoft就把Win32的缺省Image Base改为0x400000。老的以基址0x10000进行连结而成的可执行文件在Win9x下装载将会花费更长的时间,因为装载器需要进行基址重定位。

 

   Section Alignment:

 

   当映射到内存中的时候,每一节要保证是这个值的一个倍数的虚拟地址作为开始地址。对于按页的时候,缺省的节对齐方式是0x1000(4096B)

 

   File Alingnment:

 

   PE文件中,构成每一节的原始数据要保证从这个值的倍数开始。缺省的值为0x200字节(512B),可能是为了保证各节总是以磁盘节(disk sector,它的长度也为0x200)的开始作为开始。这个域在NE文件中等价于segment/resource alignment。和NE文件不同的是,PE文件通常不会有成百个节,所以由于对齐文件的节而浪费的空间几乎很少。

 

   Major Operating System Version and Minor Operating System Version:

 

   使用这种类型的可执行文件的操作系统的最低版本号。既然subsystem fields目的看起来和它相类似,这个域有点摸棱两可。这个域在所有的Win32 EXE文件中缺省为1.0

 

   Major Image Version and Minor Image Version:

 

   是一个用户可定义的域,它允许你可以有不同版本的EXEDLL。你可以通过连结器的/VERSION开关来设置这个域。如"LINK /VERSION:2.0 muobj.obj"

 

   Major Subsystem Version and Minor Subsystem Version:

 

   包含了运行这个可执行文件所需要的最小子系统版本。这个域的一个经典值为3.10(意思为Windows NT 3.1)

 

   Reserved1:

 

   看起来总为0(最为感染标志太完美了)

 

   Size Of Headers:

 

   PE文件头的大小和节(对象)表。这些节的原始数据就从这些所有文件头组件之后开始。

 

   Checksum:

 

   为这个文件的CRC校验值。正如在其它的Microsoft可执行文件格式中,这个域是忽略的并总设为0,这个规则的例外是这些EXE文件必须有合法的校验值。

 

   SubSystem:

 

   这些可执行文件的子系统的类型被它用来用户界面。WINNT.h定义了下面的值:

 

 NATIVE         1       Doesn't require a subsystem (such as a device driver)

 WINDOWS_GUI    2       Runs in the Windows GUI subsystem

 WINDOWS_CUI    3       Runs in the Windows character subsystem (console app)

 OS2_CUI        5       Runs in the OS/2 character subsystem (OS/2 1.x  only)

 POSIX_CUI      7       Runs in the Posix character subsystem

 

     一个标志集表明了在什么环境下一个DLL的初始函数(DLLMain)将会调用。这个值看起来总是设置为0,然而操作系统仍然对所有四个事件调用DLL初始函数。下面是定义的值:

 

1     DLL第一次装载到一个进程的地址空间中时调用

2     当一个线程终止时调用

3     当一个线程开始时调用

4     DLL已经存在时调用

 

   Size Of Stack Reserve:

 

   为初始线程的堆栈而保留的虚拟内存数量,然而并不是所有的内存都可以做(看下一个域)。这个域的缺省值为0x100000(1MB)。如果你用CreateThread0作为堆栈的大小,那么创建出来的堆栈就会有相同的大小。

 

   Size Of Stack Commit:

 

   保证初始线程的堆栈时的内存数量。对于Microsoft的连结器这个域的初始值为0x1000字节(1)TLINK322页。

 

   Size Of Heap Reserve:

 

   用来保留给初始进程堆时的虚拟内存,这个堆的句柄可以通过调用GetProcessHeap函数来获得。并不能保证所有内存(看下一个域)

 

   Size Of Heap Commit:

 

   在进程堆中初始时的内存数量。缺省值为1页。

 

   Loader Flags:

 

   WINNT.h来看,这个域和调试支持相关。我还没有看到任何一个这些位都有效的可执行文件,也没有看到这些位都清空的。怎么用连结器设置它们呢,下面是定义的值:

1   在开始进程前唤醒一个断点指令

2  当进程已经载入后唤醒一个调试器

 

   Number Of Rva and Sizes:

 

   DataDirectory 数组(下面)的入口个数这个值用当前的工具总是设置为16

 

IMAGE_SECTION_HEADER

^^^^^^^^^^^^^^^^^^^^

 _____________________________

|       Section Name          |<-----Begin of section header

|_____________________________|        Size:8 BYTES

|       Virtual Size          |<-----+00000008h

|_____________________________|        Size:1 DWORD

|      Virtual Address        |<-----+0000000Ch

|_____________________________|        Size:1 DWORD

|      Size Of Raw Data       |<-----+00000010h

|_____________________________|        Size:1 DWORD

|    Pointer To Raw Data      |<-----+00000014h

|_____________________________|        Size:1 DWORD

|   Pointer To Relocations    |<-----+00000018h

|_____________________________|        Size:1 DWORD

|   Pointer To Line Numbers   |<-----+0000001Ch

|_____________________________|        Size:1 DWORD

|    Number Of Relocations    |<-----+00000020h

|_____________________________|        Size:1 WORD

|   Number Of Line Numbers    |<-----+00000022h

|_____________________________|        Size:1 WORD

|      Characteristics        |<-----+00000024h

|_____________________________|        Size:1 DWORD

                                Total Size: 28h BYTES

 

Section Name:

 

    命名节用的是一个8-byteANSI名字(UNICODE)大多数的节的名字以一个.(".text")作为开始但是这不是必须的你可以在一些关于PE的文章里验证这一点。你可以直接用汇编语言来命名你的节,或者在Microsoft C/C++编译器下用"#pragma data_seg""pragma code_seg"。注意节名是否占了满满8个字节很重要,没有NULL终止符。如果你是一个printf的热爱者,你可以使用%.8s来避免把名字字符串拷贝到另外一个你可以用NULL来终止的缓冲区里面。

你可能感兴趣的:(Windows核心程序开发)