前言

这两天分析了下Yoda Cryptor 1.2的源码和大家分享下
本来想分析 Yoda's Protect 1.02的,看了一下,代码是C语言的,不过那写界面的代码比用来实现主要功能的代码还要多,看得我晕了,就分析了Yoda Cryptor 1.2的ASM源码,比VC的那个简洁多了

我主要分析了CryptStuff.ASM和PER.ASM这两个文件,主要功能是在CryptFile这个函数。
分析过程可能会有错误,大家作为参考下就行了。
有一些变量的定义在其它的两个文件大家可以在附件中下载原始的源码对着来看




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    ;这里初始化随机数种子主要利用了 GetTickCount
    
    ;----- MAP THE FILE -----
    
;;下面主要是分配内存空间并把程序读取到内存中,并且检测PE文件的有效性
;**********************************************************************************************************
    
    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        ;IT_SIZE 输入表大小
    add eax,DEPACKER_CODE_SIZE        ;DEPACKER_CODE_SIZE 解码代码的大小
    add eax,ALIGN_CORRECTION
    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'        ;检验 IMAGE_DOS_SIGNATURE
       push pMem
       call GlobalFree
       push hFile
       call CloseHandle
       jmp PEErr
    .ENDIF
    add edi,[edi+3Ch]
    .IF word ptr [edi] != 'EP'        ;检验IMAGE_NT_SIGNATURE
       push pMem
       call GlobalFree
       push hFile
       call CloseHandle
       jmp PEErr
    .ENDIF    
    
;;下面是保存程序的一些重要信息
;*****************************************************************************************************************

    mov dwNTHeaderAddr,edi
    assume edi : ptr IMAGE_NT_HEADERS
    push [edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress        ;获取程序的输入表RVA
    pop dwOrgITRVA
    push word ptr [edi].FileHeader.NumberOfSections        ;获取程序的区段数
    pop word ptr dwSectionNum
    .IF dwSectionNum > MAX_SECTION_NUM            ;区段数不能大于20
       JMP SecNumErr
    .ENDIF
    push [edi].OptionalHeader.AddressOfEntryPoint        ;保存程序OEP
    pop dwOrgEntryPoint
    push [edi].OptionalHeader.ImageBase            ;保存程序镜像基址
    pop dwImageBase
    
    ;----- DELETE Bound Import & IAT DIRECTORIES -----
    
 
;**********************************************************************************
;下面销毁 IMAGE_BOUND_IMPORT_DESCRIPTOR    

    XOR  EAXEAX
    MOV  ECX, 4
    LEA  EDI, [EDI].OptionalHeader.DataDirectory[11 *SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress        ;获取 IMAGE_BOUND_IMPORT_DESCRIPTOR RVA
    assume edi : nothing
   DirDelLoop:
        STOSD
        LOOP DirDelLoop            ;用 0 填充掉 IMAGE_BOUND_IMPORT_DESCRIPTOR

;*******************************************************************    
;加密DLL 和 API 名字 并且销毁 IID
    PUSH dwOrgITRVA
    PUSH pMem
    CALL RVA2Offset     ;RVA2Offset 把RVA转成文件的偏移,这里得到原程序输入表的偏移
    PUSH EAX
    PUSH pMem

    CALL ProcessOrgIT        ;加密原程序输入表
    OR   EAXEAX
    .IF  ZERO?        ;是否加密成功
       PUSH pMem
       CALL GlobalFree
       PUSH hFile
       CALL CloseHandle
       JMP  IIDErr
    .ENDIF    
       
;;增加一个新节******************************************************
    push pMem
    call AddSection        
    .IF eax == 0
       push pMem
       call GlobalFree
       push hFile
       call CloseHandle
       jmp NoRoom4SectionErr
    .ENDIF
    
;创建壳自己的输入表**************************************88
    xchg eax,esi                    ;ESI 指向新节的节头
    assume esi : ptr IMAGE_SECTION_HEADER
    mov eax,[esi].PointerToRawData
    MOV  dwNewSectionRO, EAX
    add eax,pMem
    push [esi].VirtualAddress
    push eax
    call AssembleIT        ;开始创建壳的输入表
    

;处理Tls表。把Tls复制到壳中*************************
    push [esi].VirtualAddress
    push pMem
    call ProcessTlsTable
    
;加密节表**************************************
    pushad
    ; generate PER
    ;这个PER是什么意思我也不知道,这里保留原注释
    ;下面是产生加密和解密函数,同时会生成花指令。是动态生成的。每一次加壳生成的加解密函数都不相同
    ;作者在文件中注释为此多态(PS:原来多态就是这样子的)。
    ;生成的函数用来后面的加密节代码,和解密节代码、
    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
    
    push [esi].VirtualAddress            ;壳段的RVA(刚开始的地方保存的就是壳的输入表)压栈
    pop [edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress            ;把PE头的的输入表指向壳的输入表
    ; EntryPoint...
    mov eax,[esi].VirtualAddress            
    add eax,IT_SIZE            ;壳段RVA + 输入表的大小,后面的就是壳的代码
    mov [edi].OptionalHeader.AddressOfEntryPoint,eax        ;OEP修改为壳的代码
    ; SizeOfImage ...
    mov eax,[esi].VirtualAddress        ;基址
    add eax,[esi].Misc.VirtualSize    ;加上虚拟大小得到程序在内存的映像大小
    mov [edi].OptionalHeader.SizeOfImage,eax            ;修改SizeOfImage
    ; 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 -----
    ;下面的代码把跳到要OEP的这段代码加密
    MOV  EDI, pMem
    ADD  EDI, dwNewSectionRO
    ADD  EDI, IT_SIZE
    ADD  EDI, (OFFSET OEP_JUMP_CODE_START - OFFSET DepackerCode)
    MOV  ESIEDI                ;ESI 指向JUMP_CODE_START
    MOV  ECX, CRYPT_OEP_JUMP_SIZE
    XOR  EBXEBX
   OepJumpEncryptLoop:        ;开始循环加密
        LODSB
       ROR  AL, 2
       ADD  ALBL
       XOR  AL, OEP_JUMP_ENCRYPT_NUM    
       STOSB
       INC EBX
       LOOP OepJumpEncryptLoop    

    ;----- 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                ;得到VarDecryptBuff的VA
    PUSH OFFSET VarEncryptBuff                
    CALL MakePER                ;产生加解密函数
    
    ; encryption !
    MOV  EDI, pMem
    ADD  EDI, dwNewSectionRO
    ADD  EDI, IT_SIZE
    ADD  EDI, (OFFSET LOADER_CRYPT_START - OFFSET DepackerCode)            ;EDI = LOADER_CRYPT_START的地址
    MOV  ECX, CRYPT_LOADER_SIZE_DB            ;大小
    MOV  ESIEDI    
   @@VarEncryptionLoop:            ;循环加密
        LODSB
    VarEncryptBuff DB VAR_PER_SIZE DUP (0)                ;MakePER产生的加密函数
        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  EAXOFFSET szIIDErr
    JMP  ShowErr
    
ShowErr:
    invoke MessageBox,hDlg,eax,offset szErr,MB_ICONERROR
    jmp @@Exit
CryptFile ENDP

;--------- functions -----------------

; esi = CryptStart
; ecx = CryptSize
EncryptSec:                ;加密函数
    mov edi,esi
SecEncryptLoop:
    LODSB
    SecEncryptBuff DB SEC_PER_SIZE DUP (0)            ;MakePER 函数生成的加密函数代码放在这里,进行加密
    STOSB                        ;把加密后的代码写回去
    LOOP SecEncryptLoop
    RET



;*********************************************************************************************************    
;添加一个区段函数
; 返回值:
; 0 - 空间不足
; 1 - 添加节失败,可能文件已经加壳
; 否则: 返回一个指向新添加节的 IMAGE_SECTION_HEADER 的指针
;********************************************************************************************************

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指向PE头
    
;检查是否有足够的空间来添加新节
    xor eax,eax
    mov ax,[edi].FileHeader.NumberOfSections            ;区段数
    mov dwSecNum,eax
    mov ecx,SIZEOF IMAGE_SECTION_HEADER
    imul eax,ecx                                           ; eax = 所有Section Header 的大小的和
    add eax,SIZEOF IMAGE_SECTION_HEADER                    ; 加上一个区段的大小
    mov ecx,edi                           ; ecx =  PE头
    sub ecx,pMem_                           ; 减去pMem 得到DOS头大小
    add ecx,eax                           ; 加上新的总 SectionHeader 的大小
    add ecx,0F8h                           ; 加上PE头的大小得 ecx = sizeof (DOS头 + Section Header +PE Header)
    .IF ecx > [edi].OptionalHeader.SizeOfHeaders            ;如果ecx < SizeOfHeaders  则有足够空间
       xor eax,eax
       jmp @@ExitProc_AS
    .ENDIF
    
    ; create a new section
    mov esi,edi
    add esi,0F8h        ;esi加上PE头大小,此时 esi 指向 节表
    assume esi : ptr IMAGE_SECTION_HEADER    
    mov edx,dwSecNum    
    sub edx,1        ;区段数减去1,下面的repeat循环用到的计数器
    ;下面循环把区段添加一个可写属性
    .REPEAT
       mov eax,[esi].Characteristics            ;得到区段的属性
       or eax,080000000h                    ;or 080000000,添加可写属性
       mov [esi].Characteristics,eax            ;更新区段属性

       add esi,SIZEOF IMAGE_SECTION_HEADER            ;指向下一个区段
       dec edx
    .UNTIL edx == 0        ;循环,循环结束里,ESI指向最后一个区段
    ; 开始添加区段
    mov edx,esi            ;EDX指向最后一个区段
    add edx,SIZEOF IMAGE_SECTION_HEADER        ; EDX指向新的区段
    assume edx : ptr IMAGE_SECTION_HEADER
    
    ; VirtualAddress...
    mov eax,[esi].VirtualAddress        ;最后一个区段的RVA
    add eax,[esi].Misc.VirtualSize    ;加上大小得到未对齐时区段末尾
    push 01000h            ;对齐大小
    push eax                ;
    call PEAlign        ;对齐,得到对齐后区段末尾,就是新节的开始
    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
    
    ; Characteristics
    mov [edx].Characteristics,0E00000E0h            ;写入新区段属性
    
    ; RawOffset
    mov eax,[esi].PointerToRawData        ;最后一个区段的偏移
    add eax,[esi].SizeOfRawData            ;加上大小,指向还没对齐的段末尾
    push 0200h
    push eax
    call PEAlign            ;对齐,对到新节的偏移
    mov [edx].PointerToRawData,eax            ;写入新节偏移
    mov eax,edx                    ; eax =新节的偏移,作为返回值
    
    ; update the PE header
    inc [edi].FileHeader.NumberOfSections        ;区段数加1
    
    assume edx : nothing
    assume esi : nothing
    assume edi : nothing    
@@ExitProc_AS:
    ret
AddSection ENDP


;***************************************************************************************8
;函数功能:创建壳自己的输入表,里面包含壳要用到的函数,LoadLibraryA,GetProcAddressA
;参数: pAdress4IT      壳输入表的 地址,
;        dwNewSectionVA  壳段的VA             
;反回值:  无
;***************************************************************************************8

AssembleIT PROC USES ebx ecx edx esi edi, pAddress4IT : LPVOID, dwNewSectionVA : DWORD
    mov esi,pAddress4IT                    ; esi -> base of the new IT        

    ; Zero the memory for the new IT
    mov eax,pAddress4IT
    mov ecx,IT_SIZE
   ZeroMem:            ;用 0 初始化 
    mov byte ptr [eax],0
    inc eax
    loop ZeroMem    
    
    ; build a new,nice ImportTable :)
    mov ebx,esi
    mov eax,SIZEOF IMAGE_IMPORT_DESCRIPTOR
    xor edx,edx
    mov ecx,2            ;壳的输入表中包含 2 个 IID
    mul ecx
    add ebx,eax            ;ebx 指向IID 末尾        
    assume esi:ptr IMAGE_IMPORT_DESCRIPTOR
    mov eax,ebx                                           
    sub eax,esi            ;eax 等于 2 个IID的大小 ,最后一个 IID 是全 0,代码 IID结束
    add eax,dwNewSectionVA     ;eax 等于 IID 末尾的RVA
    mov [esi].Name1,eax         ;笫一个 IID 的DLL 名字指向了 IID末尾
    push esi
    mov esi,offset szKernel        ;szKernel  指向 "Kernel32.dll"
    mov edi,ebx
    .REPEAT            ;把 "Kernel32.dll" 填充到IID 末尾
       lodsb
       stosb
    .UNTIL byte ptr [esi] == 0
    pop esi
    mov ebx,edi
    inc ebx                ;在"Kernel32.dll" 末尾空出一字节,是IMAGE_THUNK_DATA数组
    mov eax,ebx                            
    sub eax,esi
    add eax,dwNewSectionVA        ;RVA,这里保存一个IMAGE_THUNK_DATA
    mov [esi].FirstThunk,eax                               
    mov edx,ebx
    add edx,10        ;edx = IMAGE_IMPORT_BY_NAME    
    mov eax,edx
    sub eax,esi
    add eax,dwNewSectionVA        ;IMAGE_IMPORT_BY_NAME VA
    mov [ebx],eax
    add edx,2                        ;Hint
    push esi
    mov esi,offset szLoadLibrary        ;"LoadLibraryA"
    mov edi,edx
    .REPEAT        ;"LoadLibratyA" 填充到 edx.Name
       lodsb
       stosb
    .UNTIL byte ptr [esi] == 0
    pop esi
    mov edx,edi    
    add ebx,4            ;下一个 IMAGE_THUNK_DATA
    mov eax,edx            ;"GetProcAddress"
    sub eax,esi
    add eax,dwNewSectionVA    ;VA
    mov [ebx],eax    
    add edx,2            ;Hint
    mov esi,offset szGetProcAddress
    mov edi,edx
    .REPEAT                ;填充"GetProcAddress"到edx.name
       lodsb
       stosb
    .UNTIL byte ptr [esi] == 0    
    assume esi : nothing
    ret
AssembleIT ENDP


;****************************************************************************************
;处理Tls表。把原程序的Tls复制到壳的代码中。并把数据上录的Tls地址修正到壳的Tls地址
;参数:  pFIleMem         文件的基址
;            CryptSectionVA     壳节基址VA
;返回值: 无
;****************************************************************************************

ProcessTlsTable PROC USES edi ebx esi ecx, pFileMem : LPVOID, CryptSectionVA : DWORD
    LOCAL  pTlsDirAddr : LPVOID
    
    mov edi,pFileMem
    add edi,[edi+03Ch]                ;EDI = PE头
    assume edi : ptr IMAGE_NT_HEADERS
    lea ebx,[edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY * 9].VirtualAddress  ;ebx = IMAGE_TLS_DIRECTORY32
    mov pTlsDirAddr,ebx
    mov ebx,[ebx]                ;ebx 指向 Tls数据起始地址
    assume edi : nothing
    cmp ebx,0                     ; Tls 是否存在
    jz ExitTlsFixProc
    
    ; get a RAW pointer to the tls table
    push ebx
    push pFileMem
    call RVA2Offset        ;计算 Tls 的偏移
    cmp eax,0
    jz ExitTlsFixProc
    mov esi,pFileMem
    add esi,eax                    ; esi 指向 Tls 的地址
    
    ;把 Tls 复制到 壳的代码中保存
    mov edi,offset TlsBackup
    mov ecx,sizeof IMAGE_TLS_DIRECTORY32
    rep movsb
    
    ;把Tls的地址指向壳的Tls
    mov eax,CryptSectionVA
    add eax,IT_SIZE
    add eax,TLS_BACKUP_ADDR    ;基址加上输入表大小,再加上壳Tls基于解压代码的偏移得到Tls的地址 
    mov esi,pTlsDirAddr
    mov [esi],eax            ;修正地址
  ExitTlsFixProc:
    ret
ProcessTlsTable ENDP

;*********************************************************************************************************************
;;处理加密输入表,函数会把 DLL的名字,API的名字加密保存真情为,并销毁原来的 IID
;参数
;    pFileImage    基址
;    pITBaseR0   输入表在文件中的偏移

;函数返回值 
;            1:成功
;            0:IID结构太大
;*********************************************************************************************************************

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

    ; clear the IIDInfo array
    XOR EAX,EAX
    MOV EDIOFFSET IIDInfo
    MOV ECX, SIZEOF IIDInfo
   ClearArrayLoop:
        STOSB
        LOOP ClearArrayLoop    ;用 0 初始化 IIDInfo数组
    
    ; get a random number        ;产生一个随机数,后面会用来填充掉 IID
    INVOKE GetTickCount
    XOR EAX, ("yoda")
    MOV EDX,EAX                            ; EDX -> stupid number :)
    
    ; 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 指向 一个sItInfo数组,主要用来保存IID的一些重要信心
    .WHILE [EDI].Name1        ;循环加密 DLL 名字
       ; too much IID's ?
       INC  dwIIDNum
       .IF dwIIDNum == (MAX_IID_NUM)
           XOR  EAXEAX
           JMP  POIT_Exit
       .ENDIF
       
       ; save IID Infos        ;保存 IID的重要信息
       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        ;得到 DLL 名字的地址
       ;-> crypt string
       CALL EnDeCryptString            ;加密 DLL 名字
         
         ;--- CRYPT API name strings ---
         PUSH ESI
         MOV  ESI, [EDI].OriginalFirstThunk
         .IF !ESI            ;如果 OriginalFirstThunk 不为空,则使用 OriginalFirstThunk。否则使用 FirstThunk
            MOV ESI, [EDI].FirstThunk
         .ENDIF
         PUSH ESI
         PUSH pFileImage
         CALL RVA2Offset        
         MOV  ESIEAX
         ADD  ESI, pFileImage            ;得到 IMAGE_THUNK_DATA
         .WHILE DWORD PTR [ESI]        ;得到 IMAGE_IMPORT_BY_NAME 
            MOV  EAX, [ESI]
           
          TEST EAX,IMAGE_ORDINAL_FLAG32        ;判断 Hint高位是否为1,为1则函数以序号引出,跳过此函数
          JNZ  SkipApiString
            PUSH EAX
            PUSH pFileImage
            CALL RVA2Offset
            OR   EAXEAX
            JZ   SkipApiString
            ADD  EAX, pFileImage        
            ADD  EAX, 2                    ;跳过 Hint
            CALL EnDeCryptString        ;加密 API 名字
   SkipApiString:            
            ADD  ESI, 4        ;指向下一个IMAGE_THUNK_DATA
         .ENDW                ;循环加密API名字
         POP ESI
         
         ; 用随机数填充 IID
         MOV [EDI].Name1, EDX            
         MOV [EDI].OriginalFirstThunk, EDX
         MOV [EDI].FirstThunk, EDX
         MOV [EDI].TimeDateStamp, EDX
         MOV [EDI].ForwarderChain, EDX
          
       ;EDI 指向下一个 IID,ESI指向下一个 sItInfo
       ADD EDI,SIZEOF IMAGE_IMPORT_DESCRIPTOR
       ADD ESI,SIZEOF sItInfo
    .ENDW        ;循环
    ASSUME ESI : NOTHING
    ASSUME EDI : NOTHING
    XOR  EAXEAX
    INC  EAX        ;返回 1代表加密成功
   POIT_Exit:
    RET
ProcessOrgIT ENDP

;********************************************************************************************
;对齐函数
PEAlign PROC USES ecx edx, dwTarNum : DWORD, dwAlignTo : DWORD
    mov ecx,dwAlignTo
    mov eax,dwTarNum
    xor edx,edx
    div ecx
    cmp edx,0
    jz AlreadyAligned
    inc eax    
   AlreadyAligned:
       mul ecx
    ret
PEAlign ENDP
;********************************************************************************************


;***********************************************************************************************
; 把一个RVA值转成在文件的偏移值
; Base    - 映射基址
; dwITRVA - 要转换的RVA
; 失败返回0,成功返回转换后的偏移
;**********************************************************************************************
RVA2Offset PROC USES ebx ecx edx, Base : DWORD,dwITRVA : DWORD

    mov eax,Base
    add eax,[eax+03Ch]        ;eax指向PE头
    invoke ImageRvaToSection,eax,Base,dwITRVA        ;得到RVA所在区段的Section Header
    test eax,eax        ;是否成功
    jz @@ExitProc
    
    xchg eax,ebx
    assume ebx : ptr IMAGE_SECTION_HEADER
    mov eax,dwITRVA
    sub eax,[ebx].VirtualAddress            ;RVA - 区段虚拟地址
    add eax,[ebx].PointerToRawData        ;加上区段在文件的偏移,得到RVA在文件的偏移
    assume ebx : nothing
@@ExitProc:
    ret
RVA2Offset ENDP

;**********************************************************************************************************************
;下面是壳的代码
DepackerCode:
    pushad

    ; get base ebp
    call CallMe            ;重定位
  CallMe:
    pop ebp
    sub ebp,offset CallMe
    
    ;----- DECRYPT LOADER VARIABLES -----
    MOV ECX, CRYPT_LOADER_SIZE_DB
    LEA EDI, [EBP+OFFSET LOADER_CRYPT_START]
    MOV ESIEDI
   VarDecryptionLoop:
        LODSB
    VarDecryptBuff DB VAR_PER_SIZE DUP (0)            ;MakePER函数产生的解密函数。对下面的代码进行解密
        STOSB
        LOOP VarDecryptionLoop
        
LOADER_CRYPT_START:
    ;------ DETECT WinNT ------
    MOV  EAX, [ESP+020h]            ;判断是不是NT?什么原理?
    INC  EAX
    JS   NoNT
    MOV  DWORD PTR [EBP+bNT], 1
   NoNT:    

        ;------ Get CRC OF LOADER CODE ------
        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
    
    ; install SEH frame
    LEA  ESI,[EBP+SEH]    
    ASSUME ESI : PTR sSEH
    LEA  EAX, [EBP+OFFSET SICheck1_SP]
    mov  [ESI].SaveEip, EAX
        ASSUME ESI : NOTHING
    MOV  EDIEBP
    LEA  EAX, [EBP+OFFSET SehHandler1]        ;取SehHandler1的偏移
    XOR  EBXEBX
    push EAX                ;安装SEH    
    push FS:[EBX]
    mov  FS:[EBX], ESP
    
    ; 0 - SI not found
    ; 1 - SI found
       mov     ebp, 04243484Bh            ;?????
        mov     ax, 04h
        JMP     SM1
        DB 0FFh            ;花指令
  SM1:
          INT  3        ;异常。跳到SehHandler1
        
   SICheck1_SP:            ;SaveEip
    MOV  EBPEDI
    ; uninstall SEH frame
    XOR  EBXEBX        ;移除SEH
        POP  FS:[EBX]    
        ADD  ESP, 4        ;
        
       .IF AL != 4        
           ; 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]                ;EAX = PE Header
    add eax,080h                        ;EAX = DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtuallAddress
    mov ecx,[eax]                 ;ECX = IID RVA
    add ecx,[ebp+dwImageBase]        ;IID VA
    add ecx,16                    ;IID.FirstThunk
    mov eax,dword ptr [ecx]            ;EAX = IMAGE_THUNK_DATA RVA
    add eax,[ebp+dwImageBase]        ;Va
    mov ebx,dword ptr [eax]            ;ebx 指向LoadLibraryA的地址
    mov [ebp+_LoadLibrary],ebx        ;保存LoadLIbraryA地址
    add eax,4                            ;下一个IMAGE_THUNK_DATA_VA
    mov ebx,dword ptr [eax]            ;GetProcAddressA地址
    mov [ebp+_GetProcAddress],ebx    ;保存
    
;**********************************************************************************    
;获取需要用的函数地址
    ; get kernel base
    lea eax,[ebp+offset szKernel32]    ;"Kernel32.dll"
    push eax
    call [ebp+_LoadLibrary]                ;"加载Kernel32.dll"
    mov esi,eax                    ; esi = Kernel32 handle
    MOV [EBP+dwKernelBase], EAX    ;保存 Kernel32 handle

    ;-> GetModuleHandle
    lea eax,[ebp+szGetModuleHandle]
    call DoGetProcAddr            ;获取 GetModuleHandle 地址
    mov [ebp+_GetModuleHandle],eax    ;保存
    
    ;-> VirtualProtect
    lea eax,[ebp+szVirtualProtect]        
    call DoGetProcAddr                ;获取VirtualProtect 地址
    mov [ebp+_VirtualProtect],eax    
    
    ;-> GetModuleFileName    
    lea eax,[ebp+szGetModuleFileName]
    call DoGetProcAddr                    ;获取GetModuleFileName 地址
    mov [ebp+_GetModuleFileName],eax
    
    ;-> CreateFile
    lea eax,[ebp+szCreateFile]
    call DoGetProcAddr                    ;获取CreateFileA 地址
    mov [ebp+_CreateFile],eax
    
    ;-> GlobalAlloc
    lea eax,[ebp+szGlobalAlloc]
    call DoGetProcAddr                    ;获取GlobalAlloc 地址
    mov [ebp+_GlobalAlloc],eax
    
    ;-> GlobalFree
    lea eax,[ebp+szGlobalFree]
    call DoGetProcAddr                    ;获取GlobalFree 地址
    mov [ebp+_GlobalFree],eax
    
    ;-> ReadFile
    lea eax,[ebp+szReadFile]
    call DoGetProcAddr                    ;获取ReadFileA 地址
    mov [ebp+_ReadFile],eax
    
    ;-> GetFileSize
    lea eax,[ebp+szGetFileSize]
    call DoGetProcAddr                    ;获取GetFileSize 地址
    mov [ebp+_GetFileSize],eax
    
    ;-> CloseHandle
    lea eax,[ebp+szCloseHandle]
    call DoGetProcAddr                    ;获取CloseHandle 地址
    mov [ebp+_CloseHandle],eax
    
    ; FUNNY JUMP :)
    LEA EAX, [EBP+OFFSET LoaderContinue1]
    PUSH EAX                                    ;变形 JMP 到 LoaderContinue1
    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            ;是否要 anti dump
    jz LetDumpable
    
        push    fs:[30h]            ;PEB
        pop     eax
        TEST    EAXEAX        
        JS      fuapfdw_is9x     ; detected Win 9x
   fuapfdw_isNT:
        MOV     EAX, [EAX+0Ch]    ;_PEB_LDR_DATA
        MOV     EAX, [EAX+0Ch]    ;LDR_MODULE
        MOV     DWORD PTR [EAX+20h], 1000h ; 修改进程映像大小 为 1000h
        JMP     fuapfdw_finished
   fuapfdw_is9x:
        PUSH    0
        CALL    [ebp+_GetModuleHandle]        ;获取自身句柄
        TEST    EDXEDX
        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]        ;PE头
    assume edi : ptr IMAGE_NT_HEADERS            ; edi -> pointer to PE header
    mov esi,[ebp+dwImageBase]
    mov ecx,[edi].OptionalHeader.SizeOfHeaders        ;ECX = SizeOfHeaders
    assume edi : nothing
        
    ; 修改文件头的属性为PAGE_READWRITE
    lea eax,[ebp+Buff]        ;保存现在的页属性
    push eax
    push PAGE_READWRITE
    push ecx                        ;大小为SizeOfHeaders
    push [ebp+dwImageBase]
    call [ebp+_VirtualProtect]            
    
    
    ;是否要进行CRC校验
    test [ebp+PROTECTION_FLAGS],CHECK_HEADER_CRC
    jz DontCheckCRC        

    ;获取自身的文件名
    push MAX_PATH
    lea edi,[ebp+Buff]        ;临时保存文件名
    push edi            
    push 0
    call [ebp+_GetModuleFileName]
    
    ;以只读方式打开自身
    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            
    ;获取文件大小
    push NULL
    push edi
    call [ebp+_GetFileSize]
    sub eax,CHECKSUM_SKIP_SIZE
    xchg eax,esi        ;ESI = 文件大小    
    ;申请内存空间
    push esi
    push GMEM_FIXED+GMEM_ZEROINIT
    call [ebp+_GlobalAlloc]
    .IF eax == NULL
       jmp SkipChecksumCalcAndCleanUp
    .ENDIF
    xchg eax,ebx            ; EBX = 申请到的内存空间
    ;把文件读到申请的内存空间中
    push NULL
    lea eax,[ebp+Buff]        ;参数NumberOfBytesToRead
    push eax
    push esi                        ;大小
    push ebx                        ;申请到的内存空间
    push edi                        ;文件句柄
    call [ebp+_ReadFile]
    
    ; 计算校验和
    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            ;变形JMP
    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            ;返回值 EAX =校验和
       ;-> 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:

    ;解密
    mov eax,[ebp+dwImageBase]        
    mov ebx,1
    CALL CryptPE
    LEA EAX, [EBP+OFFSET AfterDeCryptionContinue]
    PUSH EAX        ;JMP 
    RET
    
;*****************************************************    
    ; eax = pointer to file memory
    ; ebx: 0 - 加密文件中的代码
    ;      1 - 解密内存中的代码
    CryptPE:
    mov edi,eax
    add edi,[edi+3Ch]                ;EDI = IMAGE_NT_HEADERS
    assume edi : ptr IMAGE_NT_HEADERS    
    mov esi,edi
    add esi,0F8h                    ;ESI = IMAGE_SECTION_HEADER
    assume esi : ptr IMAGE_SECTION_HEADER        
    xor edx,edx
    .REPEAT
       
       ;跳过下面这些区段,不进行加密
       .IF dword ptr [esi].Name1 == ('crsr')        ;.rsrc 资源
          jmp @@LoopEnd
       .ENDIF    
       .IF dword ptr [esi].Name1 == ('rsr.')        ;.rsr  资源
          jmp @@LoopEnd
       .ENDIF
       .IF dword ptr [esi].Name1 == ('oler')        ;.relo 重定位
          jmp @@LoopEnd
       .ENDIF
       .IF dword ptr [esi].Name1 == ('ler.')        ;.rel 重定位
          jmp @@LoopEnd
       .ENDIF
       .IF dword ptr [esi].Name1 == ('Cy')            ;.yC 自己的段
          jmp @@LoopEnd
       .ENDIF
       .IF dword ptr [esi].Name1 == ('ade.')        ;.eda    ??
          jmp @@LoopEnd
       .ENDIF
       
       ;跳过空数据段
       .IF [esi].PointerToRawData == 0 || [esi].SizeOfRawData == 0
          jmp @@LoopEnd
       .ENDIF
   
       ;-> en-/decrypt it
           pushad
       mov ecx,[esi].SizeOfRawData
       .IF ebx == 0                ; 加密文件中的代码?
          mov esi,[esi].PointerToRawData
          ADD ESIEAX            ;加上基址
          CALL EncryptSec
       .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)            ;Make PER 函数生成的解密代码放在这里,进行解密
        STOSB            ;解密后的代码重新写回去
        LOOP SecDecryptLoop
        RET
        
    SecDecryptContinue1:       
       popad
       
       @@LoopEnd:   
       add esi,SIZEOF IMAGE_SECTION_HEADER                ;ESI 指向下一个节
       INC EDX
    .UNTIL dx == [edi].FileHeader.NumberOfSections            ;循环
    assume esi : nothing
    assume edi : nothing
    ret
;**********************************************************************

   AfterDeCryptionContinue:
   
      ;------ 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
    
    ;
;下面代码将TLS索引清0
;***************************************************
    mov edi,[ebp+dwImageBase]
    add edi,dword ptr [edi+03Ch]            ;EDI = PE 头
    assume edi : ptr IMAGE_NT_HEADERS
    mov ebx,[edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY * 9].VirtualAddress        ;Tls TABLE
    assume edi : nothing
    cmp ebx,0                     ; 存在TLS?
    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        ;TLS索引置0
    assume ebx : nothing
;****************************************************    
    
  SkipTlsFix:

    ;校验和是否相等
    mov eax,[ebp+dwCalcedCRC]
    .IF eax != 0
       .IF eax != [ebp+dwOrgChecksum]
          jmp SkipInitIt        ;不相等就跳过初始化输入表
       .ENDIF
    .ENDIF
    
    ;----- INIT IMPORT TABLE -----
    ; 0 - 失败
    ; 1 - 成功
    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  ECXECX
       
;下面的循环,获取API函数的个数
       .WHILE [ESI].FirstThunk
              MOV  EDX, [ESI].FirstThunk
          ADD  EDX, [EBP+dwImageBase]
          .WHILE DWORD PTR [EDX]            ;获取一个IID的函数的个娄
             INC  ECX
             ADD  EDX, 4
          .ENDW       
          ADD  ESI, SIZEOF sItInfo        ;指向下一个IID
       .ENDW
       
; 申请内存
       XOR  EDXEDX
       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        ;EAX = 申请到的内存地址
       MOV  [EDI].pNextStub, EAX    
       ASSUME EDI : NOTHING
       POP  ESI
    .ENDIF    

    ; start with the real routine    
    ;解密DLL名字
    .WHILE [esi].FirstThunk != 0
       mov ebx,[esi].DllNameRVA
       add ebx,[ebp+dwImageBase]
       MOV EAX,EBX
       CALL EnDeCryptString
       LEA EAX, [EBP+InitITContinue1]        ; goto InitITContinue1
       PUSH EAX
       RET  ;JMP InitITContinue1,加载DLL
       
;*************************************************************************       
    ; 参数;eax = 要解密的字符串的VA
   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]        ;加载DLL
       test eax,eax
       jz SkipInitIt
    
;下面要销毁DLL名字
       PUSH EAX                      
       test [ebp+PROTECTION_FLAGS],DESTROY_IMPORT_FLAG
       jz DontKillDllName
       LEA EAX, [EBP+OFFSET DontKillDllName]
       PUSH EAX         ;KillString的返回地址
       MOV EAXEBX
       JMP KillString            ;销毁DLL名字
   DontKillDllName:
          POP EBX         ;EBX = DLL 的句柄
    
       ; process the (Original-)FirstThunk members
       mov ecx,[esi].OrgFirstThunk
       .IF ecx == 0
          mov ecx,[esi].FirstThunk
       .ENDIF
       add ecx,[ebp+dwImageBase]            ;ECX指向IMAGE_THUNK_DATA
       mov edx,[esi].FirstThunk
       add edx,[ebp+dwImageBase]            ; edx - IAT ,IMAGE_IMPORT_BY_NAME
       .WHILE dword ptr [ecx] != 0
          test dword ptr [ecx],IMAGE_ORDINAL_FLAG32           ;判断是否由序号引出
          jnz @@OrdinalImp
      
          ; process a name import
          mov dword ptr eax,[ecx]            ;EAX = IMAGE_IMPORT_BY_NAME
          add eax,2                            ;跳过Hint
          add eax,[ebp+dwImageBase]     ;EAX = API NAME
          PUSH EAX
          CALL EnDeCryptString                ;解密APINAME
          POP  EAX
          mov edi,eax                    ; 保存API名字指针,后面会用来销毁API名字
          push edx
          push ecx          ;保存IMAGE_THUNK_DATA 指针
          push eax                    
          push ebx
          call [ebp+_GetProcAddress]            ;获取函数地址
          .IF eax == NULL
             pop ecx
             pop edx
             jmp SkipInitIt
          .ENDIF
          pop ecx
          pop edx
          ;销毁API名字,这段很像 Acprotect中销毁字符串的代码
            PUSHAD
          test [ebp+PROTECTION_FLAGS],DESTROY_IMPORT_FLAG        
            JZ  DontKillApiName
            LEA EAX, [EBP+OFFSET DontKillApiName]         ;保存返回地址
            PUSH EAX
          MOV EAXEDI
          JMP KillString            ;销毁
   DontKillApiName:          
          POPAD
          ;-> paste API address
          mov dword ptr [edx],eax              ;API地址到填充IAT
          jmp @@NextThunkPlease            
          
   @@OrdinalImp:
              ; process an ordinal import
              push edx
              push ecx                                         ; save the thunk pointers
              mov dword ptr eax,[ecx]        ;取得函数序号
              sub eax,080000000h                ;去掉高位
              push eax
              push ebx
              call [ebp+_GetProcAddress]            ;获取API函数地址
              test eax,eax
              jz SkipInitIt
              pop ecx
              pop edx
              mov dword ptr [edx],eax                ;写到IAT
              
   ;下面是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]            ;如果是NT下的非系统DLL则跳过
                    .IF EBX < 070000000h || EBX > 077FFFFFFh
                        JMP SkipThunkRed
                    .ENDIF
                .ELSE
                    .IF EBX < 080000000h                ;98下的非系统DLL同样跳过
                        JMP SkipThunkRed
                    .ENDIF
                .ENDIF
                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  EAXESI
                SUB  EAX, 5            ; eax - esi -5 得到 JMP xxxxx后面的偏移
                MOV  BYTE PTR [ESI], 0E9h        ;写入E9--JMP 
                MOV  DWORD PTR [ESI+1], EAX    ;将函数地址写进去
                ADD  [EDI].pNextStub, SIZEOF sApiStub        ;指向一个sApiStub
                ASSUME EDI : NOTHING
                POP  ESI
                POP  EDI
          SkipThunkRed:
             .ENDIF
             
          add ecx,4            ;下一个IMAGE_THUNK_DATA
          add edx,4            ;下一个IMAGE-IMPORT_BY_NAME
       .ENDW    
       add esi,SIZEOF sItInfo                    ; ESI = 下一个IID
    .ENDW
    assume esi:nothing
    xor eax,eax
    inc eax
SkipInitIt:

    .IF eax != TRUE
       ; exit
       popad
       ret
    .ENDIF
    
      ;----- ERASE PE HEADER ------
      test [ebp+PROTECTION_FLAGS],ERASE_HEADER_FLAG
      jz SkipEraseHeader
      
;下面是销毁PE头。好猥琐!
    mov edi,[ebp+dwImageBase]
    add edi,[edi+03Ch]                                        ;PE头
    assume edi : ptr IMAGE_NT_HEADERS            
    mov ecx,[edi].OptionalHeader.SizeOfHeaders        ;大小
    mov esi,[ebp+dwImageBase]                                ;ESI= 镜像基址
    assume edi : nothing
   ZeroMemLoop:                ;用0填充掉
        mov byte ptr [esi],0
        inc esi
        loop ZeroMemLoop
  SkipEraseHeader:
  
      ;------ CHECK AGAIN LOADER CRC & COMPARE ------
      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  EAXEBX            ;检验校验和是否相等
      .IF !ZERO?                ;不等返回
         JMP SM12
         DB  02Ch
         SM12:
         POPAD
         JMP SM13
         DB  0E8h
         SM13:
         RET
      .ENDIF
  
 ;解密    OEP_JUMP_CODE 
      LEA  EDI, [EBP+OFFSET OEP_JUMP_CODE_START]
      MOV  ESIEDI
      MOV  ECX, CRYPT_OEP_JUMP_SIZE
    XOR  EBXEBX
   OepJumpDecryptLoop:            ;下面是解密算法
        LODSB
       XOR  AL, OEP_JUMP_ENCRYPT_NUM
       SUB  ALBL
       ROL  AL, 2
       STOSB
       INC EBX
       LOOP OepJumpDecryptLoop
 ;跳到OEP
OEP_JUMP_CODE_START:
    ;----- CHECK FOR DEBUG API's -----
    LEA EAX, [EBP+OFFSET szIsDebuggerPresent]
    PUSH EAX
    PUSH [EBP+dwKernelBase]
    CALL [EBP+_GetProcAddress]            ;获取 IsDebuggerPresent 函数地址
    OR   EAXEAX        ; 95系统下不支持这个函数
    .IF !ZERO?
       CALL EAX            ;调用 IsDebugPresent检测调试器
       OR   EAXEAX
       .IF  !ZERO?        ;存在调试器,直接返回
           POPAD
          RET
       .ENDIF
    .ENDIF
    
    ;------ SECOND SI CHECK ------
    ; doesn't work on NT
    ; install SEH frame
    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  EBXEBX
    LEA  EAX, [EBP+OFFSET SehHandler2]
    PUSH EAX
    PUSH FS:[EBX]
    MOV  FS:[EBX], ESP
    MOV  EDIEBP

    MOV  EAX, 4400h
    JMP SM4
    DB 0C7h
   SM4:
    INT  68h
   SICheck2_SP:    
        XOR  EBXEBX
    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
        
        ; 恢复原来的SEH链
        MOV  EDI, [EAX].regEsp
        PUSH [EDI]
        XOR  EDIEDI
        POP  FS:[EDI]
        
        ;恢复原来的SEH链
        ADD  [EAX].regEsp, 8
        
        MOV  EDI, [EAX].regEbx    ; EDI = 加密的 OEP
        ROL  EDI, 7                ;解密得到真正的OEP
        MOV  [EAX].regEip, EDI    ;设置EIP到OEP
        
        mov  EAX,ExceptionContinueExecution            ;继续执行,会跳到OEP
        ASSUME EAX : NOTHING
        POP  EDI
        RET
    SehHandler_OEP_Jump ENDP
;*******************************************************************************************

   OepJumpCodeCont:
   
    ;用0填充掉DepackerCode 到 SehHandler_OEP_Jump的代码
    XOR  AL,AL
    LEA  EDI, [EBP+OFFSET DepackerCode]
    MOV  ECX, (OFFSET SehHandler_OEP_Jump - OFFSET DepackerCode)
   LoaderZeroLoop:
    STOSB
    LOOP LoaderZeroLoop
    
    ;用0填充掉从LOADER_CRYPT_END到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    ; RESTORE STARTUP REGS
        ; After this POPAD:
        ; EAX - OEP Seh handler
        ; EBX - OEP (rored)
    
      ;安装SEH,在SehHandler中跳到OEP---    
    PUSH EAX
    XOR  EAXEAX
    PUSH FS:[EAX]
    MOV  FS:[EAX], ESP

    JMP  SM3
    DB   087H        ;花指令
   SM3:         ; 跳到这里执行,因为下面的代码已经用0填充,所以会异常。中到

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]        ;SaveEip压栈
    pop  [eax].regEip                ;弹出到regEip。更改EIP的地址,程序会转到SaveEip去执行
    MOV  [eax].regEbp, EDI        ;还原EBP
    MOV  [EAX].regEax, 4         ; SI NOT detected !
    mov  EAX,ExceptionContinueExecution  ;返回到SaveEip去
    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:















PER.ASM

; -> 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 -----
PERItems                   EQU 14

;----- CONST ----
.CONST
; all opcodes are in reverse order

;下面保存的是加密和解密的成对代码表
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


;*******************************************************************************
;函数功能:    得用上面的加解密表产生随机的加密和解密代码
;参数:        pEncryptBuff      加密代码
;                pDecryptBuff        解码代码,和加密代码对应
;                dwSize                要填充的缓冲区的大小
;                返回值                没有
;*******************************************************************************

MakePER PROC pEncryptBuff : LPVOID, pDecryptBuff : LPVOID, dwSize : DWORD
    LOCAL dwCurRandNum : DWORD
    
    ; prepare some things
    MOV  EDI, pEncryptBuff                    ; EDI -> EncryptBuffer
    MOV  ESI, pDecryptBuff                    ; ESI -> DecryptBuffer
    ADD  ESI, dwSize                    ; ESI will be filled from down to top
    
    ; generate !
    .REPEAT   
       ; get a random PER Item
       PUSH PERItems
       CALL rand            ;产生一个不大于14的随机数
       MOV  EBX, SIZEOF sPERTable;
       XOR  EDXEDX
       MUL  EBX
       ADD  EAXOFFSET PERTable
       XCHG EAXEDX                       ;EDX指向一组加解密指令
       
       ASSUME EDX : PTR sPERTable
       
       ; is this item too big
       MOV  EBX, [EDX].dwSize
       CMP  EBX, dwSize                    ;比较有没有足够的空间来保存代码
       JG   Retry
       
       ;把表中的加密指令数据复制到加密代码区
       MOV  ECX, [EDX].dwSize
       MOV  EAX, [EDX].dwEncrypt       

       MOV  ECX, [EDX].dwSize
       .WHILE ECX != 0
           MOV  BYTE PTR [EDI], AL        ;把多态指令的一个字节填充到缓冲区
           ADD  EDI, 1
           ROR  EAX, 8        ;右移8位,AL指向下一字节
           DEC  ECX
       .ENDW

       ; generate the random num
       MOV  EAX, [EDX].RandNumType
       .IF  EAX == 1 || EAX == 2        ;判断指令对中是否需要填充随机数
           MOV  EBXEDI
           SUB  EBX, 1            ;EBX指向[EDX].Encrypt的最后一个字节
           PUSH 0F8h
           CALL rand                ;产生一个小于0F8h的随机数
           INC  EAX                ; 加1避免产生的随机数为0!
           MOV  dwCurRandNum, EAX            ;保存现在的随机数,解密的时候要用到
           MOV  BYTE PTR [EBX], AL        ;填写随机数到[EDX].Encrypt的最后一个字节

       .ENDIF       
 
       ; update variables/pointers
       MOV  EAX, [EDX].dwSize
       SUB  dwSize, EAX            ;重新计算dwSize大小
       
       ;-> decryption buffer
       MOV  ECX, [EDX].dwSize
       MOV  EAX, [EDX].dwDecrypt       
       SUB  ECX, 1
       .WHILE ECX != 0            ;使EAX指向[EDI].dwDecrypt末尾
            ROR  EAX, 8
            DEC  ECX
       .ENDW

       MOV  ECX, [EDX].dwSize
       .WHILE ECX != 0
           SUB  ESI, 1             ;ESI指向pDecryptBuff的首地址
           MOV  BYTE PTR [ESI], AL        ;填充AL到pDEcryptBuff
           ROL  EAX, 8            ;这里用左移EAX取得下一字节
           DEC  ECX
       .ENDW
       
       ; generate the random num
       MOV  EAX, [EDX].RandNumType
       .IF  EAX == 1
           MOV  EBXESI
           ADD  EBX, 2        ;加2指向最后一个字节
           MOV  EAX, dwCurRandNum    
           MOV  BYTE PTR [EBX], AL        ;填充随机数到最后一个字节
           
       .ELSEIF EAX == 2
           MOV  EBXESI
           ADD  EBX, 1            ;加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  EDXEDX
    MOV  ECX, 32
    BitLoop:
        SHR   EAX, 1
        .IF   CARRY?
            XOR  EAX, 013245769h
        .ENDIF
    LOOP BitLoop
    
    ; force dwRange
    XOR  EDXEDX
    MOV  EBX, dwRange
    DIV  EBX
    
    XCHG EAXEDX

    RET
rand ENDP

;随机种子函数*********************************

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





yoda' cryptor 分析总结

这个壳机基本上实现了加密壳要实现的东西。总结其特点如下
1.用到了多态(作者说是多态),动态的生成加解密函数
2.API重定向,不过也仅仅是重定向。在重定向到的代码中直接跳到原系统函数了。很容易还原
3.兼容性比较好,各种可能性都考虑到了
4.我分析的这个版本没有处理重定位,不能对DLL加壳
5.作了一些手脚来反调试器和防止脱壳,例如调用IsDebuggerPresent.壳代码执行完后擦除PE头。
  修改LDR_MODULE的进程映像大小来anti dump。用SEH异常来跳到OEP,加花指令等等



最后我注后的这些文件不能编译成功,老提示windows.inc里面有错误。可能是我分析 的时候不小心修改了哪 里造成的错误。我也懒得找了。谁找到的可以告诉我<0_O>,编译可以直接下载原来没有注释的文件来编译。  

                  By Demoscene
                  2010.05.18

上传的附件 yoda's crypt 1.2源码.rar