-
-
[原创]yoda's Crypter 1.2源码再分析
-
发表于: 2013-6-14 01:21 10311
-
希望通过学习yoda's Crypter 1.2 加强对脱壳技术的理解,只是菜鸟学习的笔记而已。
demoscene也发过关于这个壳源码的分析帖子,在学习这个壳的时候我也参考了这个帖子很多地方,有些地方则是自己调试明白的。
看完这个壳的源码,感觉对脱壳有点概念了 而不是跟着教程 第一步, 第二步.......
工具准备
1 yoda's Crypter 1.2源码
下载地址 116K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4m8W2k6r3W2&6i4K6u0W2j5$3!0E0i4K6u0r3M7$3!0#2M7X3y4W2j5$3!0V1k6g2)9J5c8Y4m8S2j5$3E0Q4x3X3g2Z5N6r3@1`.
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
里面的函数有
大体是这样,其他的详细再看了
即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
这个大概可以猜出意思来,略。
yc.asm
头文件,库包含
窗口过程声明
这上面的分别对应加密选项
hInst 是程序自身的句柄
hDlg 是对话框窗口的句柄
ofn 是打开文件对话框用的结构
cFname 是文件名
CryptStuff.asm
很诚实的说,我自己对这个文件的代码都还有点迷惑。要重新看一次和进去各个子程序看看才会明白。分析完整个源码之后再在OD里面感受一下壳的流程
下面将CryptFile函数调用的子程序都看一下(基本上按顺序)
置时间为随机数种子,一遍之后的rand函数可以产生随机数
AssembleIT这个函数是在为壳引导代码的运行提供一个输入表, 这个输入表将被当做系统填写IAT的根据, 而且只包含了一个动态链接库中的两个函数
就是LoadLibrary和GetProcAdress
这个函数里的后面我没有写注释了, 比较难组织语言,看后面的解释吧
当Assemble函数整个执行完之后,新节, 也就是新输入表的内存视图如下

因为有一些数据结构在MakePER里出现得较多,所以我整个PER.ASM文件一起贴在这里了,重点看MakePER函数吧
用MakePER生成了加密/解密函数之后,下面用CryptPE将各个节加密
最后来一个在CryptFile最后用到的校验和计算函数:
至此只剩下DepackerCode未分析了,就快完成(其实上面的代码也在DepackerCode标号里)
如果这段代码是独立编译的话,看起来好像很奇怪,因为offset CallMe本来就等于ebp的值了啊,相减的话不等于0吗,等于0有什么用?(解释一下pop ebp获取到call下一行的代码)
但是这段程序不是独立编译的,而是放到其他软件上面运行,所以offset CallMe是本来的地址,而ebp中获取到的是现在的地址
看看OD反汇编代码:
两个相减得到壳加在软件身上产生的偏差,得到这个偏差之后,如果以后要访问其他的变量就可以这么做
(401E3B是一个编译器生成的常量的地址)
这个在之前的代码经常见了,用来解密壳代码(壳从LOADER_CRYPT_START开始的代码)
------------------------------------------------------------------
花了一天时间学习了一下SEH,继续学习yoda壳!
比较校验和
demoscene也发过关于这个壳源码的分析帖子,在学习这个壳的时候我也参考了这个帖子很多地方,有些地方则是自己调试明白的。
看完这个壳的源码,感觉对脱壳有点概念了 而不是跟着教程 第一步, 第二步.......
工具准备
1 yoda's Crypter 1.2源码
下载地址 116K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4m8W2k6r3W2&6i4K6u0W2j5$3!0E0i4K6u0r3M7$3!0#2M7X3y4W2j5$3!0V1k6g2)9J5c8Y4m8S2j5$3E0Q4x3X3g2Z5N6r3@1`.
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 ENDPAssembleIT这个函数是在为壳引导代码的运行提供一个输入表, 这个输入表将被当做系统填写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], EBXmov 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:
[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
他的文章
- 想去逆向培训,去前学点什么 5921
- [分享]一个Getting harder的Keygenme 6781
- [讨论]软件逆向相关的行业如何 3094
- [分享]跟了下CRC32的CrackMe 8428
- [分享]跟了一下qq-msg 14751
赞赏
雪币:
留言: