下载:http://www.luocong.com/myworks/zipped/LCCrypto.zip
其中,有一段代码貌似是对PE中的Call、Push、Jmp等进行了重定向。例如原始节中,VirtualAddress是0x1000, 节中的代码就是Call 1001h。但是在写入别的PE的节时,新节的VirtualAddress可能是0x3000。就必须将要拷贝的节中的Call 1001h 改成 Call 3001h。这是什么原理?小弟不懂OpCode。请各位大牛指教。
重定向代码:
@@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
写入新节代码:
;*********************************************************
;写入加密节的模块
;*********************************************************
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(11).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
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!