-
-
[原创]yoda's Crypter 1.2源码再分析
-
发表于: 2013-6-14 01:21 10082
-
希望通过学习yoda's Crypter 1.2 加强对脱壳技术的理解,只是菜鸟学习的笔记而已。
demoscene也发过关于这个壳源码的分析帖子,在学习这个壳的时候我也参考了这个帖子很多地方,有些地方则是自己调试明白的。
看完这个壳的源码,感觉对脱壳有点概念了 而不是跟着教程 第一步, 第二步.......
工具准备
1 yoda's Crypter 1.2源码
下载地址 http://www.pediy.com/sourcecode/pack.htm
2 MASM
下载地址 百度
编译源码(这一步折腾了很久,环境变量设不成功唯有用绝对路径了)
E:\masm32\bin\Cvtres.exe /machine:ix86 rsrc.res
E:\masm32\bin\ml.exe /c /coff /Zp1 yc.asm
E:\masm32\bin\Link.exe /SUBSYSTEM:WINDOWS /MERGE:.idata=.text /MERGE:.data=.text /MERGE:.rdata=.text /SECTION:.text,EWR /IGNORE:4078 yc.obj rsrc.obj
注意一下,源码里面的temptfile.asm是没用的。
了解yoda的大致功能
yoda是一个加密壳,在加密PE文件的同时可以
1 检测Softice调试器
2 擦除PE头
3 CRC自校验
4 反Dump
5 删除原输入表信息
6 API重定向
现在还不太清楚这些功能具体是什么, 不过看了代码之后应该就知道了!
各个文件里面大概做了什么
CryptStuff.ASM
里面的函数有
大体是这样,其他的详细再看了
即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源码
下载地址 http://www.pediy.com/sourcecode/pack.htm
2 MASM
下载地址 百度
编译源码(这一步折腾了很久,环境变量设不成功唯有用绝对路径了)
E:\masm32\bin\Cvtres.exe /machine:ix86 rsrc.res
E:\masm32\bin\ml.exe /c /coff /Zp1 yc.asm
E:\masm32\bin\Link.exe /SUBSYSTEM:WINDOWS /MERGE:.idata=.text /MERGE:.data=.text /MERGE:.rdata=.text /SECTION:.text,EWR /IGNORE:4078 yc.obj rsrc.obj
注意一下,源码里面的temptfile.asm是没用的。
了解yoda的大致功能
yoda是一个加密壳,在加密PE文件的同时可以
1 检测Softice调试器
2 擦除PE头
3 CRC自校验
4 反Dump
5 删除原输入表信息
6 API重定向
现在还不太清楚这些功能具体是什么, 不过看了代码之后应该就知道了!
各个文件里面大概做了什么
CryptStuff.ASM
里面的函数有
CryptFile PROC szFname : LPSTR, hDlg : HWND,dwProtFlags : DWORD EncryptSec esi=CryptStart ecx = CryptSize AddSection PROC USES edi esi ebx ecx edx, pMem_ : LPVOID AssembleIT PROC USES ebx ecx edx esi edi, pAddress4IT : LPVOID, dwNewSectionVA : DWORD ProcessTlsTable PROC USES edi ebx esi ecx, pFileMem : LPVOID, CryptSectionVA : DWORD ProcessOrgIT PROC USES edi esi edx, pFileImage : LPVOID, pITBaseRO : LPVOID PEAlign PROC USES ecx edx, dwTarNum : DWORD, dwAlignTo : DWORD RVA2Offset PROC USES ebx ecx edx, Base : DWORD,dwITRVA : DWORD DepackerCode
大体是这样,其他的详细再看了
即CryptStuff.ASM负责将PE文件加密,处理各个表(输入表/Tls等)
然后壳的引导段也是在这个文件里,即DepackerCode
yC.asm
主要是处理界面的问题,将用户选择的文件,加密选项传入CryptStuff.ASM的函数CryptFile处理
PER.ASM
主要跟产生随机数有关的代码,之后的加密部分可能需要用到随机数
Rsrc.res
资源文件
resource.inc
资源头文件
详细分析源代码
就是说现在我们大概读代码的顺序是
resource.inc
yc.asm
CryptStuff.ASM
PER.ASM
resource.inc
IDD_MAINDLG equ 100 IDC_TARGETFILE equ 1000 IDC_CHOOSEFILE equ 1001 IDC_CLOSE equ 1002 IDC_CRYPT equ 1003 IDC_SICHECK equ 2001 IDC_ERASEPEHEADER equ 2002 IDC_DESTROYIMPORT equ 2003 IDC_CHECKHEADERCRC equ 2004 IDC_ANTIDUMP equ 2005 IDC_APIREDIRECT equ 2006 IDI_ICON equ 9000
这个大概可以猜出意思来,略。
yc.asm
.386 .model flat, stdcall option casemap:none include \masm32\include\kernel32.inc include \masm32\include\user32.inc include \masm32\include\comdlg32.inc include \masm32\include\shell32.inc include \masm32\include\imagehlp.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\comdlg32.lib includelib \masm32\lib\shell32.lib includelib \masm32\lib\imagehlp.lib include \masm32\include\windows.inc include resource.inc
头文件,库包含
DlgProc PROTO :HWND, :UINT, :WPARAM, :LPARAM
窗口过程声明
;------------ CONST --------- .const CHECK_SI_FLAG equ 1 ERASE_HEADER_FLAG equ 2 DESTROY_IMPORT_FLAG equ 4 CHECK_HEADER_CRC equ 8 ANTI_DUMP_FLAG equ 16 API_REDIRECT_FLAG equ 32
这上面的分别对应加密选项
szFilter db "ExE files",0,"*.exe",0,"All files",0,"*.*",0,0 szCurDir db ".",0 szNoFile db "No file selected up to now !",0 szErr db "ERROR",0
szFilter是给打开文件对话框用的过滤字符串 szCurDir当前目录 szNoFile文件未选择的错误信息 szErr 未知错误信息
;------------ DATA ---------- .data hInst dd 0 hDLG dd 0 ofn OPENFILENAME <> cFname db MAX_PATH dup (0)
hInst 是程序自身的句柄
hDlg 是对话框窗口的句柄
ofn 是打开文件对话框用的结构
cFname 是文件名
;------------ CODE ---------- .code include CryptStuff.ASM include PER.ASM main: invoke GetModuleHandle,0 mov hInst,eax invoke DialogBoxParam,eax,IDD_MAINDLG,0,offset DlgProc,0 THEEND: invoke ExitProcess,0 包含两个文件,之后要用到 得到程序的句柄 打开对话框窗口,资源ID是IDD_MAINDLG,窗口处理过程是DlgProc 下面这个文件较多,直接写注释了 DlgProc proc hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL hDrop : HANDLE pushad .IF uMsg == WM_INITDIALOG invoke LoadIcon,hInst,IDI_ICON invoke SendMessage,hDlg,WM_SETICON,TRUE,eax ;设置图标为IDI_ICON push hDlg pop hDLG ; check the checkboxes :) invoke CheckDlgButton,hDlg,IDC_CHECKHEADERCRC,TRUE invoke CheckDlgButton,hDlg,IDC_DESTROYIMPORT,TRUE invoke CheckDlgButton,hDlg,IDC_ANTIDUMP,TRUE INVOKE CheckDlgButton,hDlg,IDC_APIREDIRECT,TRUE ;默认选上某几个选项 invoke DragAcceptFiles,hDlg,TRUE ;支持文件拖拽 .ELSEIF uMsg == WM_DROPFILES push wParam pop hDrop invoke DragQueryFile,hDrop,0,offset cFname,sizeof cFname invoke DragFinish,hDrop invoke SetDlgItemText,hDlg,IDC_TARGETFILE,offset cFname ;得到拖拽的文件名放到文本框IDC_TARGETFILE中 .ELSEIF uMsg == WM_COMMAND mov eax,wParam .IF ax == IDC_CLOSE invoke SendMessage,hDlg,WM_CLOSE,NULL,NULL ;关闭按钮 .ELSEIF ax == IDC_CHOOSEFILE ; get a file path mov ofn.lStructSize,SIZEOF ofn mov ofn.lpstrFilter,offset szFilter push hDlg pop ofn.hwndOwner mov ofn.lpstrFile, offset cFname mov ofn.nMaxFile,SIZEOF cFname mov ofn.lpstrInitialDir,offset szCurDir mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_HIDEREADONLY push offset ofn ;填充打开文件对话框要用的结构 call GetOpenFileName ;打开文件对话框 test eax,eax jz @@ExitDlgProc invoke SetDlgItemText,hDlg,IDC_TARGETFILE,offset cFname ;得到的文件名放到文本框IDC_TARGETFILE中 .ELSEIF ax == IDC_CRYPT ; ----- was a file selected ? ----- mov eax,offset cFname .IF byte ptr [eax] == 0 invoke MessageBox,hDlg,offset szNoFile,offset szErr,MB_ICONERROR jmp @@ExitDlgProc .ENDIF ;如果没有选择文件则弹出错误 ; ---- build the protection flag ----- xor edi,edi invoke IsDlgButtonChecked,hDlg,IDC_SICHECK .IF eax == BST_CHECKED or edi,CHECK_SI_FLAG .ENDIF invoke IsDlgButtonChecked,hDlg,IDC_ERASEPEHEADER .IF eax == BST_CHECKED or edi,ERASE_HEADER_FLAG .ENDIF invoke IsDlgButtonChecked,hDlg,IDC_DESTROYIMPORT .IF eax == BST_CHECKED or edi,DESTROY_IMPORT_FLAG .ENDIF invoke IsDlgButtonChecked,hDlg,IDC_CHECKHEADERCRC .IF eax == BST_CHECKED or edi,CHECK_HEADER_CRC .ENDIF invoke IsDlgButtonChecked,hDlg,IDC_ANTIDUMP .IF eax == BST_CHECKED or edi,ANTI_DUMP_FLAG .ENDIF INVOKE IsDlgButtonChecked,hDlg,IDC_APIREDIRECT .IF EAX == BST_CHECKED OR EDI, API_REDIRECT_FLAG .ENDIF ;用edi的32位来存储加密选项信息 push edi push hDlg push offset cFname call CryptFile ;调用加密文件 .ENDIF .ELSEIF uMsg == WM_CLOSE invoke EndDialog,hDlg,0 .ENDIF popad @@ExitDlgProc: xor eax,eax ret DlgProc endp end main
CryptStuff.asm
;------ MACROS ----- PUPO MACRO pSrc, pDest PUSH pSrc POP pDest ENDM ;------ DEFINITIONS ------- DEPACKER_CODE_SIZE equ (offset DepackerCodeEnd - offset DepackerCode) CHECKSUM_SKIP_SIZE equ 5 ; (don't include the saved checksum itself in the checksum calculation) TLS_BACKUP_ADDR equ (offset TlsBackupLabel - offset DepackerCode) CHECKSUM_ADDR equ (OFFSET ChecksumLabel - OFFSET DepackerCode) CRYPT_LOADER_SIZE_DB EQU (OFFSET LOADER_CRYPT_END - OFFSET LOADER_CRYPT_START) CRYPT_OEP_JUMP_SIZE equ (OFFSET OEP_JUMP_CODE_END - OFFSET OEP_JUMP_CODE_START) IT_SIZE equ 060h MAX_SECTION_NUM equ 20 MAX_IID_NUM equ 30 OEP_JUMP_ENCRYPT_NUM equ ('y') LOADER_CRC_CHECK_SIZE equ (OFFSET OEP_JUMP_CODE_START - OFFSET DepackerCode) VAR_PER_SIZE EQU 030h SEC_PER_SIZE EQU 030h ;------- CONST -------- .const szDone db "File encrypted successfully !",0 szDoneCap db ":)",0 szFileErr db "File access error :(",0 szNoPEErr db "Invalid PE file !",0 szNoMemErr db "Not enough memory :(",0 szFsizeErr db "Files with a filesize of 0 aren't allowed !",0 szNoRoom4SectionErr db "There's no room for a new section :(",0 szSecNumErr db "Too many sections !",0 szIIDErr DB "Too much ImageImportDescriptors !",0 ALIGN_CORRECTION dd 01000h ; this big value is e.g. needed for WATCOM compiled files DEPACKER_SECTION_NAME dd ('Cy') szKernel db "KeRnEl32.dLl",0 szLoadLibrary db "LoadLibraryA",0 szGetProcAddress db "GetProcAddress",0 ;------- DATA --------- .data pMap dd 0 dwBytesRead dd 0 dwBytesWritten dd 0 pMem dd 0 dwFsize dd 0 dwOutPutSize dd 0 dwNewFileEnd dd 0 dwNTHeaderAddr dd 0 dwSectionNum dd 0 dwNewSectionRO dd 0 dwOrgITRVA dd 0 hFile dd 0 ;------- CODE --------- .code CryptFile PROC szFname : LPSTR, hDlg : HWND,dwProtFlags : DWORD assume fs : nothing CALL InitRandom ;初始化随机种子 ;----- MAP THE FILE ----- invoke CreateFile,szFname,GENERIC_WRITE + GENERIC_READ,FILE_SHARE_WRITE + FILE_SHARE_READ,\ NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 cmp eax,INVALID_HANDLE_VALUE jz FileErr ;文件打开不成功,退出 mov hFile,eax invoke GetFileSize,hFile,0 .IF eax == 0 push hFile call CloseHandle jmp FsizeErr ;文件大小等于零,退出 .ENDIF mov dwFsize,eax mov eax,dwFsize add eax,IT_SIZE ;加上壳的输入表大小 add eax,DEPACKER_CODE_SIZE ;加上壳引导代码大小,DEPACKER_CODE_SIZE=DepackerCodeEnd - DepackerCode,这个文件的最后面是壳引导代码,其前后有标号DepackerCode和DepackerCodeEnd add eax,ALIGN_CORRECTION ;ALIGN_CORRECTION这个值是为了支持一个古老的C/C++编译器 Watcom mov dwOutPutSize,eax push eax push GMEM_FIXED + GMEM_ZEROINIT call GlobalAlloc .IF eax == NULL push hFile call CloseHandle jmp MemErr ;申请存放加壳文件的空间 .ENDIF mov pMem,eax invoke ReadFile,hFile,pMem,dwFsize,offset dwBytesRead,NULL ; ----- check the PE Signature and get some needed values ----- mov edi,pMem .IF word ptr [edi] != 'ZM' push pMem call GlobalFree push hFile call CloseHandle jmp PEErr ;如果没有MZ则退出 .ENDIF add edi,[edi+3Ch] .IF word ptr [edi] != 'EP' push pMem call GlobalFree push hFile call CloseHandle jmp PEErr ;如果没有PE标志则退出 .ENDIF mov dwNTHeaderAddr,edi assume edi : ptr IMAGE_NT_HEADERS push [edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress pop dwOrgITRVA ;获取输入表RVA push word ptr [edi].FileHeader.NumberOfSections pop word ptr dwSectionNum ;获取节数量 .IF dwSectionNum > MAX_SECTION_NUM JMP SecNumErr ;获取的数据错误 .ENDIF push [edi].OptionalHeader.AddressOfEntryPoint pop dwOrgEntryPoint ;获取原入OEP push [edi].OptionalHeader.ImageBase pop dwImageBase ;获取基址 ;----- DELETE Bound Import & IAT DIRECTORIES ----- XOR EAX, EAX MOV ECX, 4 LEA EDI, [EDI].OptionalHeader.DataDirectory[11 *SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress assume edi : nothing DirDelLoop: STOSD LOOP DirDelLoop ;每次4字节,填4次,把绑定导入表删掉 ;----- ENCRYPT DLL/API NAMES & SAVE IT & DESTROY IID's ----- PUSH dwOrgITRVA PUSH pMem CALL RVA2Offset PUSH EAX PUSH pMem CALL ProcessOrgIT ;处理输入表的函数,加密,保存一些信息之类的 OR EAX, EAX .IF ZERO? PUSH pMem CALL GlobalFree PUSH hFile CALL CloseHandle JMP IIDErr ;处理输入表失败,退出 .ENDIF ;----- ADD THE PACKER SECTION ----- push pMem call AddSection .IF eax == 0 push pMem call GlobalFree push hFile call CloseHandle jmp NoRoom4SectionErr ;加入壳段,返回其在节表的结构的地址 .ENDIF ;----- CREATE PACKER IMPORT TABLE ----- xchg eax,esi ; esi -> pointer to Section Header of the new section assume esi : ptr IMAGE_SECTION_HEADER mov eax,[esi].PointerToRawData MOV dwNewSectionRO, EAX add eax,pMem push [esi].VirtualAddress push eax call AssembleIT ;也是跟输入表有关 ;---- REPLACE TLS TABLE ----- push [esi].VirtualAddress push pMem call ProcessTlsTable ;处理TLS表 ;------ ENCRYPT THE SECTIONS ----- pushad ; generate PER PUSH SEC_PER_SIZE PUSH OFFSET SecDecryptBuff PUSH OFFSET SecEncryptBuff CALL MakePER ;随机生成加密解密代码 ; encrypt ! mov eax,pMem mov ebx,0 call CryptPE ;用生成的加密函数来加密各节 popad ; ----- UPDATE PE HEADER ----- mov edi,dwNTHeaderAddr assume edi : ptr IMAGE_NT_HEADERS ; edi -> pointer to PE header ; ImportTable RVA ... push [esi].VirtualAddress pop [edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress ;新建的节开头作为输入表,放到数据目录输入表信息里 ; EntryPoint... mov eax,[esi].VirtualAddress add eax,IT_SIZE mov [edi].OptionalHeader.AddressOfEntryPoint,eax ;输入表之后紧跟壳代码 ; SizeOfImage ... mov eax,[esi].VirtualAddress add eax,[esi].Misc.VirtualSize mov [edi].OptionalHeader.SizeOfImage,eax ;不知道什么意思 先跳过 ; save protection flags... push dwProtFlags pop PROTECTION_FLAGS ;不知道什么意思 先跳过 assume esi : nothing assume edi : nothing ; ----- CALCULATE THE NEW EOF ----- mov eax,dwNewSectionRO add eax,IT_SIZE add eax,DEPACKER_CODE_SIZE mov dwNewFileEnd,eax ;计算文件结尾位置 ; ----- COPY LOADER CODE TO FILE MEMORY & DO CHECKSUM STUFF ------ mov edi,dwNewSectionRO add edi,IT_SIZE add edi,pMem mov esi,offset DepackerCode mov ecx,DEPACKER_CODE_SIZE rep movsb ;将壳引导代码复制到新建的节里 ;----- ENCRYPT OEP JUMP CODE ----- MOV EDI, pMem ADD EDI, dwNewSectionRO ADD EDI, IT_SIZE ADD EDI, (OFFSET OEP_JUMP_CODE_START - OFFSET DepackerCode) MOV ESI, EDI MOV ECX, CRYPT_OEP_JUMP_SIZE XOR EBX, EBX OepJumpEncryptLoop: LODSB ROR AL, 2 ADD AL, BL XOR AL, OEP_JUMP_ENCRYPT_NUM STOSB INC EBX LOOP OepJumpEncryptLoop ;把跳到OEP的代码加密掉 ;----- ENCRYPT LOADER ----- ; generate PER PUSH VAR_PER_SIZE MOV EAX, pMem ADD EAX, dwNewSectionRO ADD EAX, IT_SIZE ADD EAX, (OFFSET VarDecryptBuff - OFFSET DepackerCode) PUSH EAX PUSH OFFSET VarEncryptBuff CALL MakePER ; encryption ! MOV EDI, pMem ADD EDI, dwNewSectionRO ADD EDI, IT_SIZE ADD EDI, (OFFSET LOADER_CRYPT_START - OFFSET DepackerCode) MOV ECX, CRYPT_LOADER_SIZE_DB MOV ESI, EDI @@VarEncryptionLoop: LODSB VarEncryptBuff DB VAR_PER_SIZE DUP (0) STOSB LOOP @@VarEncryptionLoop ;加密壳引导代码 ;----- CALCULATE CHECKSUM ----- mov eax,pMem mov ecx,dwNewFileEnd sub ecx,CHECKSUM_SKIP_SIZE call GetChecksum mov dwOrgChecksum,eax ;计算校验和 ;----- PASTE CHECKSUM ------ MOV EAX, pMem ADD EAX, IT_SIZE ADD EAX, dwNewSectionRO ADD EAX, CHECKSUM_ADDR MOV EDX, dwOrgChecksum MOV DWORD PTR [EAX], EDX ;把校验和放到壳节里 ; ----- WRITE FILE MEMORY TO DISK ----- invoke SetFilePointer,hFile,0,NULL,FILE_BEGIN invoke WriteFile,hFile,pMem,dwOutPutSize,offset dwBytesWritten,NULL ;写入文件 ; ------ FORCE CALCULATED FILE SIZE ------ invoke SetFilePointer,hFile,dwNewFileEnd,NULL,FILE_BEGIN invoke SetEndOfFile,hFile ;不知道为什么写入文件之后会多出一些数据,所以要用之前计算好的文件结束位置来截取文件前部分的内容。 invoke MessageBox,hDlg,offset szDone,offset szDoneCap,MB_ICONINFORMATION ; ----- CLEAN UP ----- push pMem call GlobalFree push hFile call CloseHandle @@Exit: ret ;----- ERROR MESSAGES ----- MemErr: mov eax,offset szNoMemErr jmp ShowErr PEErr: mov eax,offset szNoPEErr jmp ShowErr FileErr: mov eax,offset szFileErr jmp ShowErr NoRoom4SectionErr: mov eax,offset szNoRoom4SectionErr jmp ShowErr FsizeErr: mov eax,offset szFsizeErr jmp ShowErr SecNumErr: mov eax,offset szSecNumErr jmp ShowErr IIDErr: MOV EAX, OFFSET szIIDErr JMP ShowErr ShowErr: invoke MessageBox,hDlg,eax,offset szErr,MB_ICONERROR jmp @@Exit CryptFile ENDP
很诚实的说,我自己对这个文件的代码都还有点迷惑。要重新看一次和进去各个子程序看看才会明白。分析完整个源码之后再在OD里面感受一下壳的流程
下面将CryptFile函数调用的子程序都看一下(基本上按顺序)
InitRandom PROC ; manage the random generator CALL GetTickCount PUSH EAX CALL srand RET InitRandom ENDP
置时间为随机数种子,一遍之后的rand函数可以产生随机数
RVA2Offset PROC USES ebx ecx edx, Base : DWORD,dwITRVA : DWORD ; get the pointer to the NT header mov eax,Base add eax,[eax+03Ch] invoke ImageRvaToSection,eax,Base,dwITRVA test eax,eax jz @@ExitProc xchg eax,ebx assume ebx : ptr IMAGE_SECTION_HEADER mov eax,dwITRVA sub eax,[ebx].VirtualAddress add eax,[ebx].PointerToRawData assume ebx : nothing @@ExitProc: ret RVA2Offset ENDP
先确定RVA在哪个节中,计算出这个地址离节的距离是多少 然后将这个距离加在节的文件偏移上从而得到该RVA的文件偏移 奇怪的是我竟然无法在源文件里面搜索到ImageRvaToSection这个函数,forget it.
ProcessOrgIT PROC USES edi esi edx, pFileImage : LPVOID, pITBaseRO : LPVOID LOCAL dwIIDNum : DWORD ; clear the IIDInfo array XOR EAX,EAX MOV EDI, OFFSET IIDInfo MOV ECX, SIZEOF IIDInfo ClearArrayLoop: STOSB LOOP ClearArrayLoop ;清空结构数组IIDInfo,其定义如下: ;sItInfo STRUCT ;DllNameRVA dd ? ;FirstThunk dd ? ;OrgFirstThunk dd ? ;sItInfo ENDS ; get a random number INVOKE GetTickCount XOR EAX, ("yoda") MOV EDX,EAX ; EDX -> stupid number :) ;用当前时间和"yoda"xor产生一个随机数放在edx ; start MOV dwIIDNum, 0 MOV EDI,pITBaseRO ;输入表的文件偏移 ADD EDI,pFileImage ASSUME EDI : PTR IMAGE_IMPORT_DESCRIPTOR ; EDI -> IID MOV ESI,OFFSET IIDInfo ASSUME ESI : PTR sItInfo ; ESI -> Loder IT data array .WHILE [EDI].Name1 ;最后一个IID全部为0 ; too much IID's ? INC dwIIDNum .IF dwIIDNum == (MAX_IID_NUM) XOR EAX, EAX JMP POIT_Exit ;遍历完每个IID项,退出 .ENDIF ; save IID Infos PUPO <[EDI].Name1>, <[ESI].DllNameRVA> PUPO <[EDI].OriginalFirstThunk>, <[ESI].OrgFirstThunk> PUPO <[EDI].FirstThunk>, <[ESI].FirstThunk> ;保存信息,从左到右 ;-> get dll pointer PUSH [EDI].Name1 PUSH pFileImage CALL RVA2Offset ADD EAX, pFileImage ;-> crypt string CALL EnDeCryptString ;加密DLL名,简单的移位加密而已就不看了 ;--- CRYPT API name strings --- PUSH ESI MOV ESI, [EDI].OriginalFirstThunk .IF !ESI MOV ESI, [EDI].FirstThunk ;有的PE文件没有OriginalFirstThunk .ENDIF PUSH ESI PUSH pFileImage CALL RVA2Offset MOV ESI, EAX ADD ESI, pFileImage .WHILE DWORD PTR [ESI] ; ESI -> Thunk pointer MOV EAX, [ESI] ; is it an Ordinal Import ? TEST EAX,IMAGE_ORDINAL_FLAG32 JNZ SkipApiString ;以序号输入则跳 PUSH EAX PUSH pFileImage CALL RVA2Offset OR EAX, EAX JZ SkipApiString ADD EAX, pFileImage ADD EAX, 2 ; skip the HINT ;跳过序号,加密后面的字符串 CALL EnDeCryptString SkipApiString: ADD ESI, 4 .ENDW POP ESI ; destroy Original IID MOV [EDI].Name1, EDX MOV [EDI].OriginalFirstThunk, EDX MOV [EDI].FirstThunk, EDX MOV [EDI].TimeDateStamp, EDX MOV [EDI].ForwarderChain, EDX ;用刚才产生的随机数把原来的IDD填充掉 ; EDI -> point to next IID ADD EDI,SIZEOF IMAGE_IMPORT_DESCRIPTOR ADD ESI,SIZEOF sItInfo .ENDW ASSUME ESI : NOTHING ASSUME EDI : NOTHING XOR EAX, EAX INC EAX POIT_Exit: RET ProcessOrgIT ENDP 基本上就是将原来的输入表信息保存起来,然后是擦除原输入表的信息 AddSection PROC USES edi esi ebx ecx edx, pMem_ : LPVOID LOCAL dwSecNum : DWORD mov edi,pMem_ add DWORD PTR edi,[edi+03Ch] assume edi : ptr IMAGE_NT_HEADERS ; edi -> pointer to PE header ; check whether there's room for a new section xor eax,eax mov ax,[edi].FileHeader.NumberOfSections mov dwSecNum,eax mov ecx,SIZEOF IMAGE_SECTION_HEADER imul eax,ecx ; eax contains the size of the whole section header add eax,SIZEOF IMAGE_SECTION_HEADER ; add the size being needed for our new section mov ecx,edi ; ecx -> address of PE Header sub ecx,pMem_ ; sub ecx Map Base add ecx,eax ; ecx + calculated header sizes add ecx,0F8h ; add the size of the PE header ;从dos头到节表最后一个节信息的大小相加 .IF ecx > [edi].OptionalHeader.SizeOfHeaders ;可选头是从dos头到节表最后一个节信息的大小相加,但是后面还会有一大堆0,那是因为要对齐 ;所以添加新节是否可能就看对齐值给你留的0够不够 xor eax,eax jmp @@ExitProc_AS .ENDIF ; create a new section mov esi,edi add esi,0F8h assume esi : ptr IMAGE_SECTION_HEADER ; esi -> pointer to section headers ; go to the last section mov edx,dwSecNum sub edx,1 .REPEAT ; force the writeable flag mov eax,[esi].Characteristics or eax,080000000h mov [esi].Characteristics,eax add esi,SIZEOF IMAGE_SECTION_HEADER dec edx .UNTIL edx == 0 ;将每个节表的属性改完可写 ; start to build the new section mov edx,esi add edx,SIZEOF IMAGE_SECTION_HEADER ; edx -> pointer to the new section assume edx : ptr IMAGE_SECTION_HEADER ; VirtualAddress... mov eax,[esi].VirtualAddress add eax,[esi].Misc.VirtualSize push 01000h push eax ;eax=最后一个段的结尾 call PEAlign ;得到将eax对齐之后的地址,即新节的地址 mov [edx].VirtualAddress,eax ; VirtualSize.. mov [edx].Misc.VirtualSize,02000h ;新节大小2000h ; RawSize.. mov eax,IT_SIZE add eax,DEPACKER_CODE_SIZE mov [edx].SizeOfRawData,eax ;SizeOfRawData本来应该是对齐之后的大小,而这里直接把数据相加, ;可能因为这是最后一个节 所以无所谓吧 ; Section name lea eax,[edx].Name1 push DEPACKER_SECTION_NAME pop [eax] MOV DWORD PTR [EAX+4],0 ;将yC字符串放到eax指向的内存处3 ; Characteristics mov [edx].Characteristics,0E00000E0h ; RawOffset mov eax,[esi].PointerToRawData add eax,[esi].SizeOfRawData push 0200h push eax call PEAlign ;刚才的一个PEAlign是为了内存对齐,现在的是为了文件对齐,所以对齐值不同 mov [edx].PointerToRawData,eax mov eax,edx ; eax -> will be returned ;eax指向新节在节表的位置 ;这个Add Section还没把数据放到节里面,后面会把相关的输入表,壳引导代码等放进去的 ; update the PE header inc [edi].FileHeader.NumberOfSections assume edx : nothing assume esi : nothing assume edi : nothing @@ExitProc_AS: ret AddSection ENDP
AssembleIT这个函数是在为壳引导代码的运行提供一个输入表, 这个输入表将被当做系统填写IAT的根据, 而且只包含了一个动态链接库中的两个函数
就是LoadLibrary和GetProcAdress
这个函数里的后面我没有写注释了, 比较难组织语言,看后面的解释吧
AssembleIT PROC USES ebx ecx edx esi edi, pAddress4IT : LPVOID, dwNewSectionVA : DWORD mov esi,pAddress4IT ; esi -> base of the new IT mov eax,pAddress4IT mov ecx,IT_SIZE ZeroMem: mov byte ptr [eax],0 inc eax loop ZeroMem ;新节处的IT位置清0 ; 创建一个新的输入表 :) mov ebx,esi ;ebx=esi->新的输入表=01281C20 mov eax,SIZEOF IMAGE_IMPORT_DESCRIPTOR ;eax=IID结构大小=14h xor edx,edx mov ecx,2 ;有两个IID 一个Kernel 一个空的 mul ecx ;eax=14h*2=28h ;eax=IID总大小=28h add ebx,eax ;ebx=IID结尾处 assume esi:ptr IMAGE_IMPORT_DESCRIPTOR mov eax,ebx ; process the IID Name ;eax=IID结尾处 sub eax,esi ;eax=IID总大小=28h add eax,dwNewSectionVA ;eax=新节(即壳输入表)RVA+IID总大小 mov [esi].Name1,eax ;esi指向第一个IID,将IID结尾处地址传入第一个IID的Name1字段(那么IID结尾应该紧跟dll名吧) push esi ;保存esi mov esi,offset szKernel mov edi,ebx .REPEAT lodsb stosb .UNTIL byte ptr [esi] == 0 ;把esi指向的字符串放到edi指向的空间 pop esi mov ebx,edi inc ebx mov eax,ebx ; process the FirstThunk pointers ;eax=ebx指向IID后面的字符串+1的位置A sub eax,esi ;eax=位置A离IID起始的偏移 add eax,dwNewSectionVA ;eax=位置A的RVA mov [esi].FirstThunk,eax ;用FirstThunk来存放这个位置的RVA就是说这里是一个IMAGE_THUNK_DATA数组 mov edx,ebx add edx,10 ;留出空间给Image_Thunk_Data mov eax,edx sub eax,esi add eax,dwNewSectionVA mov [ebx],eax add edx,2 push esi mov esi,offset szLoadLibrary mov edi,edx .REPEAT lodsb stosb .UNTIL byte ptr [esi] == 0 pop esi mov edx,edi add ebx,4 mov eax,edx sub eax,esi add eax,dwNewSectionVA mov [ebx],eax add edx,2 mov esi,offset szGetProcAddress mov edi,edx .REPEAT lodsb stosb .UNTIL byte ptr [esi] == 0 assume esi : nothing ret AssembleIT ENDP
当Assemble函数整个执行完之后,新节, 也就是新输入表的内存视图如下
前面的四十个字节分别是一个kernel.dll的IID和一个空的IID Name1 [128102C]存放1281048的RVA形式 [1281048]到[1281053]存放"kernel32.dll" 然后FirstThunk存放了1281055的RVA形式 之后每拷贝一个函数名之前都会计算出 (函数名-2)位置的RVA形式,依次放在FirstThunk指向的地方(即含有两个IMAGE_THUNK_DATA结构的数组) 在这里结尾是以一个全为0的IMAGE_THUNK_DATA结构(正如我们一般所知道的一样),但是在这四个0字节之后直接就到 "LoadLibraryA" 了,这样的话我觉得也 可以啦,因为系统的PEloader是根据IMAGE_THUNK_DATA找到对应的函数字符串的,所以其Hint和结尾的IMAGE_THUNK_DATA应该不会影响程序加载 下面是ProcessTlsTable函数,相对简单
ProcessTlsTable PROC USES edi ebx esi ecx, pFileMem : LPVOID, CryptSectionVA : DWORD LOCAL pTlsDirAddr : LPVOID ;其实我也不懂Tls,没看这部分内容,不过这里也不是关于Tls表的结构的 ;检查是否有tls表 mov edi,pFileMem add edi,[edi+03Ch] ; edi -> pointer to PE header assume edi : ptr IMAGE_NT_HEADERS lea ebx,[edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY * 9].VirtualAddress mov pTlsDirAddr,ebx mov ebx,[ebx] assume edi : nothing cmp ebx,0 ;tls's RVA=0,无tls表 jz ExitTlsFixProc ; get a RAW pointer to the tls table push ebx push pFileMem call RVA2Offset cmp eax,0 jz ExitTlsFixProc mov esi,pFileMem add esi,eax ; esi -> Tls表的文件偏移 ; copy the whole TLS table into the loader data part mov edi,offset TlsBackup mov ecx,sizeof IMAGE_TLS_DIRECTORY32 rep movsb ;把esi指向的tls表移到TlsBackup ;这里的TlsBackup是壳引导代码里的变量 ; fix the TLS DIRECTORY VA mov eax,CryptSectionVA add eax,IT_SIZE add eax,TLS_BACKUP_ADDR ;eax=TLS表的位置 ;TLS_BACKUP_ADDR equ (offset TlsBackupLabel - offset DepackerCode) ;TlsBackupLabel: ;TlsBackup IMAGE_TLS_DIRECTORY32 <0> mov esi,pTlsDirAddr mov [esi],eax ;将节表的TLS指针修正为在壳段的Tls表(壳段的Tls表并没有加密) ExitTlsFixProc: ret ProcessTlsTable ENDP
因为有一些数据结构在MakePER里出现得较多,所以我整个PER.ASM文件一起贴在这里了,重点看MakePER函数吧
; -> Polymorphic En-/Decryption routine generator for per byte encryption <- ; by yoda ;---- STRUCTs ---- sPERTable STRUCT dwSize DD ? dwEncrypt DD ? dwDecrypt DD ? RandNumType DD ? sPERTable ENDS ; RandNumType: ; 0 - no random num needed ; 1 - 3th byte must be a random number ; 2 - 2nd byte must be a random number ;----- EQUs ----- ;PERTable项数 PERItems EQU 14 ;----- CONST ---- .CONST ;机器码是逆序摆放的 PERTable DD 1 DD 090h ; NOP DD 090h ; NOP DD 0 DD 1 DD 0F9h ; STC DD 0F9h ; STC DD 0 DD 1 DD 0F8h ; CLC DD 0F8h ; CLC DD 0 DD 2 DD 0C0FEh ; INC AL DD 0C8FEh ; DEC AL DD 0 DD 2 DD 00004 ; ADD AL, 0 DD 0002Ch ; SUB AL, 0 DD 2 DD 2 DD 0002Ch ; SUB AL, 0 DD 00004 ; ADD AL, 0 DD 2 DD 2 DD 0C102h ; ADD AL, CL DD 0C12Ah ; SUB AL, CL DD 0 DD 2 DD 0C12Ah ; SUB AL, CL DD 0C102h ; ADD AL, CL DD 0 DD 2 DD 00034h ; XOR AL, 0 DD 00034h ; XOR AL, 0 DD 2 DD 3 DD 000C8C0h ; ROR AL, 0 DD 000C0C0h ; ROL AL, 0 DD 1 DD 3 DD 000C0C0h ; ROL AL, 0 DD 000C8C0h ; ROR AL, 0 DD 1 DD 3 DD 0E801EBh ; Self modifing DD 0E801EBh ; Self modifing DD 0 DD 3 DD 0E901EBh ; Self modifing DD 0E901EBh ; Self modifing DD 0 DD 3 DD 0C201EBh ; Self modifing DD 0C201EBh ; Self modifing DD 0 .DATA dwRandVal DD 0 .CODE ; srand should only called one time !!! InitRandom PROC ; manage the random generator CALL GetTickCount PUSH EAX CALL srand RET InitRandom ENDP MakePER PROC pEncryptBuff : LPVOID, pDecryptBuff : LPVOID, dwSize : DWORD ;当前随机数 LOCAL dwCurRandNum : DWORD ;准备一些东西 MOV EDI, pEncryptBuff ; EDI -> EncryptBuffer MOV ESI, pDecryptBuff ; ESI -> DecryptBuffer ADD ESI, dwSize ; ESI will be filled from down to top ; generate ! .REPEAT ;随机抽取一个随即项(一共有14个,每项是两条OPCODE) PUSH PERItems CALL rand MOV EBX, SIZEOF sPERTable XOR EDX, EDX MUL EBX ADD EAX, OFFSET PERTable XCHG EAX, EDX ; EDX -> 指向一个项A的起始地址 ASSUME EDX : PTR sPERTable ;壳里面提供了一段内存,项A的OPCODE大小不能大于这段内存 MOV EBX, [EDX].dwSize CMP EBX, dwSize JG Retry ;将项A的OPCODE代码拷贝到EDI指向的内存区域去(即pEncryptBuff处) ;每次向pEncryptBuff放一个OPCODE,直到dwSize用完为止 MOV ECX, [EDX].dwSize MOV EAX, [EDX].dwEncrypt .WHILE ECX != 0 MOV BYTE PTR [EDI], AL ADD EDI, 1 ROR EAX, 8 DEC ECX .ENDW ;产生随机数 MOV EAX, [EDX].RandNumType .IF EAX == 1 || EAX == 2 MOV EBX, EDI SUB EBX, 1 ;EBX指向pEncryptBuff的最后一个字符 PUSH 0F8h CALL rand INC EAX ; avoid 0 ! ;产生一个小于F8 大于0的随机数 MOV dwCurRandNum, EAX ;将这个随机数放在pEncryptBuff的最后一个字符上(最后一个字符通常是0) MOV BYTE PTR [EBX], AL .ENDIF ;刚才放了一个OPCODE(最后一个字符经过/不经过随机),占了一点空间,相减一下 MOV EAX, [EDX].dwSize SUB dwSize, EAX ;根据ecx-1次数执行ror eax,8 MOV ECX, [EDX].dwSize MOV EAX, [EDX].dwDecrypt SUB ECX, 1 .WHILE ECX != 0 ROR EAX, 8 DEC ECX .ENDW ;从后到前将刚才的随机数用下面的算法填充到pDecryptBuff MOV ECX, [EDX].dwSize .WHILE ECX != 0 SUB ESI, 1 MOV BYTE PTR [ESI], AL ROL EAX, 8 DEC ECX .ENDW ;根据RandNumType分别在esi+1和esi+2的地方放入当前的随机数dwCurRandNum MOV EAX, [EDX].RandNumType .IF EAX == 1 MOV EBX, ESI ADD EBX, 2 MOV EAX, dwCurRandNum MOV BYTE PTR [EBX], AL .ELSEIF EAX == 2 MOV EBX, ESI ADD EBX, 1 MOV EAX, dwCurRandNum MOV BYTE PTR [EBX], AL .ENDIF ASSUME EDX : NOTHING Retry: .UNTIL dwSize == 0 RET MakePER ENDP rand PROC USES edx ebx, dwRange : DWORD MOV EAX, dwRandVal ; save new random number ADD EAX, 0567h ROL EAX, 1 MOV dwRandVal, EAX ; get new random number XOR EDX, EDX MOV ECX, 32 BitLoop: SHR EAX, 1 .IF CARRY? XOR EAX, 013245769h .ENDIF LOOP BitLoop ; force dwRange XOR EDX, EDX MOV EBX, dwRange DIV EBX XCHG EAX, EDX RET rand ENDP srand PROC dwRandNum : DWORD MOV EAX, dwRandNum MOV dwRandVal, EAX RET srand ENDP
用MakePER生成了加密/解密函数之后,下面用CryptPE将各个节加密
;从CryptPE标号看起!! ; esi = CryptStart ; ecx = CryptSize EncryptSec: mov edi,esi ;edi是当前节在mapping中的位置 SecEncryptLoop: LODSB ;节数据放进al SecEncryptBuff DB SEC_PER_SIZE DUP (0) ;加密al STOSB ;放回去节里 LOOP SecEncryptLoop RET 省略.... DepackerCode: 省略.... CryptPE: mov edi,eax add edi,[edi+3Ch] assume edi : ptr IMAGE_NT_HEADERS ; edi -> PE header mov esi,edi add esi,0F8h assume esi : ptr IMAGE_SECTION_HEADER ; esi -> Section header xor edx,edx .REPEAT ; -> 跳过一些特殊区段 .IF dword ptr [esi].Name1 == ('crsr') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('rsr.') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('oler') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('ler.') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('Cy') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('ade.') jmp @@LoopEnd .ENDIF .IF [esi].PointerToRawData == 0 || [esi].SizeOfRawData == 0 jmp @@LoopEnd .ENDIF ;-> en-/decrypt it pushad mov ecx,[esi].SizeOfRawData .IF ebx == 0 ; (ebx is a parameter) mov esi,[esi].PointerToRawData ADD ESI, EAX CALL EncryptSec ;因为现在这部分是DepackerCode的内容,而EncryptSec是加密的,所以EncryptSec不在DepackerCode标号内而在上面的地方 .ELSE mov esi,[esi].VirtualAddress add esi,eax CALL DecryptSec .ENDIF JMP SecDecryptContinue1 ; esi = CryptStart ; ecx = CryptSize ;解密函数,壳运行的时候用 DecryptSec: mov edi,esi SecDecryptLoop: LODSB SecDecryptBuff DB SEC_PER_SIZE DUP (0) STOSB LOOP SecDecryptLoop RET SecDecryptContinue1: popad @@LoopEnd: add esi,SIZEOF IMAGE_SECTION_HEADER INC EDX .UNTIL dx == [edi].FileHeader.NumberOfSections assume esi : nothing assume edi : nothing ret
最后来一个在CryptFile最后用到的校验和计算函数:
; start calculation CheckSumLoop: mov al,byte ptr [edi] mul edx add ebx,eax inc edx inc edi loop CheckSumLoop xchg eax,ebx ; eax -> checksum RET ;-> End of GetChecksum
至此只剩下DepackerCode未分析了,就快完成(其实上面的代码也在DepackerCode标号里)
DepackerCode: pushad ; get base ebp call CallMe CallMe: pop ebp sub ebp,offset CallMe
如果这段代码是独立编译的话,看起来好像很奇怪,因为offset CallMe本来就等于ebp的值了啊,相减的话不等于0吗,等于0有什么用?(解释一下pop ebp获取到call下一行的代码)
但是这段程序不是独立编译的,而是放到其他软件上面运行,所以offset CallMe是本来的地址,而ebp中获取到的是现在的地址
看看OD反汇编代码:
00440060 Z> 60 pushad 00440061 E8 00000000 call ZR.00440066 00440066 5D pop ebp 00440067 81ED F31D4000 sub ebp,ZR.00401DF3
两个相减得到壳加在软件身上产生的偏差,得到这个偏差之后,如果以后要访问其他的变量就可以这么做
lea edi,[ebp+401E3B]
(401E3B是一个编译器生成的常量的地址)
;----- DECRYPT LOADER VARIABLES ----- MOV ECX, CRYPT_LOADER_SIZE_DB LEA EDI, [EBP+OFFSET LOADER_CRYPT_START] MOV ESI, EDI VarDecryptionLoop: LODSB VarDecryptBuff DB VAR_PER_SIZE DUP (0) STOSB LOOP VarDecryptionLoop
这个在之前的代码经常见了,用来解密壳代码(壳从LOADER_CRYPT_START开始的代码)
LOADER_CRYPT_START: ;------ DETECT WinNT ------ MOV EAX, [ESP+020h] ;EAX指向KERNEL.DLL中的一个地址,其上一个地址的代码是7C817074 FF55 08 call dword ptr ss:[ebp+8] ;很明显我们的PE文件是在这个CALL里面执行的,猜测作者可能是看过windows其他系列OS程序第一条指令时esp指向的地址吧,所以拿这个来做依据很多人都不明白 INC EAX JS NoNT MOV DWORD PTR [EBP+bNT], 1 ;如果是NT的话要标明,如果不是的话后面有一些地方是不同的。发现这个作者很细腻的,面面俱到啊 NoNT: ;------ 得到壳装载代码的CRC ------ LEA EAX, [EBP+OFFSET DepackerCode] MOV ECX, LOADER_CRC_CHECK_SIZE CALL GetChecksum MOV [EBP+dwLoaderCRC], EAX ;----- SI Check 1 ----- MOV EAX, [ebp+PROTECTION_FLAGS] AND EAX, CHECK_SI_FLAG jz SkipSICheck ;如果没有选中 检查softice选项 则跳。
------------------------------------------------------------------
花了一天时间学习了一下SEH,继续学习yoda壳!
; install SEH frame LEA ESI,[EBP+SEH] ASSUME ESI : PTR sSEH LEA EAX, [EBP+OFFSET SICheck1_SP] mov [ESI].SaveEip, EAX ASSUME ESI : NOTHING ;将SICheck1_SP地址存在SEH结构的SaveEip中 MOV EDI, EBP LEA EAX, [EBP+OFFSET SehHandler1] ;EAX=异常处理程序的地址 XOR EBX, EBX push EAX push FS:[EBX] mov FS:[EBX], ESP ;似乎意图要把安装SEH的代码伪装一下 呵呵 ; 0 - SI not found ; 1 - SI found mov ebp, 04243484Bh mov ax, 04h JMP SM1 DB 0FFh SM1: INT 3 SICheck1_SP: MOV EBP, EDI ; uninstall SEH frame XOR EBX, EBX POP FS:[EBX] ADD ESP, 4 .IF AL != 4 ;AL不等于4则有SoftIce,猜想下 ; exit JMP SM2 DB 0E9h SM2: popad ret .ENDIF SkipSICheck: ;----- GET BASE API ADDRESSES ----- ; find the ImageImportDescriptor and grab dll addresses mov eax,[ebp+dwImageBase] add eax,[eax+03Ch] add eax,080h mov ecx,[eax] ; ecx contains the VirtualAddress of the IT add ecx,[ebp+dwImageBase] add ecx,16 ; ecx points to the FirstThunk address of the IID mov eax,dword ptr [ecx] add eax,[ebp+dwImageBase] mov ebx,dword ptr [eax] mov [ebp+_LoadLibrary],ebx add eax,4 mov ebx,dword ptr [eax] mov [ebp+_GetProcAddress],ebx ;----- GET ALL OTHER API ADDRESSES ----- ; get kernel base lea eax,[ebp+offset szKernel32] push eax call [ebp+_LoadLibrary] mov esi,eax ; esi -> kernel base MOV [EBP+dwKernelBase], EAX ;-> GetModuleHandle lea eax,[ebp+szGetModuleHandle] call DoGetProcAddr mov [ebp+_GetModuleHandle],eax ;-> VirtualProtect lea eax,[ebp+szVirtualProtect] call DoGetProcAddr mov [ebp+_VirtualProtect],eax ;-> GetModuleFileName lea eax,[ebp+szGetModuleFileName] call DoGetProcAddr mov [ebp+_GetModuleFileName],eax ;-> CreateFile lea eax,[ebp+szCreateFile] call DoGetProcAddr mov [ebp+_CreateFile],eax ;-> GlobalAlloc lea eax,[ebp+szGlobalAlloc] call DoGetProcAddr mov [ebp+_GlobalAlloc],eax ;-> GlobalFree lea eax,[ebp+szGlobalFree] call DoGetProcAddr mov [ebp+_GlobalFree],eax ;-> ReadFile lea eax,[ebp+szReadFile] call DoGetProcAddr mov [ebp+_ReadFile],eax ;-> GetFileSize lea eax,[ebp+szGetFileSize] call DoGetProcAddr mov [ebp+_GetFileSize],eax ;-> CloseHandle lea eax,[ebp+szCloseHandle] call DoGetProcAddr mov [ebp+_CloseHandle],eax ; FUNNY JUMP :) LEA EAX, [EBP+OFFSET LoaderContinue1] PUSH EAX RET ; it's in an own function to keep a the loader code small ; eax = address of API string ; esi = target dll base DoGetProcAddr: push eax push esi call [ebp+_GetProcAddress] ret LoaderContinue1: ;----- ANTI DUMP ----- test [ebp+PROTECTION_FLAGS],ANTI_DUMP_FLAG jz LetDumpable push fs:[30h] pop eax TEST EAX, EAX JS fuapfdw_is9x ; detected Win 9x fuapfdw_isNT: MOV EAX, [EAX+0Ch] MOV EAX, [EAX+0Ch] MOV DWORD PTR [EAX+20h], 1000h ; increase size variable JMP fuapfdw_finished fuapfdw_is9x: PUSH 0 CALL [ebp+_GetModuleHandle] TEST EDX, EDX JNS fuapfdw_finished ; Most probably incompatible!!! CMP DWORD PTR [EDX+8], -1 JNE fuapfdw_finished ; Most probably incompatible!!! MOV EDX, [EDX+4] ; get address of internaly used ; PE header MOV DWORD PTR [EDX+50h], 1000h ; increase size variable fuapfdw_finished: LetDumpable: ;---- GET HEADER WRITE ACCESS ----- mov edi,[ebp+dwImageBase] add edi,[edi+03Ch] assume edi : ptr IMAGE_NT_HEADERS ; edi -> pointer to PE header mov esi,[ebp+dwImageBase] mov ecx,[edi].OptionalHeader.SizeOfHeaders assume edi : nothing ; fix page access lea eax,[ebp+Buff] push eax push PAGE_READWRITE push ecx push [ebp+dwImageBase] call [ebp+_VirtualProtect] ;----- CALCULATE CRC ----- test [ebp+PROTECTION_FLAGS],CHECK_HEADER_CRC jz DontCheckCRC ; get the calling exe filename push MAX_PATH lea edi,[ebp+Buff] push edi ; edi -> filename push 0 call [ebp+_GetModuleFileName] ; map it... push 0 push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ push GENERIC_READ push edi call [ebp+_CreateFile] .IF eax == INVALID_HANDLE_VALUE xor eax,eax jmp SkipChecksumCalc .ENDIF mov edi,eax ; edi -> file handle push NULL push edi call [ebp+_GetFileSize] sub eax,CHECKSUM_SKIP_SIZE xchg eax,esi ; esi -> filesize push esi push GMEM_FIXED+GMEM_ZEROINIT call [ebp+_GlobalAlloc] .IF eax == NULL jmp SkipChecksumCalcAndCleanUp .ENDIF xchg eax,ebx ; ebx -> mem base push NULL lea eax,[ebp+Buff] push eax push esi push ebx push edi call [ebp+_ReadFile] ; get the checksum mov eax,ebx mov ecx,esi PUSH EBX ; [ESP] -> hMem PUSH EDI ; EDI = hFile CALL GetChecksum mov [ebp+dwCalcedCRC],eax POP EDI POP EBX ; the calculated CRC will be compared at the start of the InitIT function >:-) LEA EAX, [EBP+OFFSET AfterCRCCalcContinue] PUSH EAX RET ;-> Start of GetChecksum GetChecksum: ; eax = file image base ; ecx = filesize mov edi,eax ; edi -> data pointer xor eax,eax ; eax -> current bytes xor ebx,ebx ; ebx -> current checksum xor edx,edx ; edx -> Position (zero based) ; start calculation CheckSumLoop: mov al,byte ptr [edi] mul edx add ebx,eax inc edx inc edi loop CheckSumLoop xchg eax,ebx ; eax -> checksum RET ;-> End of GetChecksum AfterCRCCalcContinue: ; clean up PUSH EBX call [ebp+_GlobalFree] xchg esi,eax SkipChecksumCalcAndCleanUp: push eax push edi call [ebp+_CloseHandle] pop eax SkipChecksumCalc: DontCheckCRC: ;----- DECRYPTION ----- mov eax,[ebp+dwImageBase] mov ebx,1 CALL CryptPE LEA EAX, [EBP+OFFSET AfterDeCryptionContinue] PUSH EAX RET ; eax = pointer to file memory ; ebx: 0 - 加密模式 ; 1 - 解密模式 CryptPE: mov edi,eax add edi,[edi+3Ch] assume edi : ptr IMAGE_NT_HEADERS ; edi -> PE header mov esi,edi add esi,0F8h assume esi : ptr IMAGE_SECTION_HEADER ; esi -> Section header xor edx,edx .REPEAT ; -> 跳过一些特殊区段 .IF dword ptr [esi].Name1 == ('crsr') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('rsr.') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('oler') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('ler.') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('Cy') jmp @@LoopEnd .ENDIF .IF dword ptr [esi].Name1 == ('ade.') jmp @@LoopEnd .ENDIF .IF [esi].PointerToRawData == 0 || [esi].SizeOfRawData == 0 jmp @@LoopEnd .ENDIF ;-> en-/decrypt it pushad mov ecx,[esi].SizeOfRawData .IF ebx == 0 ; (ebx is a parameter) mov esi,[esi].PointerToRawData ADD ESI, EAX CALL EncryptSec ;因为现在这部分是DepackerCode的内容,而EncryptSec是加密的,所以EncryptSec不在DepackerCode标号内而在上面的地方 .ELSE mov esi,[esi].VirtualAddress add esi,eax CALL DecryptSec .ENDIF JMP SecDecryptContinue1 ; esi = CryptStart ; ecx = CryptSize ;解密函数,壳运行的时候用 DecryptSec: mov edi,esi SecDecryptLoop: LODSB SecDecryptBuff DB SEC_PER_SIZE DUP (0) STOSB LOOP SecDecryptLoop RET SecDecryptContinue1: popad @@LoopEnd: add esi,SIZEOF IMAGE_SECTION_HEADER INC EDX .UNTIL dx == [edi].FileHeader.NumberOfSections assume esi : nothing assume edi : nothing ret AfterDeCryptionContinue: ;下面将入口点地址循环移动7位,放到变量ESP10处,然后将标号SehHandler_OEP_Jump地址放在ESP1C处,还不知道这个是干什么的。。后面应该有用吧 ;------ PREPARE THE OEP JUMP EXCEPTION :) ------ MOV EBX, [EBP+dwImageBase] ADD EBX, [EBP+dwOrgEntryPoint] ROR EBX, 7 MOV [ESP+010h], EBX LEA EBX, [EBP+OFFSET SehHandler_OEP_Jump] MOV [ESP+01Ch], EBX
mov edi,[ebp+dwImageBase] add edi,dword ptr [edi+03Ch] ; edi -> pointer to PE header assume edi : ptr IMAGE_NT_HEADERS mov ebx,[edi].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY * 9].VirtualAddress assume edi : nothing cmp ebx,0 ; no tls section jz SkipTlsFix add ebx,[ebp+dwImageBase] ; ebx -> pointer to tls table assume ebx : ptr IMAGE_TLS_DIRECTORY32 mov eax,[ebx].AddressOfIndex mov dword ptr [eax],0 assume ebx : nothing ;tls表索引清0
mov eax,[ebp+dwCalcedCRC] .IF eax != 0 .IF eax != [ebp+dwOrgChecksum] jmp SkipInitIt .ENDIF .ENDIF
比较校验和
;----- INIT IMPORT TABLE ----- ; 0 - an error occurred ; 1 - IT initialized successfully LEA ESI, [EBP+OFFSET IIDInfo] ; ESI -> pointer to the current IID ASSUME ESI : PTR sItInfo ;------ PREPARE API REDIRECTION ------ TEST [EBP+PROTECTION_FLAGS], API_REDIRECT_FLAG .IF !ZERO? PUSH ESI LEA EDI, [EBP+OFFSET Buff] ASSUME EDI : PTR sReThunkInfo XOR ECX, ECX .WHILE [ESI].FirstThunk MOV EDX, [ESI].FirstThunk ADD EDX, [EBP+dwImageBase] .WHILE DWORD PTR [EDX] INC ECX ADD EDX, 4 .ENDW ADD ESI, SIZEOF sItInfo .ENDW ;计算有多少个API吧,然后根据这个数分配空间lauren存放sApiStub结构 XOR EDX, EDX MOV EAX, SIZEOF sApiStub MUL ECX PUSH EAX PUSH GMEM_FIXED CALL [EBP+_GlobalAlloc] .IF !EAX ; fatal exit ADD ESP, 4 POPAD RET .ENDIF MOV [EDI].ApiStubMemAddr, EAX MOV [EDI].pNextStub, EAX ;具体用途看继续看。。 ASSUME EDI : NOTHING POP ESI .ENDIF ; 解密dll名,完了跳到InitITContinue1 .WHILE [esi].FirstThunk != 0 ; load the library mov ebx,[esi].DllNameRVA add ebx,[ebp+dwImageBase] ; decrypt dll string MOV EAX,EBX CALL EnDeCryptString LEA EAX, [EBP+InitITContinue1] ; goto InitITContinue1 PUSH EAX RET ; eax = VA of target string EnDeCryptString: PUSH ESI PUSH EDI MOV ESI,EAX MOV EDI,EAX DllCryptLoop: LODSB ROR AL,4 STOSB CMP BYTE PTR [EDI],0 JNZ DllCryptLoop POP EDI POP ESI RET InitITContinue1: push ebx call [ebp+_LoadLibrary] test eax,eax jz SkipInitIt ;解密之后加载这个dll,加载不了就跳出现在这个循环 ; zero dll name PUSH EAX ; save dll base test [ebp+PROTECTION_FLAGS],DESTROY_IMPORT_FLAG jz DontKillDllName ; push return address LEA EAX, [EBP+OFFSET DontKillDllName] PUSH EAX ; push return address :) MOV EAX, EBX JMP KillString ;把dll名填0 DontKillDllName: POP EBX ; EBX -> dll句柄 ; process the (Original-)FirstThunk members mov ecx,[esi].OrgFirstThunk .IF ecx == 0 mov ecx,[esi].FirstThunk .ENDIF ;OrgFirstThunk没有就用FirstThunk add ecx,[ebp+dwImageBase] ; ecx -> pointer to current thunk(这个可能是OrgFirstThunk也可能是FirstThunk) mov edx,[esi].FirstThunk add edx,[ebp+dwImageBase] ; edx -> pointer to current thunk (这个总是FirstThunk) .WHILE dword ptr [ecx] != 0 test dword ptr [ecx],IMAGE_ORDINAL_FLAG32 ; 以序号导入吗 jnz @@OrdinalImp ; process a name import mov dword ptr eax,[ecx] add eax,2 ;跳过序号 add eax,[ebp+dwImageBase] ; eax points now to the Name of the Import PUSH EAX CALL EnDeCryptString ;解密API名 POP EAX mov edi,eax ; save the API name pointer for destroying it later push edx push ecx ; save the Thunk pointers push eax push ebx call [ebp+_GetProcAddress] .IF eax == NULL pop ecx pop edx jmp SkipInitIt .ENDIF pop ecx pop edx ;->kill API name PUSHAD test [ebp+PROTECTION_FLAGS],DESTROY_IMPORT_FLAG JZ DontKillApiName LEA EAX, [EBP+OFFSET DontKillApiName] ; push return address PUSH EAX MOV EAX, EDI JMP KillString DontKillApiName: POPAD ;-> paste API address mov dword ptr [edx],eax jmp @@NextThunkPlease @@OrdinalImp: ; process an ordinal import push edx push ecx ; save the thunk pointers mov dword ptr eax,[ecx] sub eax,080000000h ;把序号删掉,只剩下字符串的RVA push eax push ebx call [ebp+_GetProcAddress] test eax,eax jz SkipInitIt pop ecx pop edx mov dword ptr [edx],eax ;写入IAT @@NextThunkPlease: ; eax = Current Api address ; ebx = dll base ; edx = non-org thunk pointer TEST [EBP+PROTECTION_FLAGS], API_REDIRECT_FLAG .IF !ZERO? .IF [EBP+bNT] .IF EBX < 070000000h || EBX > 077FFFFFFh JMP SkipThunkRed .ENDIF .ELSE .IF EBX < 080000000h JMP SkipThunkRed .ENDIF .ENDIF ;非系统dll则跳过(根据地址空间判断) PUSH EDI PUSH ESI LEA EDI, [EBP+Buff] ASSUME EDI : PTR sReThunkInfo MOV ESI, [EDI].pNextStub MOV [EDX], ESI ; make the thunk point to stub mem SUB EAX, ESI SUB EAX, 5 ; sizeof E9XXXXXXXX - Jump long ;计算出这个函数地址的偏移(JMP跟的是偏移而不是绝对地址) MOV BYTE PTR [ESI], 0E9h MOV DWORD PTR [ESI+1], EAX ADD [EDI].pNextStub, SIZEOF sApiStub ASSUME EDI : NOTHING POP ESI POP EDI SkipThunkRed: .ENDIF add ecx,4 add edx,4 .ENDW add esi,SIZEOF sItInfo ; make esi point to the next IID .ENDW assume esi:nothing xor eax,eax inc eax SkipInitIt: .IF eax != TRUE ; exit popad ret .ENDIF ;----- 擦除PE头,应该是为了不让调试软件读取里面的数据吧 ------ test [ebp+PROTECTION_FLAGS],ERASE_HEADER_FLAG jz SkipEraseHeader ; zero the header mov edi,[ebp+dwImageBase] add edi,[edi+03Ch] assume edi : ptr IMAGE_NT_HEADERS ; edi -> pointer to PE header mov ecx,[edi].OptionalHeader.SizeOfHeaders mov esi,[ebp+dwImageBase] assume edi : nothing ZeroMemLoop: mov byte ptr [esi],0 inc esi loop ZeroMemLoop SkipEraseHeader: ;------ 这次是Loader的CheckSum。。这个壳搞了两次自校验 ------ LEA EAX, [EBP+DepackerCode] MOV ECX, LOADER_CRC_CHECK_SIZE JMP SM10 DB 0E9h SM10: CALL GetChecksum JMP SM11 DB 0C7h SM11: MOV EBX, [EBP+dwLoaderCRC] XOR EAX, EBX .IF !ZERO? JMP SM12 DB 02Ch SM12: POPAD JMP SM13 DB 0E8h SM13: RET .ENDIF ;----- 解密OEP跳转的代码 ----- LEA EDI, [EBP+OFFSET OEP_JUMP_CODE_START] MOV ESI, EDI MOV ECX, CRYPT_OEP_JUMP_SIZE XOR EBX, EBX OepJumpDecryptLoop: LODSB XOR AL, OEP_JUMP_ENCRYPT_NUM SUB AL, BL ROL AL, 2 STOSB INC EBX LOOP OepJumpDecryptLoop ;----- JUMP TO OEP ----- OEP_JUMP_CODE_START: ;----- 检测下是否在被调试 ----- LEA EAX, [EBP+OFFSET szIsDebuggerPresent] PUSH EAX PUSH [EBP+dwKernelBase] CALL [EBP+_GetProcAddress] OR EAX, EAX ; API not present on W95 .IF !ZERO? CALL EAX OR EAX, EAX .IF !ZERO? POPAD RET .ENDIF .ENDIF ;------ SECOND SI CHECK ------ ; 这个检测方法不支持WINNT TEST [EBP+PROTECTION_FLAGS], CHECK_SI_FLAG JZ SkipSICheck2 LEA ESI,[EBP+SEH] ASSUME ESI : PTR sSEH LEA EAX, [EBP+OFFSET SICheck2_SP] MOV [ESI].SaveEip, EAX ASSUME ESI : NOTHING XOR EBX, EBX LEA EAX, [EBP+OFFSET SehHandler2] PUSH EAX PUSH FS:[EBX] MOV FS:[EBX], ESP MOV EDI, EBP MOV EAX, 4400h JMP SM4 DB 0C7h SM4: INT 68h SICheck2_SP: XOR EBX, EBX POP FS:[EBX] ADD ESP, 4 .IF DI == 01297h || DI == 01277h || DI == 01330h JMP SM5 DB 0FFh SM5: POPAD JMP SM6 DB 0E8h SM6: RET .ENDIF SkipSICheck2: LEA EAX, [EBP+OFFSET OepJumpCodeCont] PUSH EAX RET ; ---- 在这个异常里面跳到OEP----- SehHandler_OEP_Jump PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD PUSH EDI MOV EAX,pContext ASSUME EAX : PTR CONTEXT ; restore original seh handler MOV EDI, [EAX].regEsp PUSH [EDI] XOR EDI, EDI POP FS:[EDI] ; kill seh frame ADD [EAX].regEsp, 8 ; set EIP to the OEP MOV EDI, [EAX].regEbx ; EDI -> OEP ROL EDI, 7 ;把存放在ebx的右移过的ebx还原 MOV [EAX].regEip, EDI mov EAX,ExceptionContinueExecution ;跳到OEP ASSUME EAX : NOTHING POP EDI RET SehHandler_OEP_Jump ENDP OepJumpCodeCont: ;---- 把壳引导代码和其数据清除(设为0) ---- XOR AL,AL LEA EDI, [EBP+OFFSET DepackerCode] MOV ECX, (OFFSET SehHandler_OEP_Jump - OFFSET DepackerCode) LoaderZeroLoop: STOSB LOOP LoaderZeroLoop ;OEP_JUMP_CODE_END之后的数据也清除掉 LEA EDI, [EBP+OFFSET OEP_JUMP_CODE_END] MOV ECX, (OFFSET LOADER_CRYPT_END - OFFSET OEP_JUMP_CODE_END) LoaderVarZeroLoop: STOSB LOOP LoaderVarZeroLoop POPAD ;还记得PREPARE THE OEP JUMP EXCEPTION? 那时候保存了OEP移位之后的地址 和 一个异常处理过程 ;猜想应该那两个变量的位置在这个时刻popad就会刚好放到ebx和eax里面了吧 ; POPAD之后: ; EAX - OEP Seh handler ; EBX - OEP (移位过的) ;------ install OEP JUMP SEH frame ------ PUSH EAX XOR EAX, EAX PUSH FS:[EAX] MOV FS:[EAX], ESP JMP SM3 DB 087H SM3: ;这里后面全部被填充00了,会引发异常 OEP_JUMP_CODE_END: ; EAX = ASCII string address KillString: .WHILE byte ptr [eax] != 0 mov byte ptr [eax],0 inc eax .ENDW ret SehHandler1 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD PUSH EDI MOV EAX,pContext ASSUME EAX : PTR CONTEXT MOV EDI, [EAX].regEdi push [EDI+SEH.SaveEip] pop [eax].regEip MOV [eax].regEbp, EDI MOV [EAX].regEax, 4 ; SI NOT detected ! mov EAX,ExceptionContinueExecution ASSUME EAX : NOTHING POP EDI RET SehHandler1 ENDP SehHandler2 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD PUSH EDI MOV EAX,pContext ASSUME EAX : PTR CONTEXT MOV EDI, [EAX].regEdi push [EDI+SEH.SaveEip] pop [eax].regEip MOV [eax].regEbp, EDI MOV [EAX].regEdi, 0 ; SI NOT detected ! mov EAX,ExceptionContinueExecution ASSUME EAX : NOTHING POP EDI RET SehHandler2 ENDP ;----- LOADER STRUCTS ----- sItInfo STRUCT DllNameRVA dd ? FirstThunk dd ? OrgFirstThunk dd ? sItInfo ENDS sSEH STRUCT OrgEsp dd ? OrgEbp dd ? SaveEip dd ? sSEH ENDS sReThunkInfo STRUCT ApiStubMemAddr DD ? pNextStub DD ? sReThunkInfo ENDS sApiStub STRUCT ; UNUSED ! JumpOpc DB ? JumpAddr DD ? sApiStub ENDS ;----- LOADER VARIABLES ----- dwImageBase dd 0 dwOrgEntryPoint dd 0 PROTECTION_FLAGS dd 0 dwCalcedCRC dd 0 dwLoaderCRC DD 0 bNT DD 0 IIDInfo db (SIZEOF sItInfo * MAX_IID_NUM) dup (0) SEH sSEH <0> _LoadLibrary dd 0 _GetProcAddress dd 0 ; some API stuff szKernel32 db "Kernel32.dll",0 dwKernelBase dd 0 szGetModuleHandle db "GetModuleHandleA",0 _GetModuleHandle dd 0 szVirtualProtect db "VirtualProtect",0 _VirtualProtect dd 0 szGetModuleFileName db "GetModuleFileNameA",0 _GetModuleFileName dd 0 szCreateFile db "CreateFileA",0 _CreateFile dd 0 szGlobalAlloc db "GlobalAlloc",0 _GlobalAlloc dd 0 szGlobalFree db "GlobalFree",0 _GlobalFree dd 0 szReadFile db "ReadFile",0 _ReadFile dd 0 szGetFileSize db "GetFileSize",0 _GetFileSize dd 0 szCloseHandle db "CloseHandle",0 _CloseHandle dd 0 szIsDebuggerPresent db "IsDebuggerPresent",0 LOADER_CRYPT_END: ; This variables won't be crypted: TlsBackupLabel: TlsBackup IMAGE_TLS_DIRECTORY32 <0> ChecksumLabel: dwOrgChecksum dd 0 Buff db 0 ; buffer for some stuff, its size: 2000h(VS) - DEPACKER_CODE_SIZE ;----- END OF PE LOADER CODE ----- DepackerCodeEnd:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
- 想去逆向培训,去前学点什么 5802
- [分享]一个Getting harder的Keygenme 6642
- [讨论]软件逆向相关的行业如何 2989
- [分享]跟了下CRC32的CrackMe 8264
- [分享]跟了一下qq-msg 14589
看原图
赞赏
雪币:
留言: