为什么会报病毒,是个加密程序
;*********************************************************;程序名称:LC Crypto
; 本文件只是主程序的源代码
; 注意加密部分的代码是在 attach.asm
; 本程序综合运用了SEH、PE、CRC32知识
;作者:罗聪
;日期:2002-11-29
;整理:2003-3-2
;出处:http://www.(老罗的缤纷天地)
;注意事项:如欲转载,请保持本程序的完整,并注明:
;转载自“老罗的缤纷天地”(http://www.)
;>> 在此特别强烈感激 俄罗斯 的 Comrade 大虾,
;>> 跟他的交流使我受益匪浅,他的源代码更使我汗颜!
;*********************************************************
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\shell32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\shell32.lib
includelib \masm32\lib\comdlg32.lib
;*********************************************************
;很有用的宏
;*********************************************************
CTEXT MACRO y:VARARG
LOCAL sym
CONST segment
ifidni <y>,<>
sym db 0
else
sym db y,0
endif
CONST ends
exitm <offset sym>
ENDM
m2m MACRO M1, M2
push M2
pop M1
ENDM
;*********************************************************
;子程序定义
;*********************************************************
WndProc proto :DWORD, :DWORD, :DWORD, :DWORD
Protect proto
ErrorHandler proto C :DWORD, :DWORD, :DWORD, :DWORD
ExceptionFilter proto :DWORD
AboutProc proto :DWORD, :DWORD, :DWORD, :DWORD
init_crc32table_m proto
arraycrc32_m proto
;*********************************************************
;常量定义
;*********************************************************
.const
IDI_LC equ 1
IDC_BUTTON_BROWSE equ 3000
IDC_EDIT_FILENAME equ 3001
IDC_EDIT_PASSWORD equ 3002
IDC_CHECKBOX_KEEPBACKUP equ 3003
IDC_BUTTON_PROCESS equ 3004
IDC_BUTTON_ABOUT equ 3005
IDC_BUTTON_EXIT equ 3006
IDC_BUTTON_HELP equ 3007
IDC_BUTTON_HOMEPAGE equ 3008
DLG_HELP equ 3009
IDC_EDIT_PASSWORD_2 equ 3010
IDM_MENU_ABOUT equ 3011
;*********************************************************
;变量定义
;*********************************************************
.data
szDlgName db "LC_DIALOG", 0
szCaption db "LC Crypto :: v0.1 by LC", 0
szMenuAbout db "&About LC Crypto...", 0
szFileFilter db "Executables (*.exe)", 0, "*.exe", 0, 0
szStatusDone db "Done!", 0
szError db "Error", 0
szErrorThread db "Error at %08Xh", 13, 10, "Registers:", 13, 10, "eax = %08Xh ebx = %08Xh ecx = %08Xh", 13, 10, "edx = %08Xh esp = %08Xh ebp = %08Xh", 13, 10, "esi = %08Xh edi = %08Xh", 13, 10, 13, 10, "Recovering...", 0
szErrorFinal db "Error at %08Xh", 13, 10, "Quitting...", 0
;*********************************************************
;插入宿主的代码
;*********************************************************
include attach.asm
;*********************************************************
;未初始化的变量定义
;*********************************************************
.data?
hFile dd ?
szMessage db 256 dup (?)
SEH dd 6 dup (?)
data_start equ $
mbp MSGBOXPARAMS <>
ofn OPENFILENAME <>
img_dos_hdr IMAGE_DOS_HEADER <>
img_nt_hdrs IMAGE_NT_HEADERS <>
img_sect_hdr IMAGE_SECTION_HEADER <>
szFileName db MAX_PATH dup (?)
szFileNameBak db MAX_PATH dup (?)
szPassword db 16 dup (?)
szPassword_2 db 16 dup (?)
data_size equ $ - data_start
bNew db attach_size dup (?)
hInstance dd ?
crc32tble dd 256 dup(?) ;CRC-32 table
;*********************************************************
;代码段开始
;*********************************************************
.code
main:
;挂接异常处理模块:
invoke SetUnhandledExceptionFilter, addr ExceptionFilter
push edi
xor eax, eax
mov ecx, data_size shr 2
mov edi, data_start
rep stosd
.if data_size and 3
mov edi, data_start
mov ecx, data_size and 3
rep stosb
.endif
pop edi
;不用说了吧:
invoke GetModuleHandle, eax
mov hInstance, eax
mov mbp.hInstance, eax
mov ofn.hInstance, eax
invoke DialogBoxParam, eax, addr szDlgName, 0, addr WndProc, eax
invoke ExitProcess, eax
;*********************************************************
;主程序消息循环
;*********************************************************
WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
.if uMsg == WM_CLOSE
invoke EndDialog, hWnd, 0
.elseif uMsg == WM_INITDIALOG
;设置我的图标:
invoke LoadIcon, hInstance, IDI_LC
invoke SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax
;默认“保留备份文件”选项是打钩的:
invoke CheckDlgButton, hWnd, IDC_CHECKBOX_KEEPBACKUP, BST_CHECKED
;储存hWnd,后面会用到:
mov eax, hWnd
mov mbp.hwndOwner, eax
mov ofn.hwndOwner, eax
;在系统菜单中加入我的“关于”对话框:
invoke GetSystemMenu, eax, 0
push offset szMenuAbout
push IDM_MENU_ABOUT
push MFT_STRING
push eax
invoke AppendMenu, eax, MFT_SEPARATOR, 0, 0
call AppendMenu
;设置每个 Edit 框的最大文本输入字数:
invoke SendDlgItemMessage, hWnd, IDC_EDIT_FILENAME, EM_SETLIMITTEXT, sizeof szFileName, 0
invoke SendDlgItemMessage, hWnd, IDC_EDIT_PASSWORD, EM_SETLIMITTEXT, sizeof szPassword, 0
invoke SendDlgItemMessage, hWnd, IDC_EDIT_PASSWORD_2, EM_SETLIMITTEXT, sizeof szPassword_2, 0
.elseif uMsg == WM_SYSCOMMAND
.if wParam == IDM_MENU_ABOUT
;打开“关于”对话框:
invoke DialogBoxParam, hInstance, DLG_HELP, hWnd, offset AboutProc, 0
.endif
.elseif uMsg == WM_COMMAND
mov eax, wParam
.if ax == IDC_BUTTON_BROWSE
;“打开文件”对话框:
mov ofn.lStructSize, sizeof ofn
mov ofn.lpstrFilter, offset szFileFilter
mov ofn.lpstrFile, offset szFileName
mov ofn.nMaxFile, sizeof szFileName
invoke GetOpenFileName, addr ofn
;如果选择了一个文件,就把它的文件名输出到“文件名”这个 Edit 中:
test eax, eax
.if !zero?
invoke SetDlgItemText, hWnd, IDC_EDIT_FILENAME, addr szFileName
.endif
.elseif ax == IDC_BUTTON_PROCESS || ax == IDOK
;进入加密模块:
invoke Protect
.elseif ax == IDC_BUTTON_ABOUT
;“关于”对话框:
invoke DialogBoxParam, hInstance, DLG_HELP, hWnd, offset AboutProc, 0
.elseif ax == IDC_BUTTON_EXIT
; Over,走人...
invoke SendMessage, hWnd, WM_CLOSE, 0, 0
.endif
.else
mov eax, FALSE
ret
.endif
xor eax, eax
ret
WndProc endp
;*********************************************************
;“关于”对话框
;*********************************************************
AboutProc proc uses edx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg == WM_CLOSE
invoke EndDialog, hWnd, 0
.elseif uMsg == WM_INITDIALOG
;设置我的图标:
invoke LoadIcon, hInstance, IDI_LC
invoke SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax
.elseif uMsg == WM_COMMAND
mov eax, wParam
mov edx, eax
shr edx, 16
movzx eax, ax
.if edx == BN_CLICKED
.if eax == IDCANCEL || eax == IDOK
invoke EndDialog, hWnd, NULL
.elseif eax == IDC_BUTTON_HOMEPAGE
;打开我的主页:
invoke ShellExecute, hWnd, CTEXT("open"), CTEXT("http://www.), NULL, NULL, SW_SHOWNORMAL
.elseif eax == IDC_BUTTON_HELP
;打开 help.chm :
invoke ShellExecute, hWnd, CTEXT("open"), CTEXT("help.chm"), NULL, NULL, SW_SHOWNORMAL
.endif
.endif
.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
AboutProc endp
;*********************************************************
;写入加密节的模块
;*********************************************************
Protect proc uses ebx ecx edx esi edi
LOCAL bOK: BOOL
mov bOK, TRUE
;挂接 SEH 处理链,不用多说了吧,呵呵:
assume fs:nothing
push offset ErrorHandler
push fs:[0]
mov [SEH], esp
mov [SEH+4], ebp
mov [SEH+8], ebx
mov [SEH+12], esi
mov [SEH+16], edi
mov [SEH+20], offset @@safe
mov fs:[0], esp
;获得文件名和密码:
invoke GetDlgItemText, [mbp.hwndOwner], IDC_EDIT_FILENAME, addr szFileName, sizeof szFileName
invoke GetDlgItemText, [mbp.hwndOwner], IDC_EDIT_PASSWORD, addr szPassword, sizeof szPassword
invoke GetDlgItemText, [mbp.hwndOwner], IDC_EDIT_PASSWORD_2, addr szPassword_2, sizeof szPassword_2
;打开文件:
invoke CreateFile, addr szFileName, GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
;如果打开文件失败,进行处理:
.if eax == INVALID_HANDLE_VALUE
invoke MessageBox, mbp.hwndOwner, CTEXT("打开文件失败!"), addr szCaption, MB_OK or MB_ICONHAND
invoke GetDlgItem, mbp.hwndOwner, IDC_EDIT_FILENAME
invoke SetFocus, eax
invoke SendMessage, eax, EM_SETSEL, 0, -1
mov bOK, FALSE
jmp _Err_CreateFile_Exit
.endif
;复制文件句柄:
mov hFile, eax
;取得密码的长度:
invoke lstrlen, addr szPassword
;如果密码长度为0,进行处理:
.if eax == 0
invoke MessageBox, mbp.hwndOwner, CTEXT("请先输入密码!"), addr szCaption, MB_OK or MB_ICONHAND
invoke GetDlgItem, mbp.hwndOwner, IDC_EDIT_PASSWORD
invoke SetFocus, eax
mov bOK, FALSE
jmp @@safe
.endif
;比较两次输入的密码是否相同,如不同,进行处理:
invoke lstrcmp, addr szPassword, addr szPassword_2
.if eax != 0
invoke MessageBox, mbp.hwndOwner, CTEXT("两次输入的密码不符,请检查您的输入!"), addr szCaption, MB_OK or MB_ICONHAND
mov bOK, FALSE
jmp @@safe
.endif
;判断“保留备份文件”是否被选中,如果选中了,则备份原文件,备份文件名是在原文件名的最后加“.bak”:
invoke IsDlgButtonChecked, mbp.hwndOwner, IDC_CHECKBOX_KEEPBACKUP
.if eax == BST_CHECKED
invoke lstrcpy, addr szFileNameBak, addr szFileName
invoke lstrcat, addr szFileNameBak, CTEXT(".bak")
invoke CopyFile, addr szFileName, addr szFileNameBak, FALSE
.endif
;读入 PE 文件的 IMAGE_DOS_HEADER :
invoke ReadFile, hFile, addr img_dos_hdr, sizeof img_dos_hdr, esp, 0
;判断是否为一个有效的 PE 文件,呵呵,这个不用多解释了吧?
cmp [img_dos_hdr.e_magic], "ZM"
.if !zero?
invoke MessageBox, [mbp.hwndOwner], CTEXT("这不是一个有效的 PE 文件!"), addr szCaption, MB_OK or MB_ICONHAND
mov bOK, FALSE
jmp @@safe
.endif
;把文件指针指向 IMAGE_NT_HEADERS,并读入该部分内容:
invoke SetFilePointer, hFile, img_dos_hdr.e_lfanew, 0, FILE_BEGIN
invoke ReadFile, hFile, addr img_nt_hdrs, sizeof img_nt_hdrs, esp, 0
;同样,判断是否为一个有效的 PE 文件,呵呵,这个不用多解释了吧?
cmp [img_nt_hdrs.Signature], "EP"
.if !zero?
invoke MessageBox, [mbp.hwndOwner], CTEXT("这不是一个有效的 PE 文件!"), addr szCaption, MB_OK or MB_ICONHAND
mov bOK, FALSE
jmp @@safe
.endif
;储存一些数据,后面会用到:
xor eax, eax
xor edx, edx
movzx ecx, [img_nt_hdrs.FileHeader.NumberOfSections]
@@s000:
push ecx
push eax
push edx
;读入 IMAGE_SECTION_HEADER 内容:
invoke ReadFile, hFile, addr img_sect_hdr, sizeof img_sect_hdr, esp, 0
;判断是否已被加密过:
invoke lstrcmp, CTEXT(".LC"), addr [img_sect_hdr.Name1]
.if eax == 0
invoke MessageBox, [mbp.hwndOwner], CTEXT("您忘记了?文件已被加密过啦……"), addr szCaption, MB_OK or MB_ICONHAND
mov bOK, FALSE
jmp @@safe
.endif
;查找可以插入新节的位置:
pop edx
mov eax, img_sect_hdr.PointerToRawData
add eax, img_sect_hdr.SizeOfRawData
cmp eax, edx
jbe @@s001
mov edx, eax
@@s001:
pop eax
mov ecx, [img_sect_hdr.VirtualAddress]
add ecx, [img_sect_hdr.Misc.VirtualSize]
cmp eax, ecx
jae @@s002
mov eax, ecx
@@s002:
pop ecx
loopd @@s000
;新节的名称为“.LC”,呵呵:
mov dword ptr [img_sect_hdr.Name1+00h], "CL."
;填充剩下的字段:
mov [img_sect_hdr.Misc.VirtualSize], attach_size
mov [img_sect_hdr.VirtualAddress], eax
mov [img_sect_hdr.SizeOfRawData], attach_size
mov [img_sect_hdr.PointerToRawData], edx
mov [img_sect_hdr.PointerToRelocations], ecx
mov [img_sect_hdr.PointerToLinenumbers], ecx
mov dword ptr [img_sect_hdr.NumberOfRelocations], ecx
;计算新节的加载RVA:
mov edx, 00000FFFh
test [img_sect_hdr.VirtualAddress], edx
.if !zero?
and edx, [img_sect_hdr.VirtualAddress]
sub edx, 1000h
neg edx
add [img_sect_hdr.VirtualAddress], edx
.endif
;计算新节的 PointerToRawData 的偏移值:
;(经过试验,200h是在 Win2k/XP 中能被正确加载的比较保险的值)
xor edx, edx
mov eax, [img_sect_hdr.PointerToRawData]
mov ecx, 200h
div ecx
test edx, edx
.if !zero?
sub edx, 200h
neg edx
add [img_sect_hdr.PointerToRawData], edx
.endif
;设置新节的属性:
;code/data/execute/read/write/inited data/un-inited data:(一股脑都加上了,呵呵,这样可以避免出错)
mov [img_sect_hdr.Characteristics], 0E00000E0h
;把新节信息(IMAGE_SECTION_HEADER)写入文件:
invoke WriteFile, [hFile], addr img_sect_hdr, sizeof img_sect_hdr, esp, 0
;定位到新节的起始偏移地址:
invoke SetFilePointer, [hFile], [img_sect_hdr.PointerToRawData], 0, FILE_BEGIN
;把一些有用的信息压栈,后面会用到:
push esi
push edi
mov esi, attach_start
mov edi, offset bNew
mov ecx, attach_size shr 2
rep movsd
mov ecx, attach_size and 3
rep movsb
mov esi, offset bNew
mov ecx, attach_size
;改写原程序的代码段的第一条指令的入口地址:
@@copy:
mov eax, [esi]
and eax, NOT 00000FFFh
cmp eax, attach_data_start
.if zero?
mov eax, [esi]
sub eax, attach_start
add eax, [img_nt_hdrs.OptionalHeader.ImageBase]
add eax, [img_sect_hdr.VirtualAddress]
mov [esi], eax
.endif
inc esi
loopd @@copy
mov eax, [img_nt_hdrs.OptionalHeader.AddressOfEntryPoint]
add eax, [img_nt_hdrs.OptionalHeader.ImageBase]
mov dword ptr [bNew + @@oep], eax
;初始化crc32table:
invoke init_crc32table_m
;下面赋值给寄存器ebx,以便进行crc32转换:
;EBX是待转换的字符串的首地址:
lea ebx, szPassword
;进行crc32转换:
invoke arraycrc32_m
;转换后的密码存放在 [esi] 中:
mov dword ptr [esi], eax
;把转换后的密码加入到新节里面:
mov edi, offset bNew + (offset _szRealPassword - attach_start)
mov ecx, 4
rep movsd
pop edi
pop esi
;真正把整个新节的内容写入原程序:
invoke WriteFile, [hFile], addr bNew, attach_size, esp, 0
invoke SetFilePointer, [hFile], [img_dos_hdr.e_lfanew], 0, FILE_BEGIN
mov eax, [img_sect_hdr.VirtualAddress]
add eax, attach_code_start - attach_start
inc [img_nt_hdrs.FileHeader.NumberOfSections]
mov [img_nt_hdrs.OptionalHeader.AddressOfEntryPoint], eax
add [img_nt_hdrs.OptionalHeader.SizeOfImage], attach_size
;下面两句非常重要,否则加密后的程序在 Win2k 以上的系统中运行会有错误:(感谢vBin兄的指点!)
push 0
pop [img_nt_hdrs.OptionalHeader.DataDirectory(88).VirtualAddress]
invoke WriteFile, [hFile], addr img_nt_hdrs, sizeof img_nt_hdrs, esp, 0
;安全的文件关闭点:
@@safe:
invoke CloseHandle, [hFile]
;如果一开始的打开文件出错,就会来到这里:
_Err_CreateFile_Exit:
;如果操作全部完成并没有错误发生,就显示成功提示:
.if bOK == TRUE
invoke MessageBox, [mbp.hwndOwner], CTEXT("加密成功完成!请记住您的密码!"), addr szCaption, MB_OK or MB_ICONINFORMATION
.endif
;取消 SEH 链表:
pop fs:[0]
add esp, 4
;结束啦!哈
ret
Protect endp
;*********************************************************
; SEH 处理模块
;*********************************************************
ErrorHandler proc C lpExcept:DWORD, lpFrame:DWORD, lpContext:DWORD, lpDispatch:DWORD
;这个模块很简单,不多说了,就是在捕捉到 SEH 异常的时候,把当前的出错地址和寄存器信息显示出来:
mov eax, [lpExcept]
mov ecx, [lpContext]
invoke wsprintf, addr szMessage, addr szErrorThread, [eax][EXCEPTION_RECORD.ExceptionAddress], [ecx][CONTEXT.regEax], [ecx][CONTEXT.regEbx], [ecx][CONTEXT.regEcx], [ecx][CONTEXT.regEdx], [ecx][CONTEXT.regEsp], [ecx][CONTEXT.regEbp], [ecx][CONTEXT.regEsi], [ecx][CONTEXT.regEdi]
invoke MessageBox, [mbp.hwndOwner], addr szMessage, addr szError, MB_OK OR MB_ICONHAND OR MB_APPLMODAL
;重要的 SEH 链表内容,自己找一些 SEH 资料来看吧,不多说了:
mov eax, [lpContext]
m2m [eax][CONTEXT.regEsp], [SEH]
m2m [eax][CONTEXT.regEbp], [SEH+4]
m2m [eax][CONTEXT.regEbx], [SEH+8]
m2m [eax][CONTEXT.regEsi], [SEH+12]
m2m [eax][CONTEXT.regEdi], [SEH+16]
m2m [eax][CONTEXT.regEip], [SEH+20]
; Continue Execution:
xor eax, eax
ret
ErrorHandler endp
;*********************************************************
;异常处理模块
;*********************************************************
ExceptionFilter proc lpExcept:DWORD
mov eax, [lpExcept]
invoke wsprintf, addr szMessage, addr szErrorFinal, [eax][EXCEPTION_RECORD.ExceptionAddress]
invoke MessageBox, [mbp.hwndOwner], addr szMessage, addr szError, MB_OK OR MB_ICONHAND OR MB_APPLMODAL
invoke EndDialog, [mbp.hwndOwner], eax
invoke ExitProcess, 0
xor eax, eax
inc eax ; EXCEPTION_EXECUTE_HANDLER
ret
ExceptionFilter endp
;**********************************************************
;函数功能:生成CRC-32表
;**********************************************************
init_crc32table_m proc
;如果用C语言来表示,应该如下:
;
; for (i = 0; i < 256; i++)
; {
; crc = i;
; for (j = 0; j < 8; j++)
; {
; if (crc & 1)
; crc = (crc >> 1) ^ 0xEDB88320;
; else
; crc >>= 1;
; }
; crc32tbl[i] = crc;
; }
;
;呵呵,让我们把上面的语句改成assembly的:
mov ecx, 256 ; repeat for every DWORD in table
mov edx, 0EDB88320h
$BigLoop_m:
lea eax, [ecx-1]
push ecx
mov ecx, 8
$SmallLoop_m:
shr eax, 1
jnc @F
xor eax, edx
@@:
dec ecx
jne $SmallLoop_m
pop ecx
mov [crc32tble+ecx*4-4], eax
dec ecx
jne $BigLoop_m
ret
init_crc32table_m endp
;**************************************************************
;函数功能:计算CRC-32
;**************************************************************
arraycrc32_m proc
;计算 CRC-32 ,我采用的是把整个字符串当作一个数组,然后把这个数组的首地址赋值给 EBX,把数组的长度赋值给 ECX,然后循环计算,返回值(计算出来的 CRC-32 值)储存在 EAX 中:
;
; 参数:
; EBX = address of first byte
; 返回值:
; EAX = CRC-32 of the entire array
; EBX = ?
; ECX = 0
; EDX = ?
mov eax, -1 ; 先初始化eax
or ebx, ebx
jz $Done_m ; 避免出现空指针
@@:
mov dl, [ebx]
or dl, dl
je $Done_m ;判断是否对字符串扫描完毕
;这里我用查表法来计算 CRC-32 ,因此非常快速:
;因为这是assembly代码,所以不需要给这个过程传递参数,只需要把oldcrc赋值给EAX,以及把byte赋值给DL:
;
; 在C语言中的形式:
;
; temp = (oldcrc ^ abyte) & 0x000000FF;
; crc = (( oldcrc >> 8) & 0x00FFFFFF) ^ crc32tbl[temp];
;
; 参数:
; EAX = old CRC-32
; DL = a byte
; 返回值:
; EAX = new CRC-32
; EDX = ?
xor dl, al
movzx edx, dl
shr eax, 8
xor eax, [crc32tble+edx*4]
inc ebx
jmp @B
$Done_m:
not eax
ret
arraycrc32_m endp
;收工!
end main
;******************** over ********************
;by LC and Comrade
[ 本帖最后由 u332744 于 2012-8-21 09:28 编辑 ]