首页
社区
课程
招聘
[原创]yoda's Crypter 1.2源码再分析
发表于: 2013-6-14 01:21 9952

[原创]yoda's Crypter 1.2源码再分析

2013-6-14 01:21
9952
希望通过学习yoda's Crypter 1.2 加强对脱壳技术的理解,只是菜鸟学习的笔记而已。
demoscene也发过关于这个壳源码的分析帖子,在学习这个壳的时候我也参考了这个帖子很多地方,有些地方则是自己调试明白的。
看完这个壳的源码,感觉对脱壳有点概念了 而不是跟着教程 第一步, 第二步.......

工具准备

1 yoda's Crypter 1.2源码
下载地址 http://www.pediy.com/sourcecode/pack.htm
2 MASM
下载地址 百度

编译源码(这一步折腾了很久,环境变量设不成功唯有用绝对路径了)

E:\masm32\bin\Cvtres.exe /machine:ix86 rsrc.res
E:\masm32\bin\ml.exe /c /coff /Zp1 yc.asm
E:\masm32\bin\Link.exe /SUBSYSTEM:WINDOWS /MERGE:.idata=.text /MERGE:.data=.text  /MERGE:.rdata=.text /SECTION:.text,EWR /IGNORE:4078 yc.obj rsrc.obj
注意一下,源码里面的temptfile.asm是没用的。

了解yoda的大致功能

yoda是一个加密壳,在加密PE文件的同时可以
1 检测Softice调试器
2 擦除PE头
3 CRC自校验
4 反Dump
5 删除原输入表信息
6 API重定向
现在还不太清楚这些功能具体是什么, 不过看了代码之后应该就知道了!

各个文件里面大概做了什么

CryptStuff.ASM
里面的函数有
CryptFile PROC szFname : LPSTR, hDlg : HWND,dwProtFlags : DWORD
EncryptSec esi=CryptStart ecx = CryptSize
AddSection PROC USES edi esi ebx ecx edx, pMem_ : LPVOID
AssembleIT PROC USES ebx ecx edx esi edi, pAddress4IT : LPVOID, dwNewSectionVA : DWORD
ProcessTlsTable PROC USES edi ebx esi ecx, pFileMem : LPVOID, CryptSectionVA : DWORD
ProcessOrgIT PROC USES edi esi edx, pFileImage : LPVOID, pITBaseRO : LPVOID
PEAlign PROC USES ecx edx, dwTarNum : DWORD, dwAlignTo : DWORD
RVA2Offset PROC USES ebx ecx edx, Base : DWORD,dwITRVA : DWORD
DepackerCode


大体是这样,其他的详细再看了
即CryptStuff.ASM负责将PE文件加密,处理各个表(输入表/Tls等)
然后壳的引导段也是在这个文件里,即DepackerCode

yC.asm
主要是处理界面的问题,将用户选择的文件,加密选项传入CryptStuff.ASM的函数CryptFile处理

PER.ASM
主要跟产生随机数有关的代码,之后的加密部分可能需要用到随机数
Rsrc.res
资源文件

resource.inc
资源头文件
详细分析源代码

就是说现在我们大概读代码的顺序是
resource.inc
yc.asm
CryptStuff.ASM
PER.ASM

resource.inc
IDD_MAINDLG              equ 100
              
IDC_TARGETFILE           equ 1000
IDC_CHOOSEFILE           equ 1001
IDC_CLOSE                equ 1002
IDC_CRYPT                equ 1003

IDC_SICHECK              equ 2001
IDC_ERASEPEHEADER        equ 2002
IDC_DESTROYIMPORT        equ 2003
IDC_CHECKHEADERCRC       equ 2004
IDC_ANTIDUMP             equ 2005
IDC_APIREDIRECT          equ 2006

IDI_ICON                 equ 9000

这个大概可以猜出意思来,略。
yc.asm
.386
.model flat, stdcall
option casemap:none


include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\shell32.inc
include \masm32\include\imagehlp.inc


includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\shell32.lib
includelib \masm32\lib\imagehlp.lib


include \masm32\include\windows.inc
include resource.inc

头文件,库包含

DlgProc PROTO :HWND, :UINT, :WPARAM, :LPARAM

窗口过程声明

;------------ CONST ---------
.const
CHECK_SI_FLAG           equ 1
ERASE_HEADER_FLAG       equ 2
DESTROY_IMPORT_FLAG     equ 4
CHECK_HEADER_CRC        equ 8
ANTI_DUMP_FLAG          equ 16
API_REDIRECT_FLAG       equ 32

这上面的分别对应加密选项

szFilter                db "ExE files",0,"*.exe",0,"All files",0,"*.*",0,0
szCurDir             db ".",0
szNoFile             db "No file selected up to now !",0
szErr                    db "ERROR",0


szFilter是给打开文件对话框用的过滤字符串
szCurDir当前目录
szNoFile文件未选择的错误信息
szErr   未知错误信息


;------------ DATA ----------
.data
hInst                   dd 0
hDLG                    dd 0
ofn                     OPENFILENAME <>
cFname             db MAX_PATH dup (0)


hInst  是程序自身的句柄
hDlg   是对话框窗口的句柄
ofn    是打开文件对话框用的结构
cFname 是文件名

;------------ CODE ----------
.code

include CryptStuff.ASM
include PER.ASM

main:
  invoke GetModuleHandle,0
  mov hInst,eax
  invoke DialogBoxParam,eax,IDD_MAINDLG,0,offset DlgProc,0
THEEND:
  invoke ExitProcess,0
包含两个文件,之后要用到
得到程序的句柄
打开对话框窗口,资源ID是IDD_MAINDLG,窗口处理过程是DlgProc

下面这个文件较多,直接写注释了
DlgProc proc hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  LOCAL  hDrop : HANDLE

  pushad
  .IF uMsg == WM_INITDIALOG
     invoke LoadIcon,hInst,IDI_ICON
     invoke SendMessage,hDlg,WM_SETICON,TRUE,eax                         ;设置图标为IDI_ICON
     push hDlg
     pop hDLG
     ; check the checkboxes :)
     invoke CheckDlgButton,hDlg,IDC_CHECKHEADERCRC,TRUE
     invoke CheckDlgButton,hDlg,IDC_DESTROYIMPORT,TRUE
     invoke CheckDlgButton,hDlg,IDC_ANTIDUMP,TRUE
     INVOKE CheckDlgButton,hDlg,IDC_APIREDIRECT,TRUE      ;默认选上某几个选项
     invoke DragAcceptFiles,hDlg,TRUE          ;支持文件拖拽
     
  .ELSEIF uMsg == WM_DROPFILES
     push wParam
     pop hDrop
     invoke DragQueryFile,hDrop,0,offset cFname,sizeof cFname
     invoke DragFinish,hDrop
     invoke SetDlgItemText,hDlg,IDC_TARGETFILE,offset cFname     ;得到拖拽的文件名放到文本框IDC_TARGETFILE中

     
  .ELSEIF uMsg == WM_COMMAND
           mov eax,wParam
           
           .IF ax == IDC_CLOSE
              invoke SendMessage,hDlg,WM_CLOSE,NULL,NULL        ;关闭按钮
              
     .ELSEIF ax == IDC_CHOOSEFILE
        ; get a file path
        mov ofn.lStructSize,SIZEOF ofn 
        mov ofn.lpstrFilter,offset szFilter
        push hDlg
        pop ofn.hwndOwner
        mov ofn.lpstrFile, offset cFname 
        mov ofn.nMaxFile,SIZEOF cFname
        mov ofn.lpstrInitialDir,offset szCurDir
        mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_HIDEREADONLY
        push offset ofn                  ;填充打开文件对话框要用的结构
        call GetOpenFileName                ;打开文件对话框
        test eax,eax
        jz @@ExitDlgProc
        invoke SetDlgItemText,hDlg,IDC_TARGETFILE,offset cFname          ;得到的文件名放到文本框IDC_TARGETFILE中

                    
     .ELSEIF ax == IDC_CRYPT
        ; ----- was a file selected ? -----
        mov eax,offset cFname
        .IF byte ptr [eax] == 0
           invoke MessageBox,hDlg,offset szNoFile,offset szErr,MB_ICONERROR
           jmp @@ExitDlgProc
        .ENDIF                     ;如果没有选择文件则弹出错误
        
        ; ---- build the protection flag -----
        xor edi,edi
        invoke IsDlgButtonChecked,hDlg,IDC_SICHECK
        .IF eax == BST_CHECKED
           or edi,CHECK_SI_FLAG
        .ENDIF
        invoke IsDlgButtonChecked,hDlg,IDC_ERASEPEHEADER
        .IF eax == BST_CHECKED
           or edi,ERASE_HEADER_FLAG
        .ENDIF
        invoke IsDlgButtonChecked,hDlg,IDC_DESTROYIMPORT
        .IF eax == BST_CHECKED
           or edi,DESTROY_IMPORT_FLAG
        .ENDIF
        invoke IsDlgButtonChecked,hDlg,IDC_CHECKHEADERCRC
        .IF eax == BST_CHECKED
           or edi,CHECK_HEADER_CRC
        .ENDIF
        invoke IsDlgButtonChecked,hDlg,IDC_ANTIDUMP
        .IF eax == BST_CHECKED
           or edi,ANTI_DUMP_FLAG
        .ENDIF
        INVOKE IsDlgButtonChecked,hDlg,IDC_APIREDIRECT
        .IF EAX == BST_CHECKED
           OR  EDI, API_REDIRECT_FLAG
        .ENDIF                        ;用edi的32位来存储加密选项信息
        push edi
        push hDlg
        push offset cFname
        call CryptFile                ;调用加密文件
     .ENDIF
     
  .ELSEIF uMsg == WM_CLOSE
     invoke EndDialog,hDlg,0
  .ENDIF
  popad
  
  @@ExitDlgProc:
  xor eax,eax
  ret
DlgProc endp
end main


CryptStuff.asm
;------ MACROS -----
PUPO MACRO pSrc, pDest
  PUSH pSrc
  POP  pDest
ENDM

;------ DEFINITIONS -------
DEPACKER_CODE_SIZE      equ (offset DepackerCodeEnd - offset DepackerCode)
CHECKSUM_SKIP_SIZE      equ 5  ; (don't include the saved checksum itself in the checksum calculation)
TLS_BACKUP_ADDR         equ (offset TlsBackupLabel - offset DepackerCode)
CHECKSUM_ADDR           equ (OFFSET ChecksumLabel - OFFSET DepackerCode)
CRYPT_LOADER_SIZE_DB    EQU (OFFSET LOADER_CRYPT_END - OFFSET LOADER_CRYPT_START)
CRYPT_OEP_JUMP_SIZE     equ (OFFSET OEP_JUMP_CODE_END - OFFSET OEP_JUMP_CODE_START)
IT_SIZE                 equ 060h
MAX_SECTION_NUM         equ 20
MAX_IID_NUM             equ 30
OEP_JUMP_ENCRYPT_NUM    equ ('y')
LOADER_CRC_CHECK_SIZE   equ (OFFSET OEP_JUMP_CODE_START - OFFSET DepackerCode)
VAR_PER_SIZE            EQU 030h
SEC_PER_SIZE            EQU 030h

;------- CONST --------
.const
szDone                  db "File encrypted successfully !",0
szDoneCap               db ":)",0
szFileErr               db "File access error :(",0
szNoPEErr               db "Invalid PE file !",0
szNoMemErr              db "Not enough memory :(",0
szFsizeErr              db "Files with a filesize of 0 aren't allowed !",0
szNoRoom4SectionErr     db "There's no room for a new section :(",0
szSecNumErr             db "Too many sections !",0
szIIDErr                DB "Too much ImageImportDescriptors !",0

ALIGN_CORRECTION        dd 01000h    ; this big value is e.g. needed for WATCOM compiled files
DEPACKER_SECTION_NAME   dd ('Cy')
szKernel                db "KeRnEl32.dLl",0
szLoadLibrary           db "LoadLibraryA",0
szGetProcAddress        db "GetProcAddress",0

;------- DATA ---------
.data
pMap                    dd 0
dwBytesRead             dd 0
dwBytesWritten          dd 0
pMem                    dd 0
dwFsize                 dd 0
dwOutPutSize            dd 0
dwNewFileEnd            dd 0
dwNTHeaderAddr          dd 0
dwSectionNum            dd 0
dwNewSectionRO          dd 0
dwOrgITRVA              dd 0
hFile                   dd 0

;------- CODE ---------
.code
CryptFile PROC szFname : LPSTR, hDlg : HWND,dwProtFlags : DWORD
  assume fs : nothing
  
  CALL InitRandom
  ;初始化随机种子
  
  ;----- MAP THE FILE -----
  invoke CreateFile,szFname,GENERIC_WRITE + GENERIC_READ,FILE_SHARE_WRITE + FILE_SHARE_READ,\
                     NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
  cmp eax,INVALID_HANDLE_VALUE
  jz FileErr
  ;文件打开不成功,退出
  mov hFile,eax
  invoke GetFileSize,hFile,0
  .IF eax == 0
     push hFile
     call CloseHandle
     jmp FsizeErr
     ;文件大小等于零,退出
  .ENDIF
  mov dwFsize,eax
  mov eax,dwFsize
  add eax,IT_SIZE
  ;加上壳的输入表大小
  add eax,DEPACKER_CODE_SIZE
  ;加上壳引导代码大小,DEPACKER_CODE_SIZE=DepackerCodeEnd - DepackerCode,这个文件的最后面是壳引导代码,其前后有标号DepackerCode和DepackerCodeEnd
  add eax,ALIGN_CORRECTION
  ;ALIGN_CORRECTION这个值是为了支持一个古老的C/C++编译器 Watcom
  mov dwOutPutSize,eax
  push eax
  push GMEM_FIXED + GMEM_ZEROINIT
  call GlobalAlloc
  .IF eax == NULL
     push hFile
     call CloseHandle
     jmp MemErr
  ;申请存放加壳文件的空间   
  .ENDIF
  mov pMem,eax
  invoke ReadFile,hFile,pMem,dwFsize,offset dwBytesRead,NULL
  
  ; ----- check the PE Signature and get some needed values -----
  mov edi,pMem
  .IF word ptr [edi] != 'ZM'
     push pMem
     call GlobalFree
     push hFile
     call CloseHandle
     jmp PEErr
     ;如果没有MZ则退出
  .ENDIF
  add edi,[edi+3Ch]
  .IF word ptr [edi] != 'EP'
     push pMem
     call GlobalFree
     push hFile
     call CloseHandle
     jmp PEErr
     ;如果没有PE标志则退出
  .ENDIF  
  mov dwNTHeaderAddr,edi
  assume edi : ptr IMAGE_NT_HEADERS
  push [edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress
  pop dwOrgITRVA
  ;获取输入表RVA
  push word ptr [edi].FileHeader.NumberOfSections
  pop word ptr dwSectionNum
  ;获取节数量
  .IF dwSectionNum > MAX_SECTION_NUM
     JMP SecNumErr
  ;获取的数据错误
  .ENDIF
  push [edi].OptionalHeader.AddressOfEntryPoint
  pop dwOrgEntryPoint
  ;获取原入OEP
  push [edi].OptionalHeader.ImageBase
  pop dwImageBase
  ;获取基址
  
  ;----- DELETE Bound Import & IAT DIRECTORIES -----
  XOR  EAX, EAX
  MOV  ECX, 4
  LEA  EDI, [EDI].OptionalHeader.DataDirectory[11 *SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress
  assume edi : nothing
   DirDelLoop:
        STOSD
        LOOP DirDelLoop
  ;每次4字节,填4次,把绑定导入表删掉
  ;----- ENCRYPT DLL/API NAMES & SAVE IT & DESTROY IID's -----
  PUSH dwOrgITRVA
  PUSH pMem
  CALL RVA2Offset
  PUSH EAX
  PUSH pMem
  CALL ProcessOrgIT
  ;处理输入表的函数,加密,保存一些信息之类的
  OR   EAX, EAX
  .IF  ZERO?
     PUSH pMem
     CALL GlobalFree
     PUSH hFile
     CALL CloseHandle
     JMP  IIDErr
     ;处理输入表失败,退出
  .ENDIF  
     
  ;----- ADD THE PACKER SECTION -----
  push pMem
  call AddSection
  .IF eax == 0
     push pMem
     call GlobalFree
     push hFile
     call CloseHandle
     jmp NoRoom4SectionErr
     ;加入壳段,返回其在节表的结构的地址
  .ENDIF
  
  ;----- CREATE PACKER IMPORT TABLE -----
  xchg eax,esi          ; esi -> pointer to Section Header of the new section
  assume esi : ptr IMAGE_SECTION_HEADER
  mov eax,[esi].PointerToRawData
  MOV  dwNewSectionRO, EAX
  add eax,pMem
  push [esi].VirtualAddress
  push eax
  call AssembleIT
  ;也是跟输入表有关
  
  ;---- REPLACE TLS TABLE -----
  push [esi].VirtualAddress
  push pMem
  call ProcessTlsTable
  ;处理TLS表
  
  ;------ ENCRYPT THE SECTIONS -----
  pushad
  ; generate PER
  PUSH SEC_PER_SIZE
  PUSH OFFSET SecDecryptBuff
  PUSH OFFSET SecEncryptBuff
  CALL MakePER
  ;随机生成加密解密代码
  
  ; encrypt !
  mov eax,pMem
  mov ebx,0
  call CryptPE
  ;用生成的加密函数来加密各节
  popad
  
  ; ----- UPDATE PE HEADER -----
  mov edi,dwNTHeaderAddr
  assume edi : ptr IMAGE_NT_HEADERS    ; edi -> pointer to PE header
  ; ImportTable RVA ...
  push [esi].VirtualAddress
  pop [edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress
  ;新建的节开头作为输入表,放到数据目录输入表信息里
  ; EntryPoint...
  mov eax,[esi].VirtualAddress
  add eax,IT_SIZE
  mov [edi].OptionalHeader.AddressOfEntryPoint,eax
  ;输入表之后紧跟壳代码
  ; SizeOfImage ...
  mov eax,[esi].VirtualAddress
  add eax,[esi].Misc.VirtualSize
  mov [edi].OptionalHeader.SizeOfImage,eax
  ;不知道什么意思 先跳过
  
  ; save protection flags...
  push dwProtFlags
  pop PROTECTION_FLAGS
  ;不知道什么意思 先跳过
  
  assume esi : nothing
  assume edi : nothing
  
  ; ----- CALCULATE THE NEW EOF -----
  mov eax,dwNewSectionRO
  add eax,IT_SIZE
  add eax,DEPACKER_CODE_SIZE
  mov dwNewFileEnd,eax
   ;计算文件结尾位置
   
  ; ----- COPY LOADER CODE TO FILE MEMORY & DO CHECKSUM STUFF ------
  mov edi,dwNewSectionRO
  add edi,IT_SIZE
  add edi,pMem
  mov esi,offset DepackerCode
  mov ecx,DEPACKER_CODE_SIZE
  rep movsb
  ;将壳引导代码复制到新建的节里
  
  ;----- ENCRYPT OEP JUMP CODE -----
  MOV  EDI, pMem
  ADD  EDI, dwNewSectionRO
  ADD  EDI, IT_SIZE
  ADD  EDI, (OFFSET OEP_JUMP_CODE_START - OFFSET DepackerCode)
  MOV  ESI, EDI
  MOV  ECX, CRYPT_OEP_JUMP_SIZE
  XOR  EBX, EBX
   OepJumpEncryptLoop:
        LODSB
     ROR  AL, 2
     ADD  AL, BL
     XOR  AL, OEP_JUMP_ENCRYPT_NUM  
     STOSB
     INC EBX
     LOOP OepJumpEncryptLoop  
   ;把跳到OEP的代码加密掉

  ;----- ENCRYPT LOADER -----
  ; generate PER
  PUSH VAR_PER_SIZE
  MOV  EAX, pMem
  ADD  EAX, dwNewSectionRO
  ADD  EAX, IT_SIZE
  ADD  EAX, (OFFSET VarDecryptBuff - OFFSET DepackerCode)
  PUSH EAX
  PUSH OFFSET VarEncryptBuff
  CALL MakePER
  
  ; encryption !
  MOV  EDI, pMem
  ADD  EDI, dwNewSectionRO
  ADD  EDI, IT_SIZE
  ADD  EDI, (OFFSET LOADER_CRYPT_START - OFFSET DepackerCode)
  MOV  ECX, CRYPT_LOADER_SIZE_DB
  MOV  ESI, EDI  
   @@VarEncryptionLoop:
        LODSB
  VarEncryptBuff DB VAR_PER_SIZE DUP (0)
        STOSB
        LOOP @@VarEncryptionLoop
   ;加密壳引导代码
  ;----- CALCULATE CHECKSUM -----
  mov eax,pMem
  mov ecx,dwNewFileEnd
  sub ecx,CHECKSUM_SKIP_SIZE
  call GetChecksum
  mov dwOrgChecksum,eax
  ;计算校验和

  ;----- PASTE CHECKSUM ------
  MOV EAX, pMem
  ADD EAX, IT_SIZE
  ADD EAX, dwNewSectionRO
  ADD EAX, CHECKSUM_ADDR
  MOV EDX, dwOrgChecksum
  MOV DWORD PTR [EAX], EDX
  ;把校验和放到壳节里

  ; ----- WRITE FILE MEMORY TO DISK -----
  invoke SetFilePointer,hFile,0,NULL,FILE_BEGIN
  invoke WriteFile,hFile,pMem,dwOutPutSize,offset dwBytesWritten,NULL
  ;写入文件
  
  ; ------ FORCE CALCULATED FILE SIZE ------
  invoke SetFilePointer,hFile,dwNewFileEnd,NULL,FILE_BEGIN
  invoke SetEndOfFile,hFile
  ;不知道为什么写入文件之后会多出一些数据,所以要用之前计算好的文件结束位置来截取文件前部分的内容。
  
  invoke MessageBox,hDlg,offset szDone,offset szDoneCap,MB_ICONINFORMATION

  ; ----- CLEAN UP -----
  push pMem
  call GlobalFree
  push hFile
  call CloseHandle
@@Exit:
  ret
  
;----- ERROR MESSAGES -----
MemErr:
  mov eax,offset szNoMemErr
  jmp ShowErr
PEErr:
  mov eax,offset szNoPEErr
  jmp ShowErr
FileErr:
  mov eax,offset szFileErr
  jmp ShowErr
  
NoRoom4SectionErr:
  mov eax,offset szNoRoom4SectionErr
  jmp ShowErr  
FsizeErr:
  mov eax,offset szFsizeErr
  jmp ShowErr
  
SecNumErr:
  mov eax,offset szSecNumErr
  jmp ShowErr
  
IIDErr:
  MOV  EAX, OFFSET szIIDErr
  JMP  ShowErr
  
ShowErr:
  invoke MessageBox,hDlg,eax,offset szErr,MB_ICONERROR
  jmp @@Exit
CryptFile ENDP


很诚实的说,我自己对这个文件的代码都还有点迷惑。要重新看一次和进去各个子程序看看才会明白。分析完整个源码之后再在OD里面感受一下壳的流程
下面将CryptFile函数调用的子程序都看一下(基本上按顺序)

InitRandom PROC
  ; manage the random generator 
  CALL GetTickCount
  PUSH EAX
  CALL srand
  RET
InitRandom ENDP

置时间为随机数种子,一遍之后的rand函数可以产生随机数

RVA2Offset PROC USES ebx ecx edx, Base : DWORD,dwITRVA : DWORD
  ; get the pointer to the NT header
  mov eax,Base
  add eax,[eax+03Ch]
  invoke ImageRvaToSection,eax,Base,dwITRVA
  test eax,eax
  jz @@ExitProc
  
  xchg eax,ebx
  assume ebx : ptr IMAGE_SECTION_HEADER
  mov eax,dwITRVA
  sub eax,[ebx].VirtualAddress
  add eax,[ebx].PointerToRawData    
  assume ebx : nothing
@@ExitProc:
  ret
RVA2Offset ENDP


先确定RVA在哪个节中,计算出这个地址离节的距离是多少
然后将这个距离加在节的文件偏移上从而得到该RVA的文件偏移
奇怪的是我竟然无法在源文件里面搜索到ImageRvaToSection这个函数,forget it.


ProcessOrgIT PROC USES edi esi edx, pFileImage : LPVOID, pITBaseRO : LPVOID
  LOCAL dwIIDNum : DWORD

  ; clear the IIDInfo array
  XOR EAX,EAX
  MOV EDI, OFFSET IIDInfo
  MOV ECX, SIZEOF IIDInfo
   ClearArrayLoop:
        STOSB
        LOOP ClearArrayLoop
  ;清空结构数组IIDInfo,其定义如下:
        ;sItInfo STRUCT
            ;DllNameRVA       dd ?
            ;FirstThunk       dd ?
            ;OrgFirstThunk    dd ?
        ;sItInfo ENDS

  ; get a random number
  INVOKE GetTickCount
  XOR EAX, ("yoda")
  MOV EDX,EAX              ; EDX -> stupid number :)
  ;用当前时间和"yoda"xor产生一个随机数放在edx

  ; start
  MOV  dwIIDNum, 0
  MOV  EDI,pITBaseRO
  ;输入表的文件偏移
  ADD  EDI,pFileImage
  ASSUME EDI : PTR IMAGE_IMPORT_DESCRIPTOR      ; EDI -> IID
  MOV ESI,OFFSET IIDInfo
  ASSUME ESI : PTR sItInfo          ; ESI -> Loder IT data array
  .WHILE [EDI].Name1
        ;最后一个IID全部为0
     ; too much IID's ?
     INC  dwIIDNum
     .IF dwIIDNum == (MAX_IID_NUM)
         XOR  EAX, EAX
         JMP  POIT_Exit
         ;遍历完每个IID项,退出
     .ENDIF
     
     ; save IID Infos
     PUPO <[EDI].Name1>, <[ESI].DllNameRVA>
     PUPO <[EDI].OriginalFirstThunk>, <[ESI].OrgFirstThunk>
     PUPO <[EDI].FirstThunk>, <[ESI].FirstThunk>
           ;保存信息,从左到右     

     ;-> get dll pointer
     PUSH  [EDI].Name1
     PUSH  pFileImage  
     CALL  RVA2Offset
     ADD   EAX, pFileImage
     ;-> crypt string
     CALL EnDeCryptString
       ;加密DLL名,简单的移位加密而已就不看了
       ;--- CRYPT API name strings ---
       PUSH ESI
       MOV  ESI, [EDI].OriginalFirstThunk
       .IF !ESI
          MOV ESI, [EDI].FirstThunk
        ;有的PE文件没有OriginalFirstThunk
       .ENDIF
       PUSH ESI
       PUSH pFileImage
       CALL RVA2Offset
       MOV  ESI, EAX
       ADD  ESI, pFileImage
       .WHILE DWORD PTR [ESI]  ; ESI -> Thunk pointer
          MOV  EAX, [ESI]
          ; is it an Ordinal Import ?
        TEST EAX,IMAGE_ORDINAL_FLAG32
        JNZ  SkipApiString
        ;以序号输入则跳
          PUSH EAX
          PUSH pFileImage
          CALL RVA2Offset
          OR   EAX, EAX
          JZ   SkipApiString
          ADD  EAX, pFileImage
          ADD  EAX, 2    ; skip the HINT
        ;跳过序号,加密后面的字符串
          CALL EnDeCryptString
   SkipApiString:          
          ADD  ESI, 4
       .ENDW
       POP ESI
       
       ; destroy Original IID
       MOV [EDI].Name1, EDX
       MOV [EDI].OriginalFirstThunk, EDX
       MOV [EDI].FirstThunk, EDX
       MOV [EDI].TimeDateStamp, EDX
       MOV [EDI].ForwarderChain, EDX
     ;用刚才产生的随机数把原来的IDD填充掉
        
     ; EDI -> point to next IID     
     ADD EDI,SIZEOF IMAGE_IMPORT_DESCRIPTOR
     ADD ESI,SIZEOF sItInfo
  .ENDW
  ASSUME ESI : NOTHING
  ASSUME EDI : NOTHING
  XOR  EAX, EAX
  INC  EAX
   POIT_Exit:
  RET
ProcessOrgIT ENDP
基本上就是将原来的输入表信息保存起来,然后是擦除原输入表的信息

AddSection PROC USES edi esi ebx ecx edx, pMem_ : LPVOID
  LOCAL dwSecNum    : DWORD

  mov edi,pMem_
  add DWORD PTR edi,[edi+03Ch]
  assume edi : ptr IMAGE_NT_HEADERS      ; edi -> pointer to PE header
  
  ; check whether there's room for a new section
  xor eax,eax
  mov ax,[edi].FileHeader.NumberOfSections
  mov dwSecNum,eax
  mov ecx,SIZEOF IMAGE_SECTION_HEADER
  imul eax,ecx                                           ; eax contains the size of the whole section header
  add eax,SIZEOF IMAGE_SECTION_HEADER                    ; add the size being needed for our new section
  mov ecx,edi                 ; ecx -> address of PE Header  
  sub ecx,pMem_                 ; sub ecx Map Base
  add ecx,eax                 ; ecx + calculated header sizes  
  add ecx,0F8h                 ; add the size of the PE header
        ;从dos头到节表最后一个节信息的大小相加
  .IF ecx > [edi].OptionalHeader.SizeOfHeaders
        ;可选头是从dos头到节表最后一个节信息的大小相加,但是后面还会有一大堆0,那是因为要对齐
  ;所以添加新节是否可能就看对齐值给你留的0够不够
     xor eax,eax
     jmp @@ExitProc_AS
  .ENDIF
  
  ; create a new section
  mov esi,edi
  add esi,0F8h
  assume esi : ptr IMAGE_SECTION_HEADER    ; esi -> pointer to section headers
  ; go to the last section
  mov edx,dwSecNum
  sub edx,1
  .REPEAT
     ; force the writeable flag
     mov eax,[esi].Characteristics
     or eax,080000000h
     mov [esi].Characteristics,eax

     add esi,SIZEOF IMAGE_SECTION_HEADER
     dec edx
  .UNTIL edx == 0
  ;将每个节表的属性改完可写
  ; start to build the new section
  mov edx,esi
  add edx,SIZEOF IMAGE_SECTION_HEADER    ; edx -> pointer to the new section
  assume edx : ptr IMAGE_SECTION_HEADER
  
  ; VirtualAddress...
  mov eax,[esi].VirtualAddress
  add eax,[esi].Misc.VirtualSize
  push 01000h
  push eax
  ;eax=最后一个段的结尾
  call PEAlign
  ;得到将eax对齐之后的地址,即新节的地址
  mov [edx].VirtualAddress,eax
  
  ; VirtualSize..
  mov [edx].Misc.VirtualSize,02000h
  ;新节大小2000h
  
  ; RawSize..
  mov eax,IT_SIZE
  add eax,DEPACKER_CODE_SIZE
  mov [edx].SizeOfRawData,eax
        ;SizeOfRawData本来应该是对齐之后的大小,而这里直接把数据相加,
  ;可能因为这是最后一个节  所以无所谓吧
  
  ; Section name
  lea  eax,[edx].Name1
  push DEPACKER_SECTION_NAME
  pop  [eax]
  MOV  DWORD PTR [EAX+4],0
  ;将yC字符串放到eax指向的内存处3
  
  ; Characteristics
  mov [edx].Characteristics,0E00000E0h
  
  ; RawOffset
  mov eax,[esi].PointerToRawData
  add eax,[esi].SizeOfRawData
  push 0200h
  push eax
  call PEAlign
  ;刚才的一个PEAlign是为了内存对齐,现在的是为了文件对齐,所以对齐值不同
  mov [edx].PointerToRawData,eax
  mov eax,edx          ; eax -> will be returned
  ;eax指向新节在节表的位置
  ;这个Add
Section还没把数据放到节里面,后面会把相关的输入表,壳引导代码等放进去的
  ; update the PE header
  inc [edi].FileHeader.NumberOfSections
  
  assume edx : nothing
  assume esi : nothing
  assume edi : nothing  
@@ExitProc_AS:
  ret
AddSection ENDP


AssembleIT这个函数是在为壳引导代码的运行提供一个输入表, 这个输入表将被当做系统填写IAT的根据, 而且只包含了一个动态链接库中的两个函数
就是LoadLibrary和GetProcAdress
这个函数里的后面我没有写注释了, 比较难组织语言,看后面的解释吧
AssembleIT PROC USES ebx ecx edx esi edi, pAddress4IT : LPVOID, dwNewSectionVA : DWORD

  mov esi,pAddress4IT          
   ; esi -> base of the new IT    
  mov eax,pAddress4IT
  mov ecx,IT_SIZE
   ZeroMem:  
  mov byte ptr [eax],0
  inc eax
  loop ZeroMem
  ;新节处的IT位置清0  
  
  ; 创建一个新的输入表 :)
  mov ebx,esi
  ;ebx=esi->新的输入表=01281C20
  mov eax,SIZEOF IMAGE_IMPORT_DESCRIPTOR
  ;eax=IID结构大小=14h
  xor edx,edx
  mov ecx,2
  ;有两个IID 一个Kernel 一个空的
  mul ecx
  ;eax=14h*2=28h
  ;eax=IID总大小=28h
  add ebx,eax
  ;ebx=IID结尾处
  assume esi:ptr IMAGE_IMPORT_DESCRIPTOR
  mov eax,ebx                                            ; process the IID Name
  ;eax=IID结尾处
  sub eax,esi
  ;eax=IID总大小=28h
  add eax,dwNewSectionVA
  ;eax=新节(即壳输入表)RVA+IID总大小
  mov [esi].Name1,eax
  ;esi指向第一个IID,将IID结尾处地址传入第一个IID的Name1字段(那么IID结尾应该紧跟dll名吧)
  push esi
  ;保存esi
  mov esi,offset szKernel
  mov edi,ebx
  .REPEAT
     lodsb
     stosb
  .UNTIL byte ptr [esi] == 0
  ;把esi指向的字符串放到edi指向的空间
  pop esi
  mov ebx,edi
  inc ebx
  mov eax,ebx                                            ; process the FirstThunk pointers
  ;eax=ebx指向IID后面的字符串+1的位置A
  sub eax,esi
  ;eax=位置A离IID起始的偏移
  add eax,dwNewSectionVA
  ;eax=位置A的RVA
  mov [esi].FirstThunk,eax
  ;用FirstThunk来存放这个位置的RVA就是说这里是一个IMAGE_THUNK_DATA数组
  mov edx,ebx
  add edx,10
  ;留出空间给Image_Thunk_Data
  mov eax,edx
  sub eax,esi
  add eax,dwNewSectionVA
  mov [ebx],eax
  add edx,2
  push esi
  mov esi,offset szLoadLibrary
  mov edi,edx
  .REPEAT
     lodsb
     stosb
  .UNTIL byte ptr [esi] == 0
  pop esi
  mov edx,edi
  add ebx,4
  mov eax,edx
  sub eax,esi
  add eax,dwNewSectionVA
  mov [ebx],eax
  add edx,2
  mov esi,offset szGetProcAddress
  mov edi,edx
  .REPEAT
     lodsb
     stosb
  .UNTIL byte ptr [esi] == 0  
  assume esi : nothing
  ret
AssembleIT ENDP

当Assemble函数整个执行完之后,新节, 也就是新输入表的内存视图如下


前面的四十个字节分别是一个kernel.dll的IID和一个空的IID
Name1        [128102C]存放1281048的RVA形式
                     [1281048]到[1281053]存放"kernel32.dll"

然后FirstThunk存放了1281055的RVA形式
之后每拷贝一个函数名之前都会计算出  (函数名-2)位置的RVA形式,依次放在FirstThunk指向的地方(即含有两个IMAGE_THUNK_DATA结构的数组)
在这里结尾是以一个全为0的IMAGE_THUNK_DATA结构(正如我们一般所知道的一样),但是在这四个0字节之后直接就到 "LoadLibraryA" 了,这样的话我觉得也
可以啦,因为系统的PEloader是根据IMAGE_THUNK_DATA找到对应的函数字符串的,所以其Hint和结尾的IMAGE_THUNK_DATA应该不会影响程序加载

下面是ProcessTlsTable函数,相对简单


ProcessTlsTable PROC USES edi ebx esi ecx, pFileMem : LPVOID, CryptSectionVA : DWORD
  LOCAL  pTlsDirAddr : LPVOID
  ;其实我也不懂Tls,没看这部分内容,不过这里也不是关于Tls表的结构的
  ;检查是否有tls表
  mov edi,pFileMem
  add edi,[edi+03Ch]        ; edi -> pointer to PE header
  assume edi : ptr IMAGE_NT_HEADERS
  lea ebx,[edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY * 9].VirtualAddress
  mov pTlsDirAddr,ebx
  mov ebx,[ebx]
  assume edi : nothing
  cmp ebx,0           
  ;tls's RVA=0,无tls表
  jz ExitTlsFixProc
  
  ; get a RAW pointer to the tls table
  push ebx
  push pFileMem
  call RVA2Offset
  cmp eax,0
  jz ExitTlsFixProc
  mov esi,pFileMem
  add esi,eax          
  ; esi -> Tls表的文件偏移
  
  ; copy the whole TLS table into the loader data part
  mov edi,offset TlsBackup
  mov ecx,sizeof IMAGE_TLS_DIRECTORY32
  rep movsb
  ;把esi指向的tls表移到TlsBackup
  ;这里的TlsBackup是壳引导代码里的变量
  
  ; fix the TLS DIRECTORY VA
  mov eax,CryptSectionVA
  add eax,IT_SIZE
  add eax,TLS_BACKUP_ADDR
  ;eax=TLS表的位置
  ;TLS_BACKUP_ADDR         equ (offset TlsBackupLabel - offset DepackerCode)
  ;TlsBackupLabel:
  ;TlsBackup               IMAGE_TLS_DIRECTORY32 <0>
  mov esi,pTlsDirAddr
  mov [esi],eax
  ;将节表的TLS指针修正为在壳段的Tls表(壳段的Tls表并没有加密)
  ExitTlsFixProc:
  ret
ProcessTlsTable ENDP


因为有一些数据结构在MakePER里出现得较多,所以我整个PER.ASM文件一起贴在这里了,重点看MakePER函数吧

; -> Polymorphic En-/Decryption routine generator for per byte encryption <-
; by yoda

;---- STRUCTs ----
sPERTable STRUCT
  dwSize           DD ?
  dwEncrypt        DD ?
  dwDecrypt        DD ?
  RandNumType      DD ?
sPERTable ENDS

; RandNumType:
; 0 - no random num needed
; 1 - 3th byte must be a random number
; 2 - 2nd byte must be a random number

;----- EQUs -----
;PERTable项数
PERItems                   EQU 14

;----- CONST ----
.CONST
;机器码是逆序摆放的
PERTable                   DD 1
         DD 090h        ; NOP
         DD 090h        ; NOP
         DD 0
         
         DD 1
         DD 0F9h        ; STC
         DD 0F9h        ; STC
         DD 0
         
         DD 1
         DD 0F8h        ; CLC
         DD 0F8h        ; CLC
         DD 0         

         DD 2
         DD 0C0FEh        ; INC  AL
         DD 0C8FEh                            ; DEC  AL
         DD 0
         
         DD 2
         DD 00004        ; ADD AL, 0
         DD 0002Ch        ; SUB AL, 0
         DD 2

         DD 2
         DD 0002Ch        ; SUB AL, 0
         DD 00004        ; ADD AL, 0
         DD 2
         
         DD 2
         DD 0C102h        ; ADD AL, CL
         DD 0C12Ah        ; SUB AL, CL
         DD 0

         DD 2
         DD 0C12Ah        ; SUB AL, CL
         DD 0C102h        ; ADD AL, CL
         DD 0

         DD 2
         DD 00034h        ; XOR AL, 0
         DD 00034h        ; XOR AL, 0
         DD 2

         DD 3
         DD 000C8C0h                          ; ROR  AL, 0
         DD 000C0C0h        ; ROL  AL, 0
         DD 1

         DD 3
         DD 000C0C0h        ; ROL  AL, 0
         DD 000C8C0h                          ; ROR  AL, 0
         DD 1
         
         DD 3
         DD 0E801EBh        ; Self modifing
         DD 0E801EBh        ; Self modifing
         DD 0
         
         DD 3
         DD 0E901EBh        ; Self modifing
         DD 0E901EBh        ; Self modifing
         DD 0
         
         DD 3
         DD 0C201EBh        ; Self modifing
         DD 0C201EBh        ; Self modifing
         DD 0         

.DATA
dwRandVal                  DD 0

.CODE

; srand should only called one time !!!
InitRandom PROC
  ; manage the random generator 
  CALL GetTickCount
  PUSH EAX
  CALL srand
  RET
InitRandom ENDP

MakePER PROC pEncryptBuff : LPVOID, pDecryptBuff : LPVOID, dwSize : DWORD
  
  ;当前随机数
  LOCAL dwCurRandNum : DWORD
  ;准备一些东西
  MOV  EDI, pEncryptBuff          ; EDI -> EncryptBuffer
  MOV  ESI, pDecryptBuff          ; ESI -> DecryptBuffer
  ADD  ESI, dwSize          ; ESI will be filled from down to top
  
  ; generate !
  .REPEAT   
     ;随机抽取一个随即项(一共有14个,每项是两条OPCODE)
     PUSH PERItems
     CALL rand
     MOV  EBX, SIZEOF sPERTable
     XOR  EDX, EDX
     MUL  EBX
     ADD  EAX, OFFSET PERTable
     XCHG EAX, EDX             
     ; EDX -> 指向一个项A的起始地址
     
     ASSUME EDX : PTR sPERTable
     
     ;壳里面提供了一段内存,项A的OPCODE大小不能大于这段内存
     MOV  EBX, [EDX].dwSize
     CMP  EBX, dwSize
     JG   Retry
     
    ;将项A的OPCODE代码拷贝到EDI指向的内存区域去(即pEncryptBuff处)
    ;每次向pEncryptBuff放一个OPCODE,直到dwSize用完为止
     MOV  ECX, [EDX].dwSize
     MOV  EAX, [EDX].dwEncrypt     
     .WHILE ECX != 0
         MOV  BYTE PTR [EDI], AL
         ADD  EDI, 1
         ROR  EAX, 8
         DEC  ECX
     .ENDW

     ;产生随机数
     MOV  EAX, [EDX].RandNumType
     .IF  EAX == 1 || EAX == 2
         MOV  EBX, EDI
         SUB  EBX, 1
         ;EBX指向pEncryptBuff的最后一个字符
         PUSH 0F8h
         CALL rand
         INC  EAX        ; avoid 0 !
         ;产生一个小于F8 大于0的随机数
         MOV  dwCurRandNum, EAX
         ;将这个随机数放在pEncryptBuff的最后一个字符上(最后一个字符通常是0)
         MOV  BYTE PTR [EBX], AL
     .ENDIF     
 
     ;刚才放了一个OPCODE(最后一个字符经过/不经过随机),占了一点空间,相减一下
     MOV  EAX, [EDX].dwSize
     SUB  dwSize, EAX
     
     ;根据ecx-1次数执行ror eax,8
     MOV  ECX, [EDX].dwSize
     MOV  EAX, [EDX].dwDecrypt     
     SUB  ECX, 1
     .WHILE ECX != 0
          ROR  EAX, 8
          DEC  ECX
     .ENDW

    ;从后到前将刚才的随机数用下面的算法填充到pDecryptBuff
     MOV  ECX, [EDX].dwSize
     .WHILE ECX != 0
         SUB  ESI, 1 
         MOV  BYTE PTR [ESI], AL
         ROL  EAX, 8
         DEC  ECX
     .ENDW
     
     ;根据RandNumType分别在esi+1和esi+2的地方放入当前的随机数dwCurRandNum
     MOV  EAX, [EDX].RandNumType
     .IF  EAX == 1
         MOV  EBX, ESI
         ADD  EBX, 2
         MOV  EAX, dwCurRandNum
         MOV  BYTE PTR [EBX], AL
         
     .ELSEIF EAX == 2
         MOV  EBX, ESI
         ADD  EBX, 1
         MOV  EAX, dwCurRandNum
         MOV  BYTE PTR [EBX], AL  
            
     .ENDIF     
     
     ASSUME EDX : NOTHING
     
       Retry:
  .UNTIL dwSize == 0  
  RET
MakePER ENDP
  
rand PROC USES edx ebx, dwRange : DWORD
  MOV  EAX, dwRandVal
  
  ; save new random number
  ADD  EAX, 0567h
  ROL  EAX, 1
  MOV  dwRandVal, EAX
  
  ; get new random number
  XOR  EDX, EDX
  MOV  ECX, 32
    BitLoop:
      SHR   EAX, 1
      .IF   CARRY?
          XOR  EAX, 013245769h
      .ENDIF
  LOOP BitLoop
  
  ; force dwRange
  XOR  EDX, EDX
  MOV  EBX, dwRange
  DIV  EBX
  
  XCHG EAX, EDX

  RET
rand ENDP

srand PROC dwRandNum : DWORD
  MOV  EAX, dwRandNum
  MOV  dwRandVal, EAX
  RET
srand ENDP


用MakePER生成了加密/解密函数之后,下面用CryptPE将各个节加密

;从CryptPE标号看起!!
; esi = CryptStart
; ecx = CryptSize
EncryptSec:
  mov edi,esi
  ;edi是当前节在mapping中的位置
SecEncryptLoop:
  LODSB
  ;节数据放进al
  SecEncryptBuff DB SEC_PER_SIZE DUP (0)
  ;加密al
  STOSB
  ;放回去节里
  LOOP SecEncryptLoop
  RET

省略....

DepackerCode:

省略....

CryptPE:
  mov edi,eax
  add edi,[edi+3Ch]
  assume edi : ptr IMAGE_NT_HEADERS    ; edi -> PE header
  mov esi,edi
  add esi,0F8h
  assume esi : ptr IMAGE_SECTION_HEADER    ; esi -> Section header
  xor edx,edx
  .REPEAT
     
     ; -> 跳过一些特殊区段
     .IF dword ptr [esi].Name1 == ('crsr')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('rsr.')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('oler')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('ler.')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('Cy')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('ade.')
        jmp @@LoopEnd
     .ENDIF
     .IF [esi].PointerToRawData == 0 || [esi].SizeOfRawData == 0
        jmp @@LoopEnd
     .ENDIF
   
     ;-> en-/decrypt it
      pushad
     mov ecx,[esi].SizeOfRawData
     .IF ebx == 0        ; (ebx is a parameter)
        mov esi,[esi].PointerToRawData
        ADD ESI, EAX
        CALL EncryptSec
        ;因为现在这部分是DepackerCode的内容,而EncryptSec是加密的,所以EncryptSec不在DepackerCode标号内而在上面的地方
     .ELSE
        mov  esi,[esi].VirtualAddress
        add  esi,eax
        CALL DecryptSec
     .ENDIF

     JMP SecDecryptContinue1
     
  ; esi = CryptStart
  ; ecx = CryptSize
  ;解密函数,壳运行的时候用
  DecryptSec:
    mov edi,esi
  SecDecryptLoop:
    LODSB
    SecDecryptBuff DB SEC_PER_SIZE DUP (0)
    STOSB
    LOOP SecDecryptLoop
    RET
    
  SecDecryptContinue1:     
     popad
     
     @@LoopEnd:   
     add esi,SIZEOF IMAGE_SECTION_HEADER
     INC EDX
  .UNTIL dx == [edi].FileHeader.NumberOfSections
  assume esi : nothing
  assume edi : nothing
  ret

最后来一个在CryptFile最后用到的校验和计算函数:
  ; start calculation
   CheckSumLoop:
        mov al,byte ptr [edi]
        mul edx
        add ebx,eax        
        inc edx
       inc edi     
     loop CheckSumLoop
     xchg eax,ebx    ; eax -> checksum
     RET
     ;-> End of GetChecksum


至此只剩下DepackerCode未分析了,就快完成(其实上面的代码也在DepackerCode标号里)
DepackerCode:
  pushad

  ; get base ebp
  call CallMe
  CallMe:
  pop ebp
  sub ebp,offset CallMe

如果这段代码是独立编译的话,看起来好像很奇怪,因为offset CallMe本来就等于ebp的值了啊,相减的话不等于0吗,等于0有什么用?(解释一下pop ebp获取到call下一行的代码)
但是这段程序不是独立编译的,而是放到其他软件上面运行,所以offset CallMe是本来的地址,而ebp中获取到的是现在的地址
看看OD反汇编代码:
00440060 Z>  60               pushad
00440061     E8 00000000      call ZR.00440066
00440066     5D               pop ebp
00440067     81ED F31D4000    sub ebp,ZR.00401DF3

两个相减得到壳加在软件身上产生的偏差,得到这个偏差之后,如果以后要访问其他的变量就可以这么做
lea edi,[ebp+401E3B]

(401E3B是一个编译器生成的常量的地址)

  ;----- DECRYPT LOADER VARIABLES -----
  MOV ECX, CRYPT_LOADER_SIZE_DB
  LEA EDI, [EBP+OFFSET LOADER_CRYPT_START]
  MOV ESI, EDI
   VarDecryptionLoop:
        LODSB
  VarDecryptBuff DB VAR_PER_SIZE DUP (0)
        STOSB
        LOOP VarDecryptionLoop

这个在之前的代码经常见了,用来解密壳代码(壳从LOADER_CRYPT_START开始的代码)
LOADER_CRYPT_START:
  ;------ DETECT WinNT ------
  MOV  EAX, [ESP+020h]
  ;EAX指向KERNEL.DLL中的一个地址,其上一个地址的代码是7C817074     FF55 08          call dword ptr ss:[ebp+8]
  ;很明显我们的PE文件是在这个CALL里面执行的,猜测作者可能是看过windows其他系列OS程序第一条指令时esp指向的地址吧,所以拿这个来做依据很多人都不明白
  INC  EAX
  JS   NoNT
  MOV  DWORD PTR [EBP+bNT], 1
  ;如果是NT的话要标明,如果不是的话后面有一些地方是不同的。发现这个作者很细腻的,面面俱到啊
   NoNT:  

        ;------ 得到壳装载代码的CRC ------
        LEA  EAX, [EBP+OFFSET DepackerCode]
        MOV  ECX, LOADER_CRC_CHECK_SIZE
        CALL GetChecksum
        MOV  [EBP+dwLoaderCRC], EAX        
  
  ;----- SI Check 1 -----
  MOV EAX, [ebp+PROTECTION_FLAGS]
  AND EAX, CHECK_SI_FLAG
  jz SkipSICheck
  ;如果没有选中 检查softice选项 则跳。

------------------------------------------------------------------
花了一天时间学习了一下SEH,继续学习yoda壳!
  ; install SEH frame
  LEA  ESI,[EBP+SEH]
  ASSUME ESI : PTR sSEH
  LEA  EAX, [EBP+OFFSET SICheck1_SP]
  mov  [ESI].SaveEip, EAX
   ASSUME ESI : NOTHING
  ;将SICheck1_SP地址存在SEH结构的SaveEip中
  
  MOV  EDI, EBP
  LEA  EAX, [EBP+OFFSET SehHandler1]
  ;EAX=异常处理程序的地址
  XOR  EBX, EBX
  push EAX
  push FS:[EBX]
  mov  FS:[EBX], ESP
  ;似乎意图要把安装SEH的代码伪装一下 呵呵
  
  ; 0 - SI not found
  ; 1 - SI found
     mov     ebp, 04243484Bh
      mov     ax, 04h
      JMP     SM1
      DB 0FFh
  SM1:
        INT  3
      
   SICheck1_SP:
  MOV  EBP, EDI
  ; uninstall SEH frame
  XOR  EBX, EBX
   POP  FS:[EBX]
   ADD  ESP, 4   
     
     .IF AL != 4
        ;AL不等于4则有SoftIce,猜想下
         ; exit
         JMP SM2
           DB 0E9h
   SM2:    popad
     ret
      .ENDIF
  SkipSICheck:
  
  ;----- GET BASE API ADDRESSES -----
  ; find the ImageImportDescriptor and grab dll addresses
  mov eax,[ebp+dwImageBase]
  add eax,[eax+03Ch]
  add eax,080h
  mov ecx,[eax]                            ; ecx contains the VirtualAddress of the IT
  add ecx,[ebp+dwImageBase]
  add ecx,16                               ; ecx points to the FirstThunk address of the IID
  mov eax,dword ptr [ecx]
  add eax,[ebp+dwImageBase]
  mov ebx,dword ptr [eax]
  mov [ebp+_LoadLibrary],ebx
  add eax,4
  mov ebx,dword ptr [eax]
  mov [ebp+_GetProcAddress],ebx  
  
  ;----- GET ALL OTHER API ADDRESSES -----
  ; get kernel base
  lea eax,[ebp+offset szKernel32]
  push eax
  call [ebp+_LoadLibrary]
  mov esi,eax          ; esi -> kernel base
  MOV [EBP+dwKernelBase], EAX

  ;-> GetModuleHandle
  lea eax,[ebp+szGetModuleHandle]
  call DoGetProcAddr
  mov [ebp+_GetModuleHandle],eax  
  
  ;-> VirtualProtect
  lea eax,[ebp+szVirtualProtect]
  call DoGetProcAddr
  mov [ebp+_VirtualProtect],eax  
  
  ;-> GetModuleFileName
  lea eax,[ebp+szGetModuleFileName]
  call DoGetProcAddr
  mov [ebp+_GetModuleFileName],eax
  
  ;-> CreateFile
  lea eax,[ebp+szCreateFile]
  call DoGetProcAddr
  mov [ebp+_CreateFile],eax
  
  ;-> GlobalAlloc
  lea eax,[ebp+szGlobalAlloc]
  call DoGetProcAddr
  mov [ebp+_GlobalAlloc],eax
  
  ;-> GlobalFree
  lea eax,[ebp+szGlobalFree]
  call DoGetProcAddr
  mov [ebp+_GlobalFree],eax
  
  ;-> ReadFile
  lea eax,[ebp+szReadFile]
  call DoGetProcAddr
  mov [ebp+_ReadFile],eax
  
  ;-> GetFileSize
  lea eax,[ebp+szGetFileSize]
  call DoGetProcAddr
  mov [ebp+_GetFileSize],eax
  
  ;-> CloseHandle
  lea eax,[ebp+szCloseHandle]
  call DoGetProcAddr
  mov [ebp+_CloseHandle],eax
  
  ; FUNNY JUMP :)
  LEA EAX, [EBP+OFFSET LoaderContinue1]
  PUSH EAX
  RET
  
; it's in an own function to keep a the loader code small
; eax = address of API string
; esi = target dll base  
DoGetProcAddr:
  push eax
  push esi
  call [ebp+_GetProcAddress]
  ret

LoaderContinue1:
  ;----- ANTI DUMP -----
  test [ebp+PROTECTION_FLAGS],ANTI_DUMP_FLAG
  jz LetDumpable
  
        push    fs:[30h]
        pop     eax
        TEST    EAX, EAX
        JS      fuapfdw_is9x     ; detected Win 9x
   fuapfdw_isNT:
        MOV     EAX, [EAX+0Ch]
        MOV     EAX, [EAX+0Ch]
        MOV     DWORD PTR [EAX+20h], 1000h ; increase size variable
        JMP     fuapfdw_finished
   fuapfdw_is9x:
        PUSH    0
        CALL    [ebp+_GetModuleHandle]
        TEST    EDX, EDX
        JNS     fuapfdw_finished      ; Most probably incompatible!!!
        CMP     DWORD PTR [EDX+8], -1
        JNE     fuapfdw_finished      ; Most probably incompatible!!!
        MOV     EDX, [EDX+4]          ; get address of internaly used
                                      ; PE header
        MOV     DWORD PTR [EDX+50h], 1000h ; increase size variable
   fuapfdw_finished:

   LetDumpable:
  
  ;---- GET HEADER WRITE ACCESS -----
  mov edi,[ebp+dwImageBase]
  add edi,[edi+03Ch]
  assume edi : ptr IMAGE_NT_HEADERS      ; edi -> pointer to PE header
  mov esi,[ebp+dwImageBase]
  mov ecx,[edi].OptionalHeader.SizeOfHeaders
  assume edi : nothing
    
  ; fix page access
  lea eax,[ebp+Buff]
  push eax
  push PAGE_READWRITE
  push ecx
  push [ebp+dwImageBase]
  call [ebp+_VirtualProtect]
  
  
  ;----- CALCULATE CRC -----
  test [ebp+PROTECTION_FLAGS],CHECK_HEADER_CRC
  jz DontCheckCRC

  ; get the calling exe filename
  push MAX_PATH
  lea edi,[ebp+Buff]
  push edi      ; edi -> filename
  push 0
  call [ebp+_GetModuleFileName]
  
  ; map it...
  push 0
  push FILE_ATTRIBUTE_NORMAL
  push OPEN_EXISTING
  push NULL
  push FILE_SHARE_READ
  push GENERIC_READ
  push edi
  call [ebp+_CreateFile]
  .IF eax == INVALID_HANDLE_VALUE
     xor eax,eax
     jmp SkipChecksumCalc
  .ENDIF
  mov edi,eax      ; edi -> file handle
  
  push NULL
  push edi
  call [ebp+_GetFileSize]
  sub eax,CHECKSUM_SKIP_SIZE
  xchg eax,esi      ; esi -> filesize
  
  push esi
  push GMEM_FIXED+GMEM_ZEROINIT
  call [ebp+_GlobalAlloc]
  .IF eax == NULL
     jmp SkipChecksumCalcAndCleanUp
  .ENDIF
  xchg eax,ebx      ; ebx -> mem base
  
  push NULL
  lea eax,[ebp+Buff]
  push eax
  push esi
  push ebx
  push edi
  call [ebp+_ReadFile]
  
  ; get the checksum
  mov eax,ebx
  mov ecx,esi
  PUSH EBX  ; [ESP] -> hMem
  PUSH EDI        ; EDI = hFile
  
  CALL GetChecksum
  mov [ebp+dwCalcedCRC],eax
  
  POP  EDI
  POP  EBX
  ; the calculated CRC will be compared at the start of the InitIT function >:-)
  LEA  EAX, [EBP+OFFSET AfterCRCCalcContinue]
  PUSH EAX
  RET

  ;-> Start of GetChecksum
   GetChecksum:
  ; eax = file image base
  ; ecx = filesize  
  mov edi,eax            ; edi -> data pointer
  xor eax,eax            ; eax -> current bytes
  xor ebx,ebx            ; ebx -> current checksum
  xor edx,edx            ; edx -> Position (zero based)
  
  ; start calculation
   CheckSumLoop:
        mov al,byte ptr [edi]
        mul edx
        add ebx,eax        
        inc edx
       inc edi     
     loop CheckSumLoop
     xchg eax,ebx    ; eax -> checksum
     RET
     ;-> End of GetChecksum

   AfterCRCCalcContinue:
  
  ; clean up
  PUSH EBX
  call [ebp+_GlobalFree]
  xchg esi,eax

  SkipChecksumCalcAndCleanUp:  
  push eax
  push edi
  call [ebp+_CloseHandle]  
  pop eax
   SkipChecksumCalc:
   DontCheckCRC:

  ;----- DECRYPTION -----
  mov eax,[ebp+dwImageBase]
  mov ebx,1
  CALL CryptPE
  LEA EAX, [EBP+OFFSET AfterDeCryptionContinue]
  PUSH EAX
  RET
  
  ; eax = pointer to file memory
  ; ebx: 0 - 加密模式
  ;      1 - 解密模式
  CryptPE:
  mov edi,eax
  add edi,[edi+3Ch]
  assume edi : ptr IMAGE_NT_HEADERS    ; edi -> PE header
  mov esi,edi
  add esi,0F8h
  assume esi : ptr IMAGE_SECTION_HEADER    ; esi -> Section header
  xor edx,edx
  .REPEAT
     
     ; -> 跳过一些特殊区段
     .IF dword ptr [esi].Name1 == ('crsr')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('rsr.')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('oler')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('ler.')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('Cy')
        jmp @@LoopEnd
     .ENDIF
     .IF dword ptr [esi].Name1 == ('ade.')
        jmp @@LoopEnd
     .ENDIF
     .IF [esi].PointerToRawData == 0 || [esi].SizeOfRawData == 0
        jmp @@LoopEnd
     .ENDIF
   
     ;-> en-/decrypt it
      pushad
     mov ecx,[esi].SizeOfRawData
     .IF ebx == 0        ; (ebx is a parameter)
        mov esi,[esi].PointerToRawData
        ADD ESI, EAX
        CALL EncryptSec
        ;因为现在这部分是DepackerCode的内容,而EncryptSec是加密的,所以EncryptSec不在DepackerCode标号内而在上面的地方
     .ELSE
        mov  esi,[esi].VirtualAddress
        add  esi,eax
        CALL DecryptSec
     .ENDIF

     JMP SecDecryptContinue1
     
  ; esi = CryptStart
  ; ecx = CryptSize
  ;解密函数,壳运行的时候用
  DecryptSec:
    mov edi,esi
  SecDecryptLoop:
    LODSB
    SecDecryptBuff DB SEC_PER_SIZE DUP (0)
    STOSB
    LOOP SecDecryptLoop
    RET
    
  SecDecryptContinue1:     
     popad
     
     @@LoopEnd:   
     add esi,SIZEOF IMAGE_SECTION_HEADER
     INC EDX
  .UNTIL dx == [edi].FileHeader.NumberOfSections
  assume esi : nothing
  assume edi : nothing
  ret

   AfterDeCryptionContinue:
        ;下面将入口点地址循环移动7位,放到变量ESP10处,然后将标号SehHandler_OEP_Jump地址放在ESP1C处,还不知道这个是干什么的。。后面应该有用吧

    ;------ PREPARE THE OEP JUMP EXCEPTION :) ------
  MOV  EBX, [EBP+dwImageBase]
  ADD  EBX, [EBP+dwOrgEntryPoint]
  ROR  EBX, 7
  MOV  [ESP+010h], EBX
  LEA  EBX, [EBP+OFFSET SehHandler_OEP_Jump]
  MOV  [ESP+01Ch], EBX


  mov edi,[ebp+dwImageBase]
  add edi,dword ptr [edi+03Ch]      ; edi -> pointer to PE header
  assume edi : ptr IMAGE_NT_HEADERS
  mov ebx,[edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY * 9].VirtualAddress
  assume edi : nothing
  cmp ebx,0           ; no tls section
  jz SkipTlsFix
  add ebx,[ebp+dwImageBase]      ; ebx -> pointer to tls table
  assume ebx : ptr IMAGE_TLS_DIRECTORY32
  mov eax,[ebx].AddressOfIndex
  mov dword ptr [eax],0
  assume ebx : nothing
  ;tls表索引清0


  mov eax,[ebp+dwCalcedCRC]
  .IF eax != 0
     .IF eax != [ebp+dwOrgChecksum]
        jmp SkipInitIt
     .ENDIF
  .ENDIF

比较校验和

  ;----- INIT IMPORT TABLE -----
  ; 0 - an error occurred
  ; 1 - IT initialized successfully
  LEA ESI, [EBP+OFFSET IIDInfo]                         ; ESI -> pointer to the current IID
  ASSUME ESI : PTR sItInfo
  
  ;------ PREPARE API REDIRECTION ------
  TEST [EBP+PROTECTION_FLAGS], API_REDIRECT_FLAG
  .IF !ZERO?
     PUSH ESI
     LEA  EDI, [EBP+OFFSET Buff]
     ASSUME EDI : PTR sReThunkInfo
     XOR  ECX, ECX
     .WHILE [ESI].FirstThunk
         MOV  EDX, [ESI].FirstThunk
        ADD  EDX, [EBP+dwImageBase]
        .WHILE DWORD PTR [EDX]
           INC  ECX
           ADD  EDX, 4
        .ENDW     
        ADD  ESI, SIZEOF sItInfo
     .ENDW
     ;计算有多少个API吧,然后根据这个数分配空间lauren存放sApiStub结构
     XOR  EDX, EDX
     MOV  EAX, SIZEOF sApiStub  
     MUL  ECX
     PUSH EAX
     PUSH GMEM_FIXED
     CALL [EBP+_GlobalAlloc]
     .IF !EAX    ; fatal exit
              ADD  ESP, 4
        POPAD
        RET
     .ENDIF
     MOV  [EDI].ApiStubMemAddr, EAX
     MOV  [EDI].pNextStub, EAX
     ;具体用途看继续看。。
     ASSUME EDI : NOTHING
     POP  ESI
  .ENDIF  

  ; 解密dll名,完了跳到InitITContinue1
  .WHILE [esi].FirstThunk != 0
     ; load the library
     mov ebx,[esi].DllNameRVA
     add ebx,[ebp+dwImageBase]
     
     ; decrypt dll string
     MOV EAX,EBX
     CALL EnDeCryptString
     LEA EAX, [EBP+InitITContinue1]    ; goto InitITContinue1
     PUSH EAX
     RET
     
  ; eax = VA of target string
   EnDeCryptString:
       PUSH ESI
       PUSH EDI       
    MOV ESI,EAX
    MOV EDI,EAX
  DllCryptLoop:
       LODSB
       ROR AL,4
       STOSB
       CMP BYTE PTR [EDI],0
       JNZ DllCryptLoop
       POP EDI
       POP ESI
    RET     
     
   InitITContinue1:
     push ebx
     call [ebp+_LoadLibrary]
     test eax,eax
     jz SkipInitIt
     ;解密之后加载这个dll,加载不了就跳出现在这个循环
  
     ; zero dll name
     PUSH EAX              ; save dll base
     test [ebp+PROTECTION_FLAGS],DESTROY_IMPORT_FLAG
     jz DontKillDllName
     ; push return address
     LEA EAX, [EBP+OFFSET DontKillDllName]
     PUSH EAX ; push return address :)
     MOV EAX, EBX
     JMP KillString
     ;把dll名填0
   DontKillDllName:
     POP EBX                                     ; EBX -> dll句柄
  
     ; process the (Original-)FirstThunk members
     mov ecx,[esi].OrgFirstThunk
     .IF ecx == 0
        mov ecx,[esi].FirstThunk
     .ENDIF
     ;OrgFirstThunk没有就用FirstThunk
     add ecx,[ebp+dwImageBase]      ; ecx -> pointer to current thunk(这个可能是OrgFirstThunk也可能是FirstThunk)
     mov edx,[esi].FirstThunk
     add edx,[ebp+dwImageBase]      ; edx -> pointer to current thunk (这个总是FirstThunk)
     .WHILE dword ptr [ecx] != 0
        test dword ptr [ecx],IMAGE_ORDINAL_FLAG32            ; 以序号导入吗
        jnz @@OrdinalImp
    
        ; process a name import
        mov dword ptr eax,[ecx]
        add eax,2
        ;跳过序号
        add eax,[ebp+dwImageBase]                         ; eax points now to the Name of the Import
        PUSH EAX
        CALL EnDeCryptString
        ;解密API名
        POP  EAX
        mov edi,eax          ; save the API name pointer for destroying it later
        push edx
        push ecx                                          ; save the Thunk pointers
        push eax
        push ebx
        call [ebp+_GetProcAddress]
        .IF eax == NULL
           pop ecx
           pop edx
           jmp SkipInitIt
        .ENDIF
        pop ecx
        pop edx
        ;->kill API name
          PUSHAD
        test [ebp+PROTECTION_FLAGS],DESTROY_IMPORT_FLAG
          JZ  DontKillApiName
          LEA EAX, [EBP+OFFSET DontKillApiName]        ; push return address
          PUSH EAX
        MOV EAX, EDI
        JMP KillString
   DontKillApiName:        
        POPAD
        ;-> paste API address
        mov dword ptr [edx],eax    
        jmp @@NextThunkPlease
        
   @@OrdinalImp:
              ; process an ordinal import
              push edx
              push ecx                                         ; save the thunk pointers
              mov dword ptr eax,[ecx]
              sub eax,080000000h
              ;把序号删掉,只剩下字符串的RVA
              push eax
              push ebx
              call [ebp+_GetProcAddress]
              test eax,eax
              jz SkipInitIt
              pop ecx
              pop edx
              mov dword ptr [edx],eax
              ;写入IAT
              
   @@NextThunkPlease:
           ; eax = Current Api address
           ; ebx = dll base
           ; edx = non-org thunk pointer
           TEST [EBP+PROTECTION_FLAGS], API_REDIRECT_FLAG
           .IF !ZERO?
              .IF [EBP+bNT]
                  .IF EBX < 070000000h || EBX > 077FFFFFFh
                      JMP SkipThunkRed
                  .ENDIF
              .ELSE
                  .IF EBX < 080000000h
                      JMP SkipThunkRed
                  .ENDIF
              .ENDIF
              ;非系统dll则跳过(根据地址空间判断)
              PUSH EDI
              PUSH ESI
              LEA  EDI, [EBP+Buff]
              ASSUME EDI : PTR sReThunkInfo
              MOV  ESI, [EDI].pNextStub
              MOV  [EDX], ESI    ; make the thunk point to stub mem
              SUB  EAX, ESI
              SUB  EAX, 5      ; sizeof E9XXXXXXXX - Jump long
              ;计算出这个函数地址的偏移(JMP跟的是偏移而不是绝对地址)
              MOV  BYTE PTR [ESI], 0E9h
              MOV  DWORD PTR [ESI+1], EAX
              ADD  [EDI].pNextStub, SIZEOF sApiStub
              ASSUME EDI : NOTHING
              POP  ESI
              POP  EDI
        SkipThunkRed:
           .ENDIF
           
        add ecx,4
        add edx,4
     .ENDW
     add esi,SIZEOF sItInfo              ; make esi point to the next IID
  .ENDW
  assume esi:nothing
  xor eax,eax
  inc eax
SkipInitIt:

  .IF eax != TRUE
     ; exit
     popad
     ret
  .ENDIF
  
    ;----- 擦除PE头,应该是为了不让调试软件读取里面的数据吧 ------
    test [ebp+PROTECTION_FLAGS],ERASE_HEADER_FLAG
    jz SkipEraseHeader
    
  ; zero the header
  mov edi,[ebp+dwImageBase]
  add edi,[edi+03Ch]
  assume edi : ptr IMAGE_NT_HEADERS      ; edi -> pointer to PE header
  mov ecx,[edi].OptionalHeader.SizeOfHeaders
  mov esi,[ebp+dwImageBase]
  assume edi : nothing
   ZeroMemLoop:
        mov byte ptr [esi],0
        inc esi
        loop ZeroMemLoop

  SkipEraseHeader:
  
    ;------ 这次是Loader的CheckSum。。这个壳搞了两次自校验 ------
    LEA  EAX, [EBP+DepackerCode]
    MOV  ECX, LOADER_CRC_CHECK_SIZE
    JMP SM10
    DB   0E9h
    SM10:
    CALL GetChecksum
    JMP SM11
    DB   0C7h
    SM11:
    MOV  EBX, [EBP+dwLoaderCRC]
    XOR  EAX, EBX
    .IF !ZERO?
       JMP SM12
       DB  02Ch
       SM12:
       POPAD
       JMP SM13
       DB  0E8h
       SM13:
       RET
    .ENDIF
  
    ;----- 解密OEP跳转的代码 -----
    LEA  EDI, [EBP+OFFSET OEP_JUMP_CODE_START]
    MOV  ESI, EDI
    MOV  ECX, CRYPT_OEP_JUMP_SIZE
  XOR  EBX, EBX
   OepJumpDecryptLoop:
      LODSB
     XOR  AL, OEP_JUMP_ENCRYPT_NUM
     SUB  AL, BL
     ROL  AL, 2
     STOSB
     INC EBX
     LOOP OepJumpDecryptLoop
  
  ;----- JUMP TO OEP -----
OEP_JUMP_CODE_START:
  ;----- 检测下是否在被调试 -----
  LEA EAX, [EBP+OFFSET szIsDebuggerPresent]
  PUSH EAX
  PUSH [EBP+dwKernelBase]
  CALL [EBP+_GetProcAddress]
  OR   EAX, EAX    ; API not present on W95
  .IF !ZERO?
     CALL EAX
     OR   EAX, EAX
     .IF  !ZERO?
         POPAD
        RET
     .ENDIF
  .ENDIF
  
  ;------ SECOND SI CHECK ------
  ; 这个检测方法不支持WINNT
  TEST [EBP+PROTECTION_FLAGS], CHECK_SI_FLAG
  JZ   SkipSICheck2
  LEA  ESI,[EBP+SEH]
  ASSUME ESI : PTR sSEH
  LEA  EAX, [EBP+OFFSET SICheck2_SP]
  MOV  [ESI].SaveEip, EAX
      ASSUME ESI : NOTHING
      XOR  EBX, EBX
  LEA  EAX, [EBP+OFFSET SehHandler2]
  PUSH EAX
  PUSH FS:[EBX]
  MOV  FS:[EBX], ESP
  MOV  EDI, EBP

  MOV  EAX, 4400h
  JMP SM4
  DB 0C7h
   SM4:
  INT  68h
   SICheck2_SP:  
        XOR  EBX, EBX
  POP  FS:[EBX]
  ADD  ESP, 4
  
  .IF DI == 01297h || DI == 01277h || DI == 01330h
     JMP SM5
     DB 0FFh
           SM5:     
     POPAD
     JMP SM6
     DB 0E8h
     SM6:
     RET
  .ENDIF
   SkipSICheck2:
   LEA  EAX, [EBP+OFFSET OepJumpCodeCont]
   PUSH EAX
   RET    
        ; ---- 在这个异常里面跳到OEP-----
  SehHandler_OEP_Jump PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
    PUSH EDI
    MOV  EAX,pContext
    ASSUME EAX : PTR CONTEXT
    
    ; restore original seh handler
    MOV  EDI, [EAX].regEsp
    PUSH [EDI]
    XOR  EDI, EDI
    POP  FS:[EDI]
    
    ; kill seh frame
    ADD  [EAX].regEsp, 8
    
    ; set EIP to the OEP
    MOV  EDI, [EAX].regEbx  ; EDI -> OEP
    ROL  EDI, 7
    ;把存放在ebx的右移过的ebx还原
    MOV  [EAX].regEip, EDI
    
    mov  EAX,ExceptionContinueExecution
    ;跳到OEP
    ASSUME EAX : NOTHING
    POP  EDI
    RET
  SehHandler_OEP_Jump ENDP

   OepJumpCodeCont:
  ;---- 把壳引导代码和其数据清除(设为0) ----
  XOR  AL,AL
  LEA  EDI, [EBP+OFFSET DepackerCode]
  MOV  ECX, (OFFSET SehHandler_OEP_Jump - OFFSET DepackerCode)
   LoaderZeroLoop:
  STOSB
  LOOP LoaderZeroLoop
  
  ;OEP_JUMP_CODE_END之后的数据也清除掉
  LEA  EDI, [EBP+OFFSET OEP_JUMP_CODE_END]
  MOV  ECX, (OFFSET LOADER_CRYPT_END - OFFSET OEP_JUMP_CODE_END)
   LoaderVarZeroLoop:
  STOSB
  LOOP LoaderVarZeroLoop

  POPAD  
    ;还记得PREPARE THE OEP JUMP EXCEPTION? 那时候保存了OEP移位之后的地址 和 一个异常处理过程
    ;猜想应该那两个变量的位置在这个时刻popad就会刚好放到ebx和eax里面了吧
     ; POPAD之后:
    ; EAX - OEP Seh handler
    ; EBX - OEP (移位过的)
  
    ;------ install OEP JUMP SEH frame ------  
  PUSH EAX
  XOR  EAX, EAX
  PUSH FS:[EAX]
  MOV  FS:[EAX], ESP

  JMP  SM3
  DB   087H
   SM3:     ;这里后面全部被填充00了,会引发异常

OEP_JUMP_CODE_END:

; EAX = ASCII string address
KillString:
  .WHILE byte ptr [eax] != 0
     mov byte ptr [eax],0
     inc eax
  .ENDW
  ret
  
SehHandler1 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
  PUSH EDI
  MOV  EAX,pContext
  ASSUME EAX : PTR CONTEXT
  MOV  EDI, [EAX].regEdi
  push [EDI+SEH.SaveEip]
  pop  [eax].regEip
  MOV  [eax].regEbp, EDI
  MOV  [EAX].regEax, 4     ; SI NOT detected !
  mov  EAX,ExceptionContinueExecution
  ASSUME EAX : NOTHING
  POP  EDI
  RET
SehHandler1 ENDP

SehHandler2 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
  PUSH EDI
  MOV  EAX,pContext
  ASSUME EAX : PTR CONTEXT
  MOV  EDI, [EAX].regEdi
  push [EDI+SEH.SaveEip]
  pop  [eax].regEip
  MOV  [eax].regEbp, EDI
  MOV  [EAX].regEdi, 0     ; SI NOT detected !
  mov  EAX,ExceptionContinueExecution
  ASSUME EAX : NOTHING
  POP  EDI
  RET  
SehHandler2 ENDP

;----- LOADER STRUCTS -----
sItInfo STRUCT
  DllNameRVA       dd ?
  FirstThunk       dd ?
  OrgFirstThunk    dd ?
sItInfo ENDS

sSEH STRUCT
  OrgEsp           dd ?
  OrgEbp           dd ?
  SaveEip          dd ?
sSEH ENDS

sReThunkInfo STRUCT
  ApiStubMemAddr   DD ?
  pNextStub        DD ?
sReThunkInfo ENDS

sApiStub STRUCT        ; UNUSED !
  JumpOpc          DB ?
  JumpAddr         DD ?
sApiStub ENDS
  
;----- LOADER VARIABLES -----

dwImageBase             dd 0
dwOrgEntryPoint         dd 0
PROTECTION_FLAGS        dd 0
dwCalcedCRC             dd 0
dwLoaderCRC             DD 0
bNT                     DD 0

IIDInfo                 db (SIZEOF sItInfo * MAX_IID_NUM) dup (0)

SEH                     sSEH <0>

_LoadLibrary            dd 0
_GetProcAddress         dd 0

; some API stuff
szKernel32              db "Kernel32.dll",0
dwKernelBase            dd 0
szGetModuleHandle       db "GetModuleHandleA",0
_GetModuleHandle        dd 0
szVirtualProtect        db "VirtualProtect",0
_VirtualProtect         dd 0
szGetModuleFileName     db "GetModuleFileNameA",0
_GetModuleFileName      dd 0
szCreateFile            db "CreateFileA",0
_CreateFile             dd 0
szGlobalAlloc           db "GlobalAlloc",0
_GlobalAlloc            dd 0
szGlobalFree            db "GlobalFree",0
_GlobalFree             dd 0
szReadFile              db "ReadFile",0
_ReadFile               dd 0
szGetFileSize           db "GetFileSize",0
_GetFileSize            dd 0
szCloseHandle           db "CloseHandle",0
_CloseHandle            dd 0
szIsDebuggerPresent     db "IsDebuggerPresent",0
LOADER_CRYPT_END:

; This variables won't be crypted:
TlsBackupLabel:
TlsBackup               IMAGE_TLS_DIRECTORY32 <0>

ChecksumLabel:
dwOrgChecksum           dd 0

Buff                    db 0  ; buffer for some stuff, its size: 2000h(VS) - DEPACKER_CODE_SIZE

;----- END OF PE LOADER CODE -----
DepackerCodeEnd:

[课程]FART 脱壳王!加量不加价!FART作者讲授!

收藏
免费 0
支持
分享
最新回复 (3)
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
2
楼主能否给出demoscene的那个帖子地址,方便以后也可以作为参考 : )
2013-6-14 08:54
0
雪    币: 1015
活跃值: (235)
能力值: ( LV12,RANK:440 )
在线值:
发帖
回帖
粉丝
3
前排站位,谢谢楼主分享!
2013-6-14 09:19
0
雪    币: 45
活跃值: (55)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
这个地址 http://bbs.pediy.com/showthread.php?t=113362
挺好的,他的代码高亮也做得好 哈哈,看着舒服
我的这个高亮在blog里http://blog.csdn.net/u010887709/article/details/9042395
2013-6-14 09:47
0
游客
登录 | 注册 方可回帖
返回
//