飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

123
返回列表 发新帖
楼主: fonge

[活动] 活动之一PE自定义

[复制链接]

该用户从未签到

发表于 2008-8-28 00:12:37 | 显示全部楼层
十分感兴趣。。。
关注中!
PYG19周年生日快乐!

该用户从未签到

发表于 2008-9-25 10:24:20 | 显示全部楼层
强,如果做成一个有简入深的教程就好了.
PYG19周年生日快乐!
  • TA的每日心情
    擦汗
    昨天 08:03
  • 签到天数: 1360 天

    [LV.10]以坛为家III

    发表于 2008-9-25 13:17:00 | 显示全部楼层
    支持fonge,正在学习
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2008-10-3 16:30:16 | 显示全部楼层
    要跟贵宾学习``
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-7 09:23:57 | 显示全部楼层
    今天才看到,可惜啊!
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2010-5-7 18:39:26 | 显示全部楼层
    学习PE文件格式有一段时间了。下面是一个只包含一个节的exe文件,文件大小一共为1024字节。所写的东西没有太多新意,只能被初学的看官

    当作厕中佳品哈哈!~~

    ->DOS Header
       e_magic:         0x5A4D    //'MZ'
       e_cblp:          0x0000
       e_cp:            0x0000
       e_crlc:          0x0000
       e_cparhdr:       0x0000
       e_minalloc:      0x0000
       e_maxalloc:      0x0000
       e_ss:            0x0000
       e_sp:            0x0000
       e_csum:          0x0000
       e_ip:            0x0000
       e_cs:            0x0000
       e_lfarlc:        0x0000
       e_ovno:          0x0000
       e_res:           0x0000000000000000
       e_oemid:         0x0000
       e_oeminfo:       0x0000
       e_res2:          0x0000000000000000000000000000000000000000
       e_lfanew:        0x00000040

    ->Signature         0x00004550   //'PE'

    ->File Header
       Machine:                  0x014C  (I386)
       NumberOfSections:         0x0001  //只包含一个section
       TimeDateStamp:            0x00000000
       PointerToSymbolTable:     0x00000000
       NumberOfSymbols:          0x00000000
       SizeOfOptionalHeader:     0x00E0
       Characteristics:          0x010F  (为exe文件)

    ->Optional Header
       Magic:                         0x010B
       MajorLinkerVersion:            0x00
       MinorLinkerVersion:            0x00
       SizeOfCode:                    0x00000200
       SizeOfInitializedData:         0x00000200
       SizeOfUninitializedData:       0x00000000
       AddressOfEntryPoint:           0x00001000
       BaseOfCode:                    0x00001000
       BaseOfData:                    0x00000000(试验知这个值在xp下任意)
       ImageBase:                     0x00400000
       SectionAlignment:              0x00001000
       FileAlignment:                 0x00000200
       MajorOperatingSystemVersion:   0x0004
       MinorOperatingSystemVersion:   0x0000  -> 4.00
       MajorImageVersion:             0x0000
       MinorImageVersion:             0x0000
       MajorSubsystemVersion:         0x0004
       MinorSubsystemVersion:         0x0000  -> 4.00
       Win32VersionValue:             0x00000000
       SizeOfImage:                   0x00002000 //只含一个节,包括文件头部一共是2000h
       SizeOfHeaders:                 0x00000200 //从文件头到第一节的原始数据的偏移量
       CheckSum:                      0x00000000
       Subsystem:                     0x0002  (WINDOWS_GUI)
       DllCharacteristics:            0x0000
       SizeOfStackReserve:            0x00100000
       SizeOfStackCommit:             0x00001000
       SizeOfHeapReserve:             0x00100000
       SizeOfHeapCommit:              0x00001000
       LoaderFlags:                   0x00000000
       NumberOfRvaAndSizes:           0x0000000F

       DataDirectory (F)             RVA             Size
       -------------                 ----------      ----------
       ExportTable                   0x00000000 0x00000000
       ImportTable                   0x00001100 0x0000003C  //把导入表放在0X300处
       Resource                      0x00000000 0x00000000
       Exception                     0x00000000 0x00000000
       Security                      0x00000000 0x00000000
       Relocation                    0x00000000 0x00000000
       Debug                         0x00000000 0x00000000
       Copyright                     0x00000000 0x00000000
       GlobalPtr                     0x00000000 0x00000000
       TLSTable                      0x00000000 0x00000000
       LoadConfig                    0x00000000 0x00000000
       BoundImport                   0x00000000 0x00000000
       IAT                           0x00000000 0x00000000  
       DelayImport                   0x00000000 0x00000000
       COM                           0x00000000 0x00000000
       Reserved                      0x00000000 0x00000000

    (二)下面是节表了
    Name     VSize   VAddress    RSize    Roffset    characterstics
    .text    200     1000        200       200         60000020 (代码节属性)
    剩余字段置0x00


    (三)代码
    因为我们的代码只有25byte,所以可以将要显示的字符串放置在0x200 + 25 = 0x225再加一个字节0x00,干脆放到0x230
    0x230: 'xiep',其余字节用0x00填充

    接着是代码:
    PUSH       0               6A 00
    push       'xiep'          68 30104000    //指向'xiep'
    PUSH       'xiep'          68 30104000
    push       0               6A 00
    call       MessageBoxA     E8 07000000    //向后7个字节
    push       0               6A 00
    call       ExitProcess     E8 06000000    //向后6个字节
    jmp        MessageBoxA     FF25 F0104000  //跳到IMAGE_THUNK_DATA
    jmp        ExitProcess     FF25 F8104000  //将IMAGE_THUNK_DATA放在0x2F8处
    从0x200处开始将上面的Opcode按序输入,再以0x00填充到0x300

    (四)导入表
    导入表的原理:
    http://photo.store.qq.com/http_i ... 78830db3b4c355debe3

    如上图:0040100E是Call MessageBoxA,当CPU执行这条指令跳转到0040101A,0040101A再跳转到00402008处包含的地址,而这个地址在文件被

    加载之前并不是一个合法的地址,而是一个指向IMPORT_BY_NAME结构的RVA(假设以函数名导入),当文件被加载的时候,操作系统会依据

    IMPORT_BY_NAME将00402008处替换为MessageBoxA的地址。

    具体的过程如下:
    1)OS根据PE文件头找到IMAGE_DIRECTORY_ENTRY_IMPORT数据目录
    2)再通过IMAGE_DIRECTORY_ENTRY_IMPORT的成员变量找到IMAGE_IMPORT_DESCRIPTOR,该结构的name是一个指向DLL文件名的RVA
    3)IMAGE_IMPORT_DESCRIPTOR的FirstThunk指向00402008,而[00402008]是一个IMAGE_THUNK_DATA结构,假设它的最高位是0,即以名称导入,

    此时它的低四位就是一个指向IMPORT_BY_NAME结构的RVA
    4)IMPORT_BY_NAME包含需要导入的函数的名称以及导入序号
    5)OS根据IMPORT_BY_NAME包含的名称,以及IMAGE_IMPORT_DESCRIPTOR中name指向的DLL文件名,在user32dll中查找函数MessageBoxA的地址,

    然后将[00402008]替换成MessageBoxA的实际地址

    程序中引入了两个函数,而前面的ImportTable我们设为1100对应文件偏移0x300.因为IMAGE_IMPOR_DESCRIPTOR结构的大小为5*4=20字节,我们

    从user32.dll和kernel32.dll分别导入MessageBoxA和ExitProcess,所以加上一个以全0结束的IMAGE_IMPOR_DESCRIPTOR,一共是3*20=60字节(3

    个IMAGE_IMPOR_DESCRIPTOR结构),所以我们可以在0x300+60(十进制)=0x33C处放置DLL文件名和IMAGE_IMPORT_BY_NAME结构。

    我们先在0x33C处,
    Hint  0x00           33C--->113C--->0040113C  //函数的导入序号
    'MessageBoxA'        //IMPORT_BY_NAME
    填充一个字节 0x00
    'User32.dll'         34A--->114A--->0040114A  //DLL文件名
    填充一个字节 0x00
    接下来:
    Hint  0x00           355--->1155--->00401155  //函数的导入序号
    'ExitProcess'        //IMPORT_BY_NAME
    'Kelnel32.dll'       363--->1163--->00401163  //DLL文件名

    而上面我们在代码中是这样写的:
    jmp        MessageBoxA     FF25 F0104000  //跳到IMAGE_THUNK_DATA
    jmp        ExitProcess     FF25 F8104000  //将IMAGE_THUNK_DATA放在0x2F8处
    所以我们需要将IMAGE_THUNK_DATA放在004010F0即0x2F0和004010F8即0x2F8处。
    所以在0x2F0处:
    3C110000  (0x33C处是MessageBoxA的IMPORT_BY_NAME)
    00000000  (以全0结束)
    55110000  (0x355处是ExitProcess的IMPORT_BY_NAME)
    00000000  (以全0结束)

    只剩下IMAGE_IMPORT_DIRECTORY
    Name1                                FirstThunk
    4A110000   //0x34A处‘user32.dll’    F0100000  //0x2F0处的IMPORT_BY_NAME
    63110000   //0x363处‘kernel32.dll’  F8100000  //0x2F8处的IMPORT_BY_NAME
    其余字段可以置0,以0x00对齐到3ff,保存为.exe文件即可。

    如有错误之处,请不吝指出!
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2010-5-7 19:11:34 | 显示全部楼层
    标 题: 【成果6.1】软件保护壳技术专题 - 添加新节
    作 者: 玩命
    时 间: 2008-06-15,18:09:52
    链 接: http://bbs.pediy.com/showthread.php?t=66612

    壳要附加到软件本身,有很多方式进行。在这里使用最常用的一种方式,添加一个新节。这里我们先用文字的形式描述一下添加新 节的算法。然后给出一段代码并在注释中详细给出没条指令的解释。在这之前,首先给出一些名词的解释,以便刚接触的朋友可以熟悉。熟悉的朋友可以直接跳过。

    名词解释:
    Offset
    相对偏移
    常指在文件中到文件头的距离。

    RVA                     
    相当内存偏移               
    常指在内存中到内存加载起始地址的距离。

    VA                     
    虚拟地址                  
    常指在内存中确切的地址也就是RVA+内存加载起始地址。

    Alignment               
    对齐粒度                    
    当作"最小单位"就可以了例如:对齐粒度为200h。可以这样理解,一辆装货的车上面装的全是箱子,这箱子的大小是200h。如果最后一箱子没有装满,但是它 仍然占着一个箱子。

       
    添加新节相关的PE头属性:
    位于IMAGE_NT_HEADERS结构中的属性:
    ImageBase(4字节)
    SizeOfImage(4字节)
    NumberOfSections(2字节)
    AddressOfEntryPoint(4字节)
    SectionAlignment(4字节)
    FileAlignment(4字节)
      
    位于IMAGE_SECTION_HEADER结构的属性:
    最后节表VirtualSize(4字节)
    最后节表的VirtualAddress(4字节)
    最后节表的SizeOfRawData(4字节)
    最后节表的PointerToRawData(4字节)
    最后节表的Characteristics(4字节)
      
    添加新节算法描述:
    1.建立文件映射
    2.判断是否是PE文件
    3.移动到最后一个节表
    4.添加新节节表
    5.设置新节的VirtualAddress,VirtualSize,SizeOfRawData,PointerToRawData,Characteristics等属性
    6.将新节的内容写入文件
    7.增加NumberOfSections属性
    8.设置SizeOfImage,AddressOfEntryPoint属性
    9.将内存映射回文件
    注:代码中讲解的部分用红色标出
    首先是建立文件映射,也可以直接读写文件,不过这样做操作起来会方便一些。

    代码:
    CryptFile proc szFname : LPSTR   
         LOCAL hFile : HANDLE
         LOCAL hMap : HANDLE
         LOCAL pMem : LPVOID
         LOCAL dwOrigFileSize : DWORD
         LOCAL dwNTHeaderAddr : DWORD
         
         ;; init data
         xor eax, eax
         mov g_bError, al
         mov eax, offset EndNewSection - offset NewSection
         mov g_dwNewSectionSize, eax
         
         ;; open file
          invoke CreateFile, szFname,\
                              GENERIC_WRITE + GENERIC_READ,\
                              FILE_SHARE_WRITE + FILE_SHARE_READ,\
                              NULL,\
                              OPEN_EXISTING,\
                              FILE_ATTRIBUTE_NORMAL,\
                              0
         .IF eax == INVALID_HANDLE_VALUE
             jmp OpenFileFailed               
         .ENDIF
         mov hFile, eax
         invoke GetFileSize, hFile, NULL
        .IF eax == 0
            invoke CloseHandle, hFile  
            jmp GetFileSizeFailed
        .ENDIF
         mov dwOrigFileSize, eax   

         ;; 这里的dwOrigFileSize 是原始的文件大小
         ;; 因为你要添加新节,所以要多上那么一点
         ;; 点尺寸.这个尺寸就是APPEND_SIZE,如果
         ;; 这个尺寸也许会让你最后添加的程序后有
         ;; 一些多余的数据。也可以没有,不过有一
         ;; 点点麻烦。这就要计算添加后的
         ;; SizeOfImage了。等到以后讲解吧。
         add eax, APPEND_SIZE

         xchg eax, ecx
         ;; create memory map
         xor ebx, ebx     
         invoke CreateFileMapping, hFile, ebx, PAGE_READWRITE, ebx, ecx, ebx
         .IF eax == 0
             invoke CloseHandle, hFile
             jmp CreateMapFailed               
         .ENDIF
         mov hMap, eax
         ;; map file to memory
         invoke MapViewOfFile, hMap,
                               FILE_MAP_WRITE+FILE_MAP_READ+FILE_MAP_COPY,
                               ebx, ebx, ebx
         .IF eax == 0
             invoke CloseHandle, hMap
             invoke CloseHandle, hFile
             jmp MapFileFailed
         .ENDIF
         mov pMem, eax                              
         ;; check it's PE file or not ?
         xchg eax, esi
         assume esi : ptr IMAGE_DOS_HEADER
         .IF [esi].e_magic != 'ZM'
             invoke UnmapViewOfFile, pMem
             invoke CloseHandle, hMap
             invoke CloseHandle, hFile
             jmp InvalidPE        
         .ENDIF      
         add esi, [esi].e_lfanew
         assume esi : ptr IMAGE_NT_HEADERS   
         .IF word ptr [esi].Signature != 'EP'
             invoke UnmapViewOfFile, pMem
             invoke CloseHandle, hMap
             invoke CloseHandle, hFile
             jmp InvalidPE        
         .ENDIF
         mov dwNTHeaderAddr, esi

        ;; 在建立映射后
        ;; 这段代码将映射后文件的指针存放在局部变量pMem.
        ;; 而指定的NT结构头指针存放到esi寄存器处。
        ;; 现在我们调用添加节函数添加一个新节,AddSection是
        ;; 我们这节的主要函数,将在下面讲解。
        ;; 增加一个新节
        ;; pMem:文件映射内存指针
        ;; g_szNewSectionName:新节的节名,这里是(.new)
        ;; g_dwNewSectionSize:新节的长度,这里是offset EndNewSection - offset NewSection
        invoke AddSection, pMem, offset g_szNewSectionName, g_dwNewSectionSize
        push eax
        mov esi, dwNTHeaderAddr
        assume esi : ptr IMAGE_NT_HEADERS
        ;; 下面做的就是设置新节的中的原代码入口点,就是真正的入口地址.
        mov ebx, dword ptr [esi].OptionalHeader.AddressOfEntryPoint
        add ebx, dword ptr [esi].OptionalHeader.ImageBase
        ;; OrigAddressOfEntry这个变量在CryptFile底部的NewSection节中
        mov eax, offset OrigAddressOfEntry
        mov dword ptr [eax], ebx
        ;; 更新入口点,将新节的入口点设置到NT头结构的AddressOfEntryPoint
        ;; 哪个节的VirusAddress被设置到AddressOfEntryPoint处,哪个节将会被先执行
        ;; 这也是最通常的入口点技术
        pop eax
        assume eax : ptr IMAGE_SECTION_HEADER   
        push dword ptr [eax].VirtualAddress
        pop dword ptr [esi].OptionalHeader.AddressOfEntryPoint
        ;; 下面的代码利用新节节表将新节的代码写入文件
        mov esi, offset NewSection
        mov edi, dword ptr [eax].PointerToRawData
        add edi, pMem
        mov ecx, g_dwNewSectionSize
        cld
        rep movsb
    LogicShellExit:
         ;; close handle & write it
         invoke UnmapViewOfFile, pMem
         invoke CloseHandle, hMap
         invoke CloseHandle, hFile
         .IF g_bError == 0
             ;; show success message  
             invoke MessageBox, NULL, offset g_szDone, offset g_szDoneCap, MB_ICONINFORMATION
         .ENDIF        
         ret
    ;; ----- Show error message -----
    OpenFileFailed:
         lea eax, g_szOpenFileFailed
         jmp ShowErr
    GetFileSizeFailed:
         lea eax, g_szGetFileSizeFailed
         jmp ShowErr   
    CreateMapFailed:
         lea eax, g_szCreateMapFailed
         jmp ShowErr
    MapFileFailed:
         lea eax, g_szMapFileFailed
         jmp ShowErr        
    InvalidPE:         
         lea eax, g_szInvalidPE
         jmp ShowErr   
    ShowErr:
         invoke MessageBox, NULL, eax, offset g_szErr, MB_ICONERROR
         mov al, 1
         mov g_bError, al
         jmp LogicShellExit
    ;; ----- 新节代码 -----
    NewSection:
       ;; 在这里获取地址
       ;; call指令会将下条指令的地址压入堆栈
       ;; 注意此指令的OPCODE为EB00000000
       ;; 病毒与Shellcode等常用此指令定位
       ;; 杀毒软件的启发式搜索常将此特征作为查找特征
       ;; 聪明的读者可以自己修改定位代码来躲过
       ;; 这类的查杀
       call GetEip
       GetEip:
       ;; eax中有保存着当前的地址,标号为GetEip
       pop eax
       add eax, offset OrigAddressOfEntry - offset GetEip
       ;; 两个偏移的差就是这两个地址之间的距离,它的距离 + 起始地址
       ;; 就为OrigAddressOfEntry的地址
       ;; 最后将OrigAddressOfEntry保存的值,也就是原来的入口节的地址
       ;; 送回eax中。
       mov eax, dword ptr [eax]
       ;; 跳到原入口点地址
       jmp eax
    ;; ----- 新节数据 -----
    OrigAddressOfEntry  dd ?

    EndNewSection:

    CryptFile endp
    上面的新节代码没有任何作用,只是直接跳入到原入口的地址。
    下面的代码就是这次主题的主要的代码.

    代码:
    AddSection proc uses ebx ecx edx esi edi, pMem : LPVOID,
                                              pSectionName : LPVOID,
                                              dwSectionSize : DWORD
    ;; add a new section
    ;; ret: eax =  new section table file offset
        LOCAL dwNTHeader : LPVOID
        LOCAL dwLastSecTbl : LPVOID   
        LOCAL dwFileAlig : DWORD
        LOCAL dwSecAlig : DWORD
       
        ;; move to section table
        mov esi, pMem
        ;; 将映射地址传给esi,再将esi设置为PE头的地址
        ;; 这里这个3ch是DOS头中e_lfanew的偏移。e_lfanew保存着
        ;; 从MZ头到PE头的偏移,所以这样的相加使esi指向PE头
        ;; 也可以使用下面同等指令替换
        ;; assume esi : ptr IMAGE_DOS_HEADER
        ;; add esi, dword ptr [esi].e_lfanew
        add esi, dword ptr [esi+3ch]
        mov dwNTHeader, esi  
        assume esi : ptr IMAGE_NT_HEADERS
        ;; update the number of section
        mov cx, word ptr [esi].FileHeader.NumberOfSections
        movzx ecx, cx
        ;; 增加节的数目
        inc word ptr [esi].FileHeader.NumberOfSections
        push dword ptr [esi].OptionalHeader.FileAlignment
        pop dwFileAlig
        push dword ptr [esi].OptionalHeader.SectionAlignment
        pop dwSecAlig        
        ;; move esi point to section table
        ;; 在NT头结构下面跟着的就是N个节表街头.加上NT头结构的长度就为第一个节表
        add esi, sizeof IMAGE_NT_HEADERS
        ;; store the last section table
        ;; 在这里保存最后一个节表的偏移,因为一会在计算新节的RVA和offset要应用。
        mov eax, sizeof IMAGE_SECTION_HEADER
        mov ebx, ecx
        imul ebx
        ;; esi 为新节节表的偏移
        ;; 到这里可以判断下SizeOfHeader大小以便检测是否有空间加入新节,这里没有做判断
        ;; 一般没有加过壳的程序都会有空间加入,这个也是按照File Alignment对齐的所以就会有
        ;; 剩余的空隙加入
        add esi, eax                            ; esi = the end of orig last section fva
        push esi
        ;; 这里保存了原最后节表的偏移,为了设置新节的地址做准备
        sub esi, sizeof IMAGE_SECTION_HEADER    ; esi = the orig last section fva
        mov dwLastSecTbl, esi
        pop esi
        ;; set new section table
        ;; 设置新节节表名
        assume esi : ptr IMAGE_SECTION_HEADER
        ;; set section name
        push esi
        lea edi, [esi].Name1
        mov esi, pSectionName
    CopySectionNameLoop:   
        lodsb
        test al, al
        jz EndCopySectionNameLoop
        stosb
        jmp CopySectionNameLoop
    EndCopySectionNameLoop:  
        pop esi  
        ;; set section characteristics
        ;; 设置设置节的属性,一般关心的有三个属性,读写执行
        ;; 0E00000E0h为设置可读可写可执行三个属性的或运算的值
        ;; 有些杀毒软件的启发式搜索也会检查入口点节是否存在写权限
        ;; 来判断是否被病毒感染,有些多态病毒需要自解密自身所有需要
        ;; 写权限,在壳被恶意程序利用后,现在的杀毒软件有可能报告为
        ;; Heru.XXX或者XCrypt之类,都是启发式惹的祸,过此类检测也是有
        ;; 方法的,例如启动后将的代码写到栈中执行.现在的病毒做的越来越像壳
        ;; 壳整的越来越像病毒,两者只有目的上的区别了。
        push 0E00000E0h
        pop dword ptr [esi].Characteristics
        ;; set section virtualsize
        ;; 这里设置节的真实大小,VirtualSize表示新节的真实大小,没有经过对齐后的
        push dwSectionSize
        pop dword ptr [esi].Misc.VirtualSize
        ;; set section sizeofrawdata
        ;; 节的SizeOfRawData为此节在文件中经过文件对齐后的大小
        ;; PEAlign函数用于计算节对齐后的大小,将在附件的代码中给出
        invoke PEAlign, dwSectionSize, dwFileAlig
        mov dword ptr [esi].SizeOfRawData, eax
        ;; set section virtualaddress
        ;; 设置新节的内存偏移和文件偏移需要上一节的一个信息
        ;; 新节的内存偏移 = 上一节的内存偏移 + 上一节经过节对齐后的长度
        ;; 新节的文件偏移 = 上一节的文件偏移 + 上一节进过文件对齐后的长度
        mov eax, dwLastSecTbl                       ; eax = orig last section table fva
        assume eax : ptr IMAGE_SECTION_HEADER
        mov ecx, dword ptr [eax].VirtualAddress
        add ecx, dword ptr [eax].Misc.VirtualSize        ; ecx = new section rva
        mov ebx, dword ptr [eax].PointerToRawData
        add ebx, dword ptr [eax].SizeOfRawData           ; ebx = new section fva
        invoke PEAlign, ecx, dwSecAlig
        mov dword ptr [esi].VirtualAddress, eax
        ;; set section pointertorawdata
        invoke PEAlign, ebx, dwFileAlig
        mov dword ptr [esi].PointerToRawData, eax
        ;; update the sizeofimage
        ;; SizeofImage表示从文件到内存映射文件的内容通过节对齐的大小
        ;; 这个值等于当前最后一节的内存偏移 + 当前最后一节的经过节对齐的大小
        ;; 大家可以思考一下。这个值很有用,可以利用此值做一些特殊的感染来躲过启发式
        ;; 搜索。呵呵...
        mov eax, dword ptr [esi].VirtualAddress
        add eax, dword ptr [esi].Misc.VirtualSize
        invoke PEAlign, eax, dwSecAlig
        mov edx, dwNTHeader
        assume edx : ptr IMAGE_NT_HEADERS
        mov dword ptr [edx].OptionalHeader.SizeOfImage, eax
        push dword ptr [esi].PointerToRawData
        pop edi
        add edi, pMem
        ;; clear the new sec
        ;; 在这里做一下清0工作ZeroMemory
        mov ecx, dwSectionSize
        xor eax, eax
        cld
        rep stosb
       
        ;; 此函数的返回值,新节节表的文件偏移
        mov eax, esi
        assume esi : nothing
        assume eax : nothing
        assume edx : nothing
        ret
    AddSection endp
    添加新节为最基本的感染方式,感染这个词用在这里虽然有些不太恰当,但是我实在想不到用什么更形象词来描述此等行为。感染算法在病毒领域有很多的讨论,感染技术很多配合入口点技术来一起讨论。如果真要详细讨论此技术怕是要在开一个专题讨论才够。
    在附件中的代码由于要修改代码节自身,所以添加新节的程序在连接时要用到
    /SECTION:.text,ERW的选项.如果在RadAsm中配置连接选项时要改为/SECTION:.text|ERW才可以正常连接。

    附件程序的使用为在AddSection同目录下放置一名为target.exe程序直接运行AddSection后即可.可使用ollydbg调试target.exe查看.

    平常很少发帖,在专题文章的质量和可读性上希望大家多多提要求。。。
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2010-5-7 19:40:02 | 显示全部楼层
    标 题: 浅谈程序脱壳后的优化
    作 者: CCDebuger
    时 间: 2006-07-03 22:52
    链 接: http://bbs.pediy.com/showthread.php?threadid=28402

    浅谈程序脱壳后的优化

    作者:CCDebuger

    写这篇文章的目的是想让大家了解如何利用现有的工具来优化脱壳后的程序。 因为要让脱壳优化过的程序可以用汉化工具正常汉化的话,要求要稍微高一些,我就基于优化后的文件可正常用汉化工具汉化这样的目标来讲解。这篇文章主要是为新手服务的,所以肯定比较罗唆,高手可以略过。
    这篇文章中我采用 dwing 的 WinUpack 0.39 final 讲述。采用 WinUpack 来讲解的原因主要是这款壳把 PE 头搞得很让人郁闷,修复其它脱壳后的程序不需要修复 PE 文件头,而修复 WinUpack 却要考虑修复 PE 头的问题,而且这个壳加壳后把原程序的各个区段都合并了,修复的步骤要多一些,这样也方便大家了解的更详细一点。我准备分两部分来讲述,第一部分我采用 WinUpack 0.39 final 来给我自己用 MASM 写的一个示例程序加壳,然后来进行脱壳优化,第二部分直接讲解 WinUpack 0.39 final 中的那个中文版 WinUpackC.exe 的脱壳优化。其实本来直接写 WinUpackC.exe 的脱壳优化就可以了,不过我开始的时候没准备写 WinUpack 主程序的脱壳,写到后来才发现用自己写的示例程序加壳后再谈脱壳后的优化,有点自说自话的感觉,所以临时决定加一个 WinUpack 主程序的脱壳优化。因为文章已经写了一部分了,不想再重新抓图,就搞出两部分了(看见砖头、鸡蛋扔来,我躲!)。

    本节实例:test app.rar

    一、示例程序的脱壳优化  

    1、脱壳

    这里的目标程序是我用 MASM 写的一个对话框的简单例子,我采用 WinUpack 的默认选项把原程序 test.exe 加壳,加壳后的程序名为 test1.exe,大小由原来的 6.5K 变为 4.4K。因为 WinUpack 给程序加壳时修改了 PE 头的缘故,普通 OD 可能加载不了用 winUpack 加壳后的程序,所以我们换用看雪兄修改的 OllyICE 载入加壳后的 test1.exe,会出现一个“32 位可执行文件格式错误或未知”的错误对话框,不用管,点确定,又出现一个“无法在内存中分配 XXXXX 字节”的错误对话框,继续点确定,我们停在这里:

    00401018 > BE B0114000 MOV ESI,test1.004011B0
    0040101D AD LODS DWORD PTR DS:[ESI]                     ; ESI地址处的值就是OEP
    0040101E 50 PUSH EAX

    WinUpack 的壳比较好脱,F8 到上面的第二条指令时,ESI 所显示的值就是存放 OEP 的地址。我这里在信息窗口中看到的是以下内容:

    DS:[ESI]=[004011B0]=00401000

    现在我在反汇编窗口中按 CTR+G 直接来到地址 00401000 处,按 F4,就停在 OEP 处了:

    00401000 6A 00 PUSH 0
    00401002 E8 79010000 CALL test1.00401180                ; JMP 到 kernel32.GetModuleHandleA
    00401007 A3 1C304000 MOV DWORD PTR DS:[40301C],EAX

    现在我们用 LordPE 完全转存 test1.exe 的进程,另存为 dumped.exe,现在可以看到这个 dumped.exe 大小有 64K 了。不要关 OD,打开 ImportREC,选择进程 test1.exe,OEP 填入 1000,选“自动查找 IAT”,会有一个“发现一些信息”的对话框,点确定,再点“获取输入表”,现在我们就得到了完整的输入表了。我们先来保存一下树文件,另存为 test_tree.txt。不要关 OD 和 ImportREC,让它们都暂时开在那,先复制一份我们刚才用 LordPE 完全转存出来的 dumped.exe 备份。

    2、根据对齐值分析区段中的内容

    在开始之前,我们先了解一下对齐的一些概念:文件区块有两种对齐值,一种是磁盘文件中的,另一种是内存中的。PE文件被映射到内存时,区块总是至少以一个页边界为开始,在x86系统中,每个内存页的大小是4K,也就是0x1000字节,所以在x86系统中,PE文件区块的内存对齐值一般等于0x1000,每个区块按0x1000之倍数内存偏移位置开始。另一种是磁盘对齐值,这个实例磁盘对齐值是0x1000,每个区块按0x1000之倍数的文件偏移位置开始。有时为了使磁盘文件更小些,你可以用0x200对齐值。
    有了上面的预备知识,我们现在用 LordPE 的 PE 编辑器打开 dumped.exe 来看看:



    现在我们点击一下区段按钮,看看各个区段中有什么内容:



    因为上面我们已经看到文件块对齐是 1000H,我们就按 1000H 大小的倍数来在16进制编辑器中选择块,分析数据中是否有区段。先在16进制编辑器中查看一下第一个区段:



    上图的偏移 2000 是第二个区段开始的地方。有人可能要问你怎么知道这是原来的第二个区段开始处?呵呵,因为我前面看到文件对齐粒度为 1000,所以我在16进制编辑器中主要注意与 1000 的倍数对应的偏移地址。当我翻到偏移地址为 2000 时,在这个地址的上面是由一大片 0 填充的,到这个地址后又开始有数据了,所以我确定偏移 2000 处应该是另一个区段的开始处。如果你在16进制编辑器中看到一大片 0 后突然看见有数据,你再根据文件对齐粒度注意一下数据的开始地址,一般就可以确定是否已到另一个区段了。由16进制窗口中查看到的信息,我们可以确定 WinUpack 把我们原来的区段都合并了,现在我们要把它们分离出来。

    3、分离区段

    要说明一下,分离区段和下面的一节修正 PE 头在其他脱壳文件的优化中并不是必须的,这里主要针对 WinUpack 的壳。现在关掉 LordPE,用 WinHEX 打开 dumped.exe,ALT+G,填入偏移 1000,转到相应位置,在偏移 1000 处点右键,选择定义选块,输入相应数据后点确定:



    在选好的选块上右键选择复制选块->进入新文件,另存为 text.bin:



    其实这个偏移 1000H,长度 1000H 的段不保存也无所谓,这里主要是让大家熟悉一下保存的方法。同样,我们把起始偏移为 3000H,大小为 1000H 的那个段也另存为 data.bin。有人可能要问了,你为什么不把起始偏移为 2000H,大小为 1000H 的那个区段也保存下来?呵呵,因为我知道这里是原来的放输入表信息的那个段,现在已经被破坏了,我就不要了。同样,偏移 4000H 以后的段我也不保存了,那里是资源段,因为在修正过程中我可能要用另外的 RVA 地址来重建资源,所以不保存了。现在我们在 WinHEX 中再定义一个选块,从偏移 2000H 直到文件尾,选中后删除这个选块。

    4.修正PE头

    现在我们还要做一件事,因为 WinUpack 把 PE 头搞得太乱,我们要找个正常的替换一下。这里我选择 XP_SP2 下的记事本,用 WinHEX 打开记事本,从文件开始偏移为 0 的地方选择一个大小为 1000H 的选块,右键选择复制选块->以十六进制数值方式,现在转到我们正在 WinHEX 中编辑的 dumped.exe,定位到文件开始偏移处,右键选择剪贴板数据->写入(从当前位置覆写),把记事本的文件头粘贴过来。



    完成上述工作后保存 dumped.exe,现在文件大小变成 8K 了,用 PETools 的 PE 编辑器来打开这个文件,进行一些修改。先点击文件头按钮:



    把区段数由 3 改成 1 后确定,再点击可选头按钮:



    改一下上面的镜像基址为 00400000,点击确定。这里可以参考原来备份的 dumped.exe 文件来修改(我前面已经说过要备份原来的那个 dumped.exe 文件用作参考,你不会没备份吧?)。

    5、修正及添加区段

    现在点击区段按钮,修改一下区段的一些属性。因为前面我们已经把区段改为 1 了,现在打开区段后,只看到一个 .text 区段。在这个区段上右击,选择编辑区段头:



    因为我们现在的 .text 段是从偏移 1000H 开始的,大小为 1000H,所以我们要把上面的虚拟偏移和 RAW 偏移都改成 1000,虚拟大小和 RAW 大小也要改成 1000。在弹出的对话框上进行设置:



    完成上述修改后点确定,回到区段编辑器中,现在我们要添加一个大小为 1000H 的段,用来存放输入表。这里可能大家也有疑问:你是知道原来的输入表段大小为 1000H,如果你不知道呢?你知道要添加多大的一个区段?其实我这里添加区段的大小是根据 ImportREC 的新建输入表信息中的大小来的:



    我们可以在 ImportREC 中看到新建输入表的大小是 18CH,根据区段的对齐粒度 1000H,我这里就选大小为 1000H,已经够用了。这里要留一点余量,有时脱一些壳修复时,余量留的过小则修复的输入表不完全,这时可以从文件中删除这个区段,再按 1000H 的对齐粒度新建一个大一点的区段,重新修复输入表。现在继续我们前面的话题,在区段编辑器中右键点击,选择添加区段菜单项,在弹出的对话框上进行设置:



    上面我打了红框的是要修改的地方。PETools 默认添加区段的名称是 .NewSH,为了便于识别这个段是用于存放输入表信息的,我们把名称选为 idata。其他的 RAW 数据及虚拟数据大小我们都填 1000,选择用 0x00 填充区段,现在点确定,一个新区段就添加进来了:



    从上图中我们可以看出 .idata 段的虚拟偏移是 13000,这肯定不正确,我们应该保证各个段的虚拟地址都是连续的。以上图为例,.text 段的虚拟偏移是 1000H,虚拟大小是 1000H,这样虚拟偏移 + 虚拟大小 = 1000H + 1000H = 2000H,可知下一个区段的虚拟偏移应该是 2000H。同样道理 RAW 偏移也应该是连续的,虽然有时候我们看到有类似这样的:第一个区段:RAW 偏移:400H,RAW 大小:1D0;第二个区段:RAW 偏移:600H,RAW 大小:1B8,这样看起来好像并不连续啊。因为 400 + 1D0 = 5D0,下一个区段应该从 5D0 开始才对。但如果你用16进制工具打开文件看一下就知道了。这种情况是文件粒度按 200H 对齐,区段中的实际数据没有 200,这里显示的只是实际的数据大小,剩下的按 200H 对齐的部分用 0x00 填充了。所以下一个区段还是按文件对齐粒度设置偏移的。同样,文件对齐粒度是其他数值时也存在这种情况。说了这么多,大家应该也明白了,编辑一下 .idata 的区段头,把虚拟偏移 13000 改成 2000。至于后面那个特征值为什么可以改成 C0000040,我就不多说了,在编辑区段的时候点一下特征值后面那个 ... 按钮,上面应该可以看到详细的说明。

    6、修正输入表

    现在我们的前期工作暂告一个段落,保存好我们的修改退出 PETools,现在轮到 ImportREC 上场了。你的 ImportREC 应该没关吧?我们把添加一个新的节前面的勾去掉,在新建输入表信息中 RVA 填 00002000(就是我们新建的那个 ,idata 段的偏移地址),点击修复转存文件,选择我们刚才修改的 dumped.exe 进行修复,得到 dumped_.exe。

    7、修正资源

    到这还有两个重要的事别忘了,一个是我们保存的那个数据段(data.bin)要放进程序里来,还有个就是资源还没有。现在我们先把资源弄出来。拿出 dREAMtHEATER 兄的 FixRes,选择我们原来备份的那个 dumped.exe,使用 FixRes 的 Dump 功能,把资源段按我们定义的 RVA Dump 出来:



    因为前面还有一个数据段 RVA 是 3000,大小是 1000,所以我们把新建资源段的 RVA 设为 4000。按上面那样设置好后我们就可以点击 Dump Resource 按钮来 Dump 资源了,文件被保存为 rsrc.bin。

    8、装配文件

    现在进入最后的装配工作了,用 LordPE 的 PE 编辑器打开 dumped_.exe,点击区段按钮,进入区段编辑功能中:



    现在我们要把磁盘上的 data.bin 和 rsrc.bin 都添加到程序中来,右键选择从磁盘载入段,按顺序添加 data.bin 和 rsrc.bin,添加好后改一下区段名和标志,最终效果如下:



    现在退出区段编辑,点目录按钮,修正一下资源及其他的目录。在这个程序中输入表目录已经不需要我们再改了,我们要改的就是资源目录的 RVA 及大小,把其他没用到的目录 RVA 和 大小清零。我们再用一个 LordPE 的 PE 编辑器打开备份的 dumped.exe 文件来做参考,最终修正效果如下:



    9、修正可选头及最终优化

    保存以上工作后关掉 LordPE,我们可以看到 dumped_.exe 的图标已经出来了,说明资源已经修复。现在再用 PETools 的 PE 编辑器打开 dumped_.exe(这里换 PETools 的原因是因为 PETools 编辑 PE 头的功能比较强),点击可选头按钮,进入可选头编辑器。现在主要要修改的就是代码基址和数据基址,代码基址一般就是第一个区段(我们这里是 .text 段)的 RVA,所以这里应该填 1000,数据基址一般指除了代码外的部分开始的 RVA,我们这里代码部分 RVA 是 1000,大小是 1000,加起来就知道除了代码外的数据 RVA 是 2000,就是我们第二个段 .idata 的 RVA。修改完这些内容后最后要纠正一下镜像大小,否则程序还是不能运行。最终修改效果如下:



    上图中的代码大小和已初始化数据大小改不改都无所谓,我是为了好看点把它改了。一般的代码大小就是指 .text 段的大小,.text 段后面所有段的大小加起来就可以作为已初始化数据大小。全部改完后点镜像大小后面的那个“?”按钮,纠正一下镜像大小,现在就可以保存退出 PETools 了。
    到这基本工作已经完成了,修复后的 dumped_.exe 大小为 20K,运行一下,一切正常。不过这里的 20K 大小还和我们原来的 6.5K 有差距,如果手工修改的话我们可以先把文件对齐的粒度设为 200,再用16进制工具打开程序,把按照 200H 倍数对齐的各个区段的多余的全是 0 的部分删掉,再根据保留下来的部分调整一下区段的 RAW 偏移和大小。当然你可以用 PETools 或 LordPE 的重建功能来重建一下程序,也会完成上述功能。不过我们这里是希望能用汉化工具正常汉化的,所以我们不能用 PETools 或 LordPE 的重建功能来重建程序, 因为它们重建的程序虽然可以正常使用,也比较小,但若用来汉化是很容易出错的。这里还是不要手工来调整了,我们直接用一下 PE Optimizer 这个工具来优化程序一下,这个工具优化出来的程序基本上和手工修改的差不多。优化后我们再看一下大小:20K->6.5K,呵呵,和我们原来的程序一样大。

    二、 WinUpack 主程序的脱壳及优化

    如果文章写到这里收工的话,估计会有人说你自己编个程序,再加个壳来谈脱壳后的优化,你完全可以对照原程序来进行啊。OK,那我们就来个没有对照的,冒着被 dwing 狂扁的危险,我就拿 WinUpack 0.39 final 中的那个中文版 WinUpackC.exe 来开刀。不过 dwing 要来了,大家要掩护我逃跑啊,呵呵。
    WinUpackC.exe 脱壳我就不多说了,OEP 是 0040A4BE,直接在 OD 中 CTR+G 转到地址 0040A4BE,F4 运行到这,就可以用 LordPE 完全转存了。我们还是把转存后的文件保存为 dumped.exe。现在不要关 OD,在 ImportREC 中选择 WinUpackC.exe 的进程,输入 OEP:A4BE,选自动查找 IAT,可以得到正确的输入表,大小是 00000B18。保存一下树文件备用。让 OD 和 ImportREC 都开在那,现在我们用 LordPE 的16进制编辑区段功能来观察一下第一个区段中的内容。具体怎么分析原来区段的起始地址我前面已经说过了,此处只谈结果。经分析可知偏移 1000-AFFF应该是代码段,大小为 A000;B000-DFFF 应该是数据段,E000-FFFF 应该是另一个段;功能我不是很清楚 ,可能原来也用于存放输入表信息的。我就把它和前面的 B000-DFFF 一起当成数据段,这样数据段就大小就是10000 - B000 = 5000;10000-11FFF 应该是资源段;12000-12FFF 包含了部分输入表的信息,应该是加壳后搞出来的。不过这个段对我们毫无作用,不作考虑。现在对我们有用的就是偏移 1000-FFFF 的部分,这里有两个区段。根据 ImportREC 中所显示的输入表大小 00000B18 及前面两个段用到的偏移,我们只要在偏移 10000 处添加一个大小为 1000 的段用来存放输入表信息就可以了。因此资源段我们应该让它从 11000 开始。分析完了就可以开工了,先把 dumped.exe 复制一份留作参考,WinHEX 上场,ALT+G 转到偏移 10000 处,从此处开始选择一个直到文件结尾的块,删除。我们还是借用一下 XP_SP2 记事本的文件头,把记事本偏移 0-FFF,大小为 1000 的内容复制过来,覆盖到 dumped.exe 的对应位置。全部完成后保存 dumped.exe。现在由 PETools 上场,用其 PE 编辑器打开 dumped.exe,先把区段数改为 2,再修正一下镜像基址为 00400000,然后转到区段编辑器,根据我们上面分析的两个区段的偏移及大小调整区段的虚拟偏移、大小;RAW 偏移、大小。完成后再新建一个区段用来保存输入表信息,偏移是 10000,大小为 1000,再修改一下特征值,全部完成后效果如图:



    现在关掉 PETools,我们开始用 ImportREC 来修正输入表。去掉添加一个新的节前面的勾,在新建输入表信息中填入 RVA:00010000,点修复转存文件,选择我们修改过的 dumped.exe 来修复,完成后我们得到 dumped_.exe。到这就要开始把资源加进去了。 FixRes 上,选我们原来备份的那个 dumped.exe,新建 RVA 为 11000,文件对齐为 200,Dump 资源为 rsrc.bin:



    再让 LordPE 上场吧(有人要说了,这么多工具换来换去你也不嫌累?这个...是比较累,本来是打算自己写一个工具来减小工作量的。不过因为太懒,能将就就将就了)。用 LordPE 打开修正过输入表的那个 dumped_.exe,点区段按钮,在区段编辑窗口中右键选择从磁盘载入段,把我们前面 Dump 的那个 rsrc.bin 添加进来。编辑一下区段,最终效果如下:



    现在关掉区段编辑,点击目录按钮,再用 LordPE 打开备份的 dumped.exe 进行参考,我们来编辑一下 dumped_.exe 的目录,主要是调整一下资源目录的 RVA 和大小,把其他一些没用到的目录 RVA 和 大小清零。最终效果如下:



    完成后退出 LordPE,再用 PETools 打开 dumped_.exe,编辑可选头中的一些内容及调整镜像大小,完成后效果如下:



    保存我们所做的工作,退出 PETools,现在 dumped_.exe 文件大小是 72.5K。运行一下 dumped_.exe,呵呵,正常运行了。把 dumped_.exe 复制一份保存为复件 dumped_.exe,用 dumped_.exe 给复件 dumped_.exe 用默认选项加个壳看看, 72.5K->27.3K,原版未脱壳前是 26.6K,看来还是有差距啊。不管了,用  PE Optimizer 来优化一下 dumped_.exe,72.5K->59.5K,收工。
    WinUpack 加壳时合并了区段,而一些其他的壳给程序加壳时并没有合并区段,也没有破坏 PE 头,这样脱壳后的程序优化起来要简单一点,可以省掉前面的到脱壳后的第一个段中判断区段及修正 PE 头的步骤,只要把有用的段给保存下来,没用的去掉,选好位置重建输入表和资源,再装配起来就可以了。要想优化后的程序可以用汉化工具汉化的话,一般都要把资源放在最后一个区段,否则容易出错。而对应 DLL 这样的文件修复时要考虑重定位和输出表,重定位可以采用 ReloX 来修复,同样可以指定位置重建。输出表可以采用看雪兄的工具 PeMove 来挪移,同样这个工具也可以挪移重定位表。关于 DLL 这类文件的脱壳后优化我就不讲了,基本方法类似。

    附件是用来测试的程序及 WinUpackC.exe 脱壳后文件。

    【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2010-5-7 19:46:37 | 显示全部楼层
    /*
                            . .: .:.. :.. .. .:.::. :. ..:
                          <<-==苒圹圹?苒圹圹?苒圹圹?==<
                           .:: 圹?圹?圹?圹?圹?圹?.:.
                           . .:.苘苒圻.咣圹圹?圹圹圹?..
                            ...圹圮苘?苘苘圹?圹?圹?::.
                           >===圹圹圹?圹圹圹?圹?圹?->>
                          .: .:.. ..:. .: ..:.::. ::.. :.:.

                                      [PEWRSEC]
                        PE Write Section, by Jacky Qwerty/29A


    Here's a new utility from 29A. This program simply sets the  write bit to a
    section in a PE file. This is needed when you need write access to the code
    section in a  first generation sample,  for instance.  There is one utility
    from the SDK (EDITBIN) which does exactly the same thing with PE filez, but
    it needs some huge DLLz from VC to work.  On the other hand, PEWRSEC can be
    compiled as a stupid COM file. Hope this will be handy enough for you ;)

                                                                              */
    /*- -[PEWRSEC.C]- - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */

    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "types.h"
    #include "mz.h"
    #include "pe.h"

    #define SizeBuffMZ sizeof(IMAGE_DOS_HEADER)
    #define SizeBuffPE (4 + IMAGE_SIZEOF_FILE_HEADER + IMAGE_SIZEOF_STD_OPTIONAL_HEADER)
    #define SizeBuffSH IMAGE_SIZEOF_SECTION_HEADER
    #define SizeBuffMax max(SizeBuffMZ, max(SizeBuffPE, SizeBuffSH))

    INT Strncmpz(BYTE *S1, BYTE *S2, INT Count) {
      while (Count--) {
        if (*S1 < *S2) return -1;  // This fucntion doesnt seem to be implemented
        if (*S1 > *S2++) return 1; // in the standard C string library, It combines
        if (!*S1++) break; }       // the funtionality of "strcmp" and "strncmp".
      return 0;
    }

    INT main(INT argc, CHAR *argv[]) {
      FILE *File;
      INT RetValue = 1;
      PCHAR SecName = NULL, FileName = NULL;
      WORD Sections;
      PIMAGE_DOS_HEADER pMZ;
      PIMAGE_NT_HEADERS pPE;
      PIMAGE_SECTION_HEADER pSH;
      CHAR Buffer[SizeBuffMax];
      printf("PEWRSEC - Sets the WRITE bit to a PE section - (c) 1997 jqwerty/29A\n\n");
      if (argc != 2 && argc != 3) {
        printf("  Syntax: PEWRSEC [/SEC:<SectionName>] <FileName>  (default: code section)\n");
        Ret: return RetValue; }
      while (--argc) {
        if (*argv[argc] != '/') {
          if ((FileName = argv[argc]) == NULL) { printf("No filename specified\n"); goto Ret; } }
        else if (!strncmpi(argv[argc] + 1, "SEC:", 4)) SecName = argv[argc] + 5;
             else { printf("Unknown option '%s'\n", argv[argc]); goto Ret; } }
      if ((File = fopen(FileName, "rb+")) == 0) {
        printf("Can't open '%s'\n", FileName); goto Ret; }
      if (!fread(pMZ = (PIMAGE_DOS_HEADER)Buffer, SizeBuffMZ, 1, File)) {
        ReadErr:
          if (!feof(File)) { printf("Error reading file\n"); CloseFile: fclose(File); goto Ret; }
          else { InvalidPE: printf("Not a valid PE file\n"); goto CloseFile; } }
      if (pMZ->e_magic != IMAGE_DOS_SIGNATURE) goto InvalidPE;
      if (fseek(File, pMZ->e_lfanew, SEEK_SET)) {
        SeekErr:
          if (errno != EBADF) { printf("Error in file seek\n"); goto CloseFile; }
          else goto InvalidPE; }
      if (!fread(pPE = (PIMAGE_NT_HEADERS)Buffer, SizeBuffPE, 1, File)) goto ReadErr;
      if (pPE->Signature != IMAGE_NT_SIGNATURE || !(Sections = pPE->FileHeader.NumberOfSections)) goto InvalidPE;
      if (fseek(File, FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pPE->FileHeader.SizeOfOptionalHeader - SizeBuffPE, SEEK_CUR)) goto SeekErr;
      do {
        if (!fread(pSH = (PIMAGE_SECTION_HEADER)Buffer, SizeBuffSH, 1, File)) goto ReadErr;
        if (SecName) { if (!Strncmpz(SecName, pSH->Name, 8)) break; }
        else if (pSH->VirtualAddress <= pPE->OptionalHeader.AddressOfEntryPoint && pPE->OptionalHeader.AddressOfEntryPoint < pSH->VirtualAddress + pSH->Misc.VirtualSize) break;
      } while (--Sections);               
      if (!Sections) { printf("Section not found\n"); goto CloseFile; }
      if (!(pSH->Characteristics & IMAGE_SCN_MEM_WRITE)) {
        pSH->Characteristics |= IMAGE_SCN_MEM_WRITE;
        if (fseek(File, - SizeBuffSH, SEEK_CUR)) goto SeekErr;
        if (!fwrite(pSH, SizeBuffSH, 1, File) || fflush(File)) {
          printf("Error writing file\n"); goto CloseFile; } }
      printf("Ok\n"); RetValue = 0; goto CloseFile;
    }

    /*- -[MZ.H] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */

    //
    // DOS EXE MZ format
    //

    #define IMAGE_DOS_SIGNATURE 0x5A4D        // MZ

    typedef struct _IMAGE_DOS_HEADER {        // DOS EXE header
        WORD   e_magic;                       // Magic number
        WORD   e_cblp;                        // Bytes on last page of file
        WORD   e_cp;                          // Pages in file
        WORD   e_crlc;                        // Relocations
        WORD   e_cparhdr;                     // Size of header in paragraphs
        WORD   e_minalloc;                    // Minimum extra paragraphs needed
        WORD   e_maxalloc;                    // Maximum extra paragraphs needed
        WORD   e_ss;                          // Initial (relative) SS value
        WORD   e_sp;                          // Initial SP value
        WORD   e_csum;                        // Checksum
        WORD   e_ip;                          // Initial IP value
        WORD   e_cs;                          // Initial (relative) CS value
        WORD   e_lfarlc;                      // File address of relocation table
        WORD   e_ovno;                        // Overlay number
        WORD   e_res[4];                      // Reserved words
        WORD   e_oemid;                       // OEM identifier (for e_oeminfo)
        WORD   e_oeminfo;                     // OEM information; e_oemid specific
        WORD   e_res2[10];                    // Reserved words
        LONG   e_lfanew;                      // File address of new exe header
      } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

    /*- -[PE.H] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */

    //
    // Portable Executable format
    //

    #define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

    // File header format.

    typedef struct _IMAGE_FILE_HEADER {
        WORD    Machine;
        WORD    NumberOfSections;
        DWORD   TimeDateStamp;
        DWORD   PointerToSymbolTable;
        DWORD   NumberOfSymbols;
        WORD    SizeOfOptionalHeader;
        WORD    Characteristics;
    } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

    #define IMAGE_SIZEOF_FILE_HEADER             20

    #define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
    #define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).
    #define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
    #define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
    #define IMAGE_FILE_MINIMAL_OBJECT            0x0010  // Reserved.
    #define IMAGE_FILE_UPDATE_OBJECT             0x0020  // Reserved.
    #define IMAGE_FILE_16BIT_MACHINE             0x0040  // 16 bit word machine.
    #define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
    #define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
    #define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file
    #define IMAGE_FILE_PATCH                     0x0400  // Reserved.
    #define IMAGE_FILE_SYSTEM                    0x1000  // System File.
    #define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
    #define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.

    #define IMAGE_FILE_MACHINE_UNKNOWN           0
    #define IMAGE_FILE_MACHINE_I386              0x14c   // Intel 386.
    #define IMAGE_FILE_MACHINE_R3000             0x162   // MIPS little-endian, 0540 big-endian
    #define IMAGE_FILE_MACHINE_R4000             0x166   // MIPS little-endian
    #define IMAGE_FILE_MACHINE_ALPHA             0x184   // Alpha_AXP
    #define IMAGE_FILE_MACHINE_POWERPC           0x1F0   // IBM PowerPC Little-Endian

    // Directory format.

    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress;
        DWORD   Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

    #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

    // Optional header format.

    typedef struct _IMAGE_OPTIONAL_HEADER {

        // Standard fields.

        WORD    Magic;
        BYTE    MajorLinkerVersion;
        BYTE    MinorLinkerVersion;
        DWORD   SizeOfCode;
        DWORD   SizeOfInitializedData;
        DWORD   SizeOfUninitializedData;
        DWORD   AddressOfEntryPoint;
        DWORD   BaseOfCode;
        DWORD   BaseOfData;

        // NT additional fields.

        DWORD   ImageBase;
        DWORD   SectionAlignment;
        DWORD   FileAlignment;
        WORD    MajorOperatingSystemVersion;
        WORD    MinorOperatingSystemVersion;
        WORD    MajorImageVersion;
        WORD    MinorImageVersion;
        WORD    MajorSubsystemVersion;
        WORD    MinorSubsystemVersion;
        DWORD   Reserved1;
        DWORD   SizeOfImage;
        DWORD   SizeOfHeaders;
        DWORD   CheckSum;
        WORD    Subsystem;
        WORD    DllCharacteristics;
        DWORD   SizeOfStackReserve;
        DWORD   SizeOfStackCommit;
        DWORD   SizeOfHeapReserve;
        DWORD   SizeOfHeapCommit;
        DWORD   LoaderFlags;
        DWORD   NumberOfRvaAndSizes;
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

    #define IMAGE_SIZEOF_STD_OPTIONAL_HEADER      28
    #define IMAGE_SIZEOF_NT_OPTIONAL_HEADER      224

    #define IMAGE_NT_OPTIONAL_HDR_MAGIC        0x10b

    typedef struct _IMAGE_NT_HEADERS {
        DWORD Signature;
        IMAGE_FILE_HEADER FileHeader;
        IMAGE_OPTIONAL_HEADER OptionalHeader;
    } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;

    // Calculate the byte offset of a field in a structure of type type.

    #define FIELD_OFFSET(type, field)    ((LONG)&(((type *)0)->field))

    // Calculate the first section header

    #define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER)        \
        ((DWORD)ntheader +                                                  \
         FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) +                 \
         ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader   \
        ))

    // Subsystem Values

    #define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
    #define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
    #define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
    #define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
    #define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
    #define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image run  in the Posix character subsystem.

    // Dll Characteristics

    #define IMAGE_LIBRARY_PROCESS_INIT           1   // Dll has a process initialization routine.
    #define IMAGE_LIBRARY_PROCESS_TERM           2   // Dll has a thread termination routine.
    #define IMAGE_LIBRARY_THREAD_INIT            4   // Dll has a thread initialization routine.
    #define IMAGE_LIBRARY_THREAD_TERM            8   // Dll has a thread termination routine.

    // Loader Flags

    #define IMAGE_LOADER_FLAGS_BREAK_ON_LOAD    0x00000001
    #define IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD    0x00000002


    // Directory Entries

    #define IMAGE_DIRECTORY_ENTRY_EXPORT         0   // Export Directory
    #define IMAGE_DIRECTORY_ENTRY_IMPORT         1   // Import Directory
    #define IMAGE_DIRECTORY_ENTRY_RESOURCE       2   // Resource Directory
    #define IMAGE_DIRECTORY_ENTRY_EXCEPTION      3   // Exception Directory
    #define IMAGE_DIRECTORY_ENTRY_SECURITY       4   // Security Directory
    #define IMAGE_DIRECTORY_ENTRY_BASERELOC      5   // Base Relocation Table
    #define IMAGE_DIRECTORY_ENTRY_DEBUG          6   // Debug Directory
    #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT      7   // Description String
    #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR      8   // Machine Value (MIPS GP)
    #define IMAGE_DIRECTORY_ENTRY_TLS            9   // TLS Directory
    #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG   10   // Load Configuration Directory

    // Section header format.

    #define IMAGE_SIZEOF_SHORT_NAME              8

    typedef struct _IMAGE_SECTION_HEADER {
        BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
        union {
                DWORD   PhysicalAddress;
                DWORD   VirtualSize;
        } Misc;
        DWORD   VirtualAddress;
        DWORD   SizeOfRawData;
        DWORD   PointerToRawData;
        DWORD   PointerToRelocations;
        DWORD   PointerToLinenumbers;
        WORD    NumberOfRelocations;
        WORD    NumberOfLinenumbers;
        DWORD   Characteristics;
    } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

    #define IMAGE_SIZEOF_SECTION_HEADER          40

    #define IMAGE_SCN_TYPE_REGULAR               0x00000000  //
    #define IMAGE_SCN_TYPE_DUMMY                 0x00000001  // Reserved.
    #define IMAGE_SCN_TYPE_NO_LOAD               0x00000002  // Reserved.
    #define IMAGE_SCN_TYPE_GROUPED               0x00000004  // Used for 16-bit offset code.
    #define IMAGE_SCN_TYPE_NO_PAD                0x00000008  // Reserved.
    #define IMAGE_SCN_TYPE_COPY                  0x00000010  // Reserved.

    #define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
    #define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
    #define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.

    #define IMAGE_SCN_LNK_OTHER                  0x00000100  // Reserved.
    #define IMAGE_SCN_LNK_INFO                   0x00000200  // Section contains comments or some other type of information.
    #define IMAGE_SCN_LNK_OVERLAY                0x00000400  // Section contains an overlay.
    #define IMAGE_SCN_LNK_REMOVE                 0x00000800  // Section contents will not become part of image.
    #define IMAGE_SCN_LNK_COMDAT                 0x00001000  // Section contents comdat.

    #define IMAGE_SCN_ALIGN_1BYTES               0x00100000  //
    #define IMAGE_SCN_ALIGN_2BYTES               0x00200000  //
    #define IMAGE_SCN_ALIGN_4BYTES               0x00300000  //
    #define IMAGE_SCN_ALIGN_8BYTES               0x00400000  //
    #define IMAGE_SCN_ALIGN_16BYTES              0x00500000  // Default alignment if no others are specified.
    #define IMAGE_SCN_ALIGN_32BYTES              0x00600000  //
    #define IMAGE_SCN_ALIGN_64BYTES              0x00700000  //

    #define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
    #define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
    #define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
    #define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
    #define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
    #define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
    #define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.

    /*- -[TYPES.H]- - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */

    #ifndef CHAR
    typedef signed char CHAR;
    #endif

    #ifndef SHORT
    typedef signed short SHORT;
    #endif

    #ifndef LONG
    typedef signed long LONG;
    #endif

    #ifndef INT
    typedef signed INT;
    #endif

    #ifndef BYTE
    typedef unsigned char BYTE;
    #endif

    #ifndef WORD
    typedef unsigned short WORD;
    #endif

    #ifndef DWORD
    typedef unsigned long DWORD;
    #endif

    #ifndef UINT
    typedef unsigned UINT;
    #endif

    #ifndef PCHAR
    typedef CHAR *PCHAR;
    #endif

    #ifndef PSHORT
    typedef SHORT *PSHORT;
    #endif

    #ifndef PLONG
    typedef LONG *PLONG;
    #endif

    #ifndef PINT
    typedef INT *PINT;
    #endif

    #ifndef PBYTE
    typedef BYTE *PBYTE;
    #endif

    #ifndef PWORD
    typedef WORD *PWORD;
    #endif

    #ifndef PDWORD
    typedef DWORD *PDWORD;
    #endif

    #ifndef PUINT
    typedef UINT *PUINT;
    #endif
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表