首页
社区
课程
招聘
[成果6.1]软件保护壳技术专题 - 添加新节
发表于: 2008-6-15 18:09 32407

[成果6.1]软件保护壳技术专题 - 添加新节

2008-6-15 18:09
32407

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

名词解释:
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   

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

[COLOR=Red]    ;; 在建立映射后
    ;; 这段代码将映射后文件的指针存放在局部变量pMem.
    ;; 而指定的NT结构头指针存放到esi寄存器处。
    ;; 现在我们调用添加节函数添加一个新节,AddSection是
    ;; 我们这节的主要函数,将在下面讲解。
    ;; 增加一个新节
    ;; pMem:文件映射内存指针
    ;; g_szNewSectionName:新节的节名,这里是(.new)
    ;; g_dwNewSectionSize:新节的长度,这里是offset EndNewSection - offset NewSection[/COLOR]
    invoke AddSection, pMem, offset g_szNewSectionName, g_dwNewSectionSize
    push eax
    mov esi, dwNTHeaderAddr
    assume esi : ptr IMAGE_NT_HEADERS
[COLOR=Red]    ;; 下面做的就是设置新节的中的原代码入口点,就是真正的入口地址[/COLOR].
    mov ebx, dword ptr [esi].OptionalHeader.AddressOfEntryPoint
    add ebx, dword ptr [esi].OptionalHeader.ImageBase
[COLOR=Red]    ;; OrigAddressOfEntry这个变量在CryptFile底部的NewSection节中[/COLOR]
    mov eax, offset OrigAddressOfEntry
    mov dword ptr [eax], ebx
[COLOR=Red]    ;; 更新入口点,将新节的入口点设置到NT头结构的AddressOfEntryPoint
    ;; 哪个节的VirusAddress被设置到AddressOfEntryPoint处,哪个节将会被先执行
    ;; 这也是最通常的入口点技术[/COLOR]
    pop eax
    assume eax : ptr IMAGE_SECTION_HEADER    
    push dword ptr [eax].VirtualAddress
    pop dword ptr [esi].OptionalHeader.AddressOfEntryPoint
[COLOR=Red]    ;; 下面的代码利用新节节表将新节的代码写入文件[/COLOR]
    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:
[COLOR=Red]   ;; 在这里获取地址
   ;; call指令会将下条指令的地址压入堆栈
   ;; 注意此指令的OPCODE为EB00000000
   ;; 病毒与Shellcode等常用此指令定位
   ;; 杀毒软件的启发式搜索常将此特征作为查找特征
   ;; 聪明的读者可以自己修改定位代码来躲过
   ;; 这类的查杀[/COLOR]
   call GetEip
   GetEip:
[COLOR=Red]   ;; eax中有保存着当前的地址,标号为GetEip[/COLOR]
   pop eax
   add eax, offset OrigAddressOfEntry - offset GetEip
[COLOR=Red]   ;; 两个偏移的差就是这两个地址之间的距离,它的距离 + 起始地址
   ;; 就为OrigAddressOfEntry的地址
   ;; 最后将OrigAddressOfEntry保存的值,也就是原来的入口节的地址
   ;; 送回eax中。[/COLOR]
   mov eax, dword ptr [eax]
[COLOR=Red]   ;; 跳到原入口点地址[/COLOR]
   jmp eax
;; ----- 新节数据 -----
OrigAddressOfEntry  dd ?

EndNewSection:

CryptFile endp

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 8
支持
分享
最新回复 (44)
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
2
沙发学习。。
2008-6-15 20:40
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
3
效率真高啊!
2008-6-15 20:55
0
雪    币: 272
活跃值: (143)
能力值: ( LV15,RANK:930 )
在线值:
发帖
回帖
粉丝
4
仔细看,学习
2008-6-15 21:17
0
雪    币: 47147
活跃值: (20465)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
5
感觉“玩命”这个ID要火
2008-6-15 21:28
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
6
经过老大鉴定,准火!
2008-6-15 23:04
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
注释很详细适合我这种菜鸟。学习,学习,再学习。
2008-6-16 08:53
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
很详细,学习
2008-6-17 23:23
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
看看楼上发帖的就知道
已经火了。。。
支持
2008-6-18 12:59
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
仔细看,学习
2008-6-20 08:22
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
看了  很好
2008-6-20 17:21
0
雪    币: 65
活跃值: (811)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
12
嘿嘿~~~
"玩命",很流行的一个词哦~~~准火的!
2008-6-28 14:13
0
雪    币: 10635
活跃值: (2329)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
玩命  火啊  记得以前乘龙大锅的一片就有“玩命”
2008-6-28 15:50
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
看下,讲的不错
2008-6-28 19:14
0
雪    币: 193
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
这个和导入加密表有同样的错误,LogicShellExit这个标号的位置错了!
2008-7-1 18:53
0
雪    币: 193
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
这个程序在处理源程序最后一个节(如果最后一个节的文件大小为0)的时候有点问题:
    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  ;如果源程序最后一个节的PointerToRawData为0
    add ebx, dword ptr [eax].SizeOfRawData           ; SizeOfRawData也为0
    invoke PEAlign, ecx, dwSecAlig
    mov dword ptr [esi].VirtualAddress, eax
    ;; set section pointertorawdata
    invoke PEAlign, ebx, dwFileAlig
    mov dword ptr [esi].PointerToRawData, eax         ;此时eax为0
    ;; 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        ;PointerToRawData 为0
    pop edi
    add edi, pMem        ;pe文件起始处
    ;; clear the new sec
    ;; 在这里做一下清0工作ZeroMemory
    mov ecx, dwSectionSize
    xor eax, eax
    cld
    rep stosb ;MZ标志被覆盖了
在MASM32写的程序中,如果最后一个段为.data段就可能出现这个问题:
                .data?

hInstance        dd                ?
hWinMain        dd                ?
比如是这个段!
我把例子文件发上来!
上传的附件:
2008-7-2 14:00
0
雪    币: 189
活跃值: (46)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
都玩命了再不火那不没天理了……哈哈!
学习之……
2008-7-2 14:28
0
雪    币: 259
活跃值: (26)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
18
如果有IMAGE_DIR_ENTRY_BOUND_IMPORT的话,好像要清零的吧,不然会有问题的
2008-7-3 18:18
0
雪    币: 7115
活跃值: (639)
能力值: (RANK:1290 )
在线值:
发帖
回帖
粉丝
19
zeal 提出的问题在2K下存在。  朋友们可以将数据目录的BOUND_IMPORT清0,这个问题就得以解决了。
2008-7-4 16:27
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
看了一天的文章了,先MARK一下,谢谢楼主!
2008-9-23 16:46
0
雪    币: 175
活跃值: (74)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
21
学习了,楼主!
谢谢提供这么好的学习文章~!
支持你~!
2008-10-2 10:44
0
雪    币: 7187
活跃值: (3702)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
仔细看了好多便,对pe有更深刻认识。
2008-11-25 22:28
0
雪    币: 315
活跃值: (23)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
23
我本来不想评论的,可是你让我评论评论,我评评你的文章。你不是觉得你自我感觉良好嘛。

add esi, sizeof IMAGE_NT_HEADERS
这么明显的低级错误。至于为什么,我在此贴已经说了。。http://bbs.pediy.com/showthread.php?t=83623   
你不是也经常看Virus代码吗,这么低级的错误也会犯??

还有不知道楼主写这样的代码和高级语言有啥区别。
.IF eax == INVALID_HANDLE_VALUE
         jmp OpenFileFailed               
     .ENDIF
     mov hFile, eax
     invoke GetFileSize, hFile, NULL
    .IF eax == 0
        invoke CloseHandle, hFile  
        jmp GetFileSizeFailed
    .ENDIF

IF eax == 0
         invoke CloseHandle, hMap
         invoke CloseHandle, hFile
         jmp MapFileFailed
     .ENDIF

人家用C写的,用vc的cl编译器开了编译优化选项都比你编译的代码效率高和好看。。 其他的我就不想看下去了。还是那句话,不了解的就不要去妄加评论。。
我就是想说一个不了解的就不要去妄加评论。。
2009-3-13 18:26
0
雪    币: 7115
活跃值: (639)
能力值: (RANK:1290 )
在线值:
发帖
回帖
粉丝
24
呵呵 谢谢你的评点。 不过这只是一个科普的帖子。 我如果把全部时间都搭到来看雪发帖, 我平时就不用工作了。 以后发帖我会尽量注意。
2009-3-13 21:23
0
雪    币: 243
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
25
添加新节的例子在罗云彬的《Windows32汇编语言程序设计》一书中也有介绍,最近刚看完,呵呵。不知楼主文中提到的利用SizeofImage这个值躲过启发式搜索,怎样实现呢?
2009-7-21 20:53
0
游客
登录 | 注册 方可回帖
返回
//