📄 lccrypto.asm
字号:
;读入 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -