我在世界的巅峰吧 关注:34贴子:13,309

回复:【考古】PE文件前考古和考古日记

只看楼主收藏回复

实验data segment
dw 2h
data ends
code segment
dw 123h
;start:
mov ax, 4c00h
int 21h
code ends
;end start
end
结论:
没有assume cs 没有start 不报错
有start 报错 no or unreachable cs


IP属地:广东16楼2022-05-06 19:39
回复
    考古,。
    求伯君《深入DOS编程》提到DOS不可重入。经研究其说法有误,真正的原因并非如书所示。
    搜到
    《DOS的不可重入性剖析及其解决办法》
    发现某图的服务又能用了,遂下载。


    IP属地:广东17楼2022-05-07 21:41
    回复
      粗略读完WS的HBYY


      IP属地:广东18楼2022-05-08 03:42
      回复
        来实战PE的装载!


        IP属地:广东20楼2022-05-10 21:15
        回复
          以windows xp下的画图程序为例。

          在optional header里,我们关注两个值(十六进制),
          Entry Point(EP),34BBF
          Image Base(映像基址),1000000(六个零)
          现在我们再来看section table:

          我查到:
          union {
          DWORD PhysicalAddress
          DWORD VirtualSize
          } Misc;
          This field has different meanings, in EXEs or OBJs. In an EXE, it holds the actual size of the code or data. This is the size before rounding up to the nearest file alignment multiple. The SizeOfRawData field (seems a bit of a misnomer) later on in the structure holds the rounded up value. The Borland linker reverses the meaning of these two fields and appears to be correct. For OBJ files, this field indicates the physical address of the section. The first section starts at address 0. To find the physical address in an OBJ file of the next section, add the SizeOfRawData value to the physical address of the current section.


          IP属地:广东21楼2022-05-10 21:32
          回复
            那既然这是一个EXE,我们现在先来分析一下这个表(我写在下面的,都已经按照小端调整过来了)

            typedef struct _IMAGE_SECTION_HEADER {
            BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
            union {
            DWORD PhysicalAddress;
            DWORD VirtualSize;
            } Misc; -------------->00 03 BC 64 第二部分
            DWORD VirtualAddress; 00 00 10 00 第三部分
            DWORD SizeOfRawData; 00 03 BE 00 第四部分
            DWORD PointerToRawData; 00 00 04 00 第五部分
            DWORD PointerToRelocations;
            DWORD PointerToLinenumbers;
            WORD NumberOfRelocations;
            WORD NumberOfLinenumbers;
            DWORD Characteristics;
            } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
            【显然在软件里作者显示的顺序与在文件里的排列是不是同一个顺序】
            第一个数组8字节,都是ACSII
            然后第二部分,是一个4字节的UNION。按照楼上的文章,这是一个EXE文件,它应该被解读为第二个结构体( VirtualSize),意思是在这段数据的【实际大小】
            In an EXE, it holds the actual size of the code or data。
            第三部分,重点,虽然英文是VR,实际上是【RVA】,意思是这一段代码,被映射到内存里以后,从楼上提到的Image Base点的偏移!这里RVA是1000
            With Microsoft tools, the first section defaults to an RVA of 0x1000.
            第四部分,重点,SizeOfRawData,意思是
            【实际大小】向上取整到【file alignment】的倍数。
            In EXEs, this field contains the size of the section after it's been rounded up to the file alignment size. For example, assume a file alignment size of 0x200. If the VirtualSize field from above says that the section is 0x35A bytes in length, this field will say that the section is 0x400 bytes long.
            豚酱评点:
            不知所谓!!!!!!!!!!!!!!!!!!!!!!!!
            按照人类的逻辑,raw data就是未经处理的数据,virtual size是虚拟的大小。
            本来按照英语,未经处理的数据是原大小,而virtual size才是真正被读进内存的大小,但是微软却偏要反过来!?
            我有空一定要去外网吐槽一下!
            以下数字按照十六进制运算:
            3BC64 / 200 = 1DE并且有余数
            (1DE+1)=1DF
            1DF*200=3BE00
            第五部分:
            这一段在【真正硬盘中文件中的起始地址大小】
            A file pointer to the first page within the COFF file. This value must be a multiple of the FileAlignment member of the IMAGE_OPTIONAL_HEADER structure. If a section contains only uninitialized data, set this member is zero.
            This is the file-based offset of where the raw data emitted by the compiler or assembler can be found. If your program memory maps a PE or COFF file itself (rather than letting the operating system load it), this field is more important than the VirtualAddress field. You'll have a completely linear file mapping in this situation, so you'll find the data for the sections at this offset, rather than at the RVA specified in the VirtualAddress field.


            IP属地:广东22楼2022-05-10 22:16
            回复
              现在我们开始来分析内存!!!!!!!!!!


              IP属地:广东23楼2022-05-10 22:33
              回复

                打开OD,别急。
                首先,左下角是内存,但它的默认地址是0103D000,这到底是啥玩意?没人知道,目前也不知道OD为什么会停在这里。
                以后我们再来研究,一步步来。
                在左下角,右键,输入01000000(6个零)。这是前面提到的Image Base,也就是EXE从硬盘读入内存的起始点~!比这个数字更小,会出错。


                IP属地:广东25楼2022-05-10 23:48
                回复

                  嘿,瞧,这不就是文件在内存中的样子嘛!一模一样!


                  IP属地:广东26楼2022-05-10 23:51
                  回复
                    好,现在我们来看代码段在内存中长啥样啦!
                    根据公式,第一个section(也就是.data)被载入内存的地址(我们成为VA):
                    Image Base + RVA = 1,000,000 + 1,000 = 1,001,000
                    (为了方便观看,每三位添加一个逗号,但是注意,这里都是十六进制!)
                    section在文件中的位置为PointerToRawData (400)
                    现在,我们来看看是不是同一回事:
                    Jesus Christ,根本就不同嘛!这些东西。什么乱七八糟的?!
                    然而我们再看看入口点,也就是程序第一条指令开始执行之处:
                    34BBF
                    注释
                    DWORD AddressOfEntryPoint
                    The address where the loader will begin execution. This is an RVA, and usually can usually be found in the .text section.
                    在内存中的实际地址(VA)1,000,000+ 34,bbf = 1,034,BBF.
                    在硬盘中的位置,根据这条公式算:

                    写到这里我发现这个RVA的概念是混乱不堪的。说明连软dog都在乱用词。(虽然图是我画的,但是我发现好像错了)
                    我文字版重新写一次:
                    硬盘某一点位置(更靠文件后部分)-某一段起点位置=内存中某一点位置(更靠后)-内存中起点位置(写作virtual address,读作起点rva)
                    大家的差值都是一样的。
                    所以这里
                    硬盘位置=EP-起点RVA+段落在硬盘中的起点
                    =34BBF-1000+400
                    =33FBF

                    哈,这下对应上了。
                    注意,OD中,程序开始执行的地址也是这个入口点!
                    那么剩下的问题就来了:
                    .text段EP前的代码为什么被改得面目全非了呢?
                    在RVA 1000到 34BBF之间,到底发生了什么?
                    现在还不知道,希望未来有一点会知道!


                    IP属地:广东27楼2022-05-11 00:06
                    回复
                      现在解开谜底,这个1000到34BBF之间,是各式各样的表

                      在.text段开头,先就是一个IAT(Import Address Table)
                      那么,这个IAT又是与什么关联的呢,它的结构是如何解读的呢,别急,我们一步步探索


                      IP属地:广东28楼2022-05-11 03:00
                      回复

                        简而言之,就是一个数组。
                        import directory table的地址(RVA)指向一个数组,这个数组里有很多个import directory entry元素。我们现在来分析一下!
                        首先看上面楼层的图,这个table 的RVA是3A8D0,我们跟踪!来到import table的第一个DLL。

                        一共20字节。我们一个一个来剖析!!!!!!
                        第一部分!import lookup table的指针,00 03 ac 38。转到这个DLL的import lookup table,发现只有ordinal number。
                        至于什么事ordinal number,这里我们先下个锚!
                        第二部分,第三部分,全是FFFFFFFF,我感觉已经废弃了,但我没有证据。
                        第四部分,这个DLL的名字的指针地址!
                        注意,我们这个表格现在还在.text部分!
                        点一下这个nameRVA追踪,可以找到这个dll的名字字符串在内存中的位置,这个部分也仍在.text部分!

                        我们再来验证一下前面的那个公式哈。
                        硬盘地址-节开头硬盘地址=RVA-节载入RVA
                        3AC60-400=3a860
                        3b860-1000=3A860
                        公式符合!yeah~~
                        duang的一下,证明他这个是真的。
                        跑题了哈。
                        第五部分,我们的鼎鼎大名的IAT,import address table终于出场了!RVA 00 00 12 78!
                        就是我们前面提到的那段读进内存后,被篡改的代码辣!
                        我们之后在研究这个IAT。
                        ==============
                        鉴于这个table 太多项了,我们换一条,再来分析一个,在IAT最开头的,却是在表内的第三项的

                        老规矩一条一条来,第一项00 03 A9 C0,点一下,来到import lookup table

                        然后下面的original thunk 点一下,就可以找到函数名字字符串了。不过,这个字符串前面有点怪怪的,不是名字开头。。。


                        IP属地:广东29楼2022-05-11 03:45
                        回复
                          那么。我们就可以知道这么一个规则了。
                          对于import directory table,
                          这个数组里有很多个import directory entry元素。每一条元素,带有一个RVA,指向这个元素的DLL;
                          带有一个RVA,指向一Import Lookup Table(ILT),
                          带有一个RVA,指向一个Import Address Table(IAT)
                          这个ILT和IAT的条数相等。
                          但是,他们在内存中的顺序是不固定的,可能你的ILT在前,我的IAT在前。但总归是一一对应。
                          ILT中 到底是cordinal还是name 微软文档有说。
                          https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-lookup-table


                          IP属地:广东30楼2022-05-11 04:07
                          回复
                            IAT中的值是loader根据INT查找出来的dll模块和名字/编号去找到的函数地址。
                            但是呢,IAT中是硬编码了一些东西,而这些东西在加载器加载后,会写入函数的真正的地址。那么问题来了,在文件中的原始东西是什么东西?
                            这个东西,查找IMAGE_BOUND_IMPORT_DESCRIPTOR,可以找到线索。
                            他就是跟IAT地址绑定有关。
                            说道这个东西,他就跟另一个表有关
                            A pointer to the first IMAGE_DATA_DIRECTORY structure in the data directory.
                            The index number of the desired directory entry. This parameter can be one of the following values.
                            IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
                            11
                            Bound import directory
                            而这个表——居然就放在PE头和DOS STUB之间!现在知道这些奇奇怪怪的东西哪来的了。


                            IP属地:广东31楼2022-05-11 06:10
                            回复



                              注意这里紧跟着的NTDLL就是import table里没有,是DLL的dll.
                              “NumberOfModuleForwarderRefs是指该dll自身依赖的dll的个数。值为n代表该结构后面紧跟了n个IMAGE_BOUND_FORWARDER_REF结构。”


                              IP属地:广东32楼2022-05-11 06:26
                              回复