Debug<<软件加密技术内幕>>--<<软件加密技术内幕>>读后感三篇
所谓Debug<<软件加密技术内幕>>也就是找出书中有不太完善的地方,或者说我对书中有疑问的地方.我读<<软件加密技术内幕>>有一段时间了,重点看了第一章,第二章,第三章,第四章前半部分,第六章前半部分,感觉书写得很经典!我在这里提出一些自己的看法,希望对其他读者有点帮助,贻笑大方了.
1.第一章15页表1-5的IMAGE_SECTION_HEADER结构少了一个字段:
PointerToLinenumbers.
2.第三章77页表3-3的tElock0.98特征数据不符合附书源码中的加壳记事本,而应该是:
编号 异常地址
1 0x0040DA1D
2 0x0040DA74
3 0x0040C08C
4 0x0040C090
5 0x0040C099
6 0x0040C09E
7 0x0040C0A3
8 0x0040C0A7
9 0x0040C6A8
10 0x0040CAA1
11 0x0040CAE4
12 0x0040CB27
13 0x0040CB67
14 0x0040CBA6
15 0x0040CBF0
16 0x0040CE0D
17 0x0040CE49
18 0x0040D6F1
19 0x0040D7E1
20 0x0040D817
编号为:
3,15,18的SHE发生地址是固定的.
我在"
tElock0.98脱壳--<<软件加密技术内幕>>读后感二篇 "中有所指出,但是对于不同的tElock版本可能又不同,我这里仅仅针对附书源码中的加壳记事本.
3.第三章94页第一行"图中的
SF标志就是Single Step"标志了",应该是""图中的
TF标志就是Single Step"",因为SF是符号为标志.
4.第六章第一节的"外壳编写基础"附书源码.
(1).BUG1.
书中有两处使用区块表名来判断是否是特殊区块表,如:
cmp dword ptr [esi], 'ade.'
jz MergeSectionOver
cmp dword ptr [esi], 'rsr.'
jz MergeSectionOver
cmp dword ptr [esi], 'oci.'
jz MergeSectionOver
但是如果对于一个程序,将其资源区块表名改为".r",那么我们的程序就会崩溃.这一点我验证了,可以按如下方法解决:
靠数据目录表来定位出特殊区块的RVA值并保存之,在遍历区块表时,将VirtualAddress字段与保存的特殊区块的RVA比较,如果相同则跳过.如:
mov eax,[esi].VirtualAddress
cmp eax,dwRsrRva
jz MergeSectionOver
(2)BUG2
较多地使用了区块表的Misc.VirtualSize,然而没有配合SizeOfRawData字段使用,有时程序也会崩溃.因为Misc.VirtualSize字段在文件中的含义是文件的实际字节数,然而它并不可靠,如对于'CODE','DATA',等区块来说它根本就没有用,
我试着将'CODE','DATA'区块表的Misc.VirtualSize清零,程序仍然可以正确加载,因为这时有用的是SizeOfRawData字段的值.但是只靠SizeOfRawData字段也是不可靠的,因为对于'BSS','.tls'区块来说,SizeOfRawData字段值为0,而这时Misc.VirtualSize便起到重要的作用了.所以仅仅只靠这两个字段中的一个是不够的,要配合起来使用,如:
mov eax,[edi].Misc.VirtualSize
.if eax==0
mov eax,[edi].SizeOfRawData
.endif
这样使用就比较安全了,但也不是绝对的.
(3)小技巧1
;********************在消息框中增加一行消息输出************************************
AddLine PROC LineToAdd: DWORD
LOCAL LineLength:DWORD
pusha
mov esi,LineToAdd
xor eax,eax
getlinelength:
cmp byte ptr [esi],0
jz Lengthgetted
inc eax
inc esi
jmp getlinelength
Lengthgetted:
mov LineLength, eax
mov edi, MessageBufferAddr
add edi, DebugMsgSize
mov esi, LineToAdd
mov ecx, LineLength
push ecx
shr ecx, 2
cld
rep movsd
pop ecx
and ecx, 3
rep movsb
mov dword ptr [edi], 00000A0Dh
mov eax, LineLength
add DebugMsgSize, eax
add DebugMsgSize, 2
invoke SetDlgItemText, hProtDlg, IDC_MESSAGEBOX_EDIT, MessageBufferAddr
invoke SendDlgItemMessage, hProtDlg, IDC_MESSAGEBOX_EDIT, EM_GETLINECOUNT, 0, 0
dec eax
invoke SendDlgItemMessage, hProtDlg, IDC_MESSAGEBOX_EDIT, EM_LINESCROLL, 0, eax
popa
ret
AddLine endp
;*****************************************************
可以改为:
;-------------------------------------------------
;向信息框里追加信息,参数lpMsg是信息串地址,使用了一个全局句柄:hEditInfo
_AddLine proc lpMsg
pushad
invoke SendMessage,hEditInfo,EM_SETSEL,-1,0
invoke SendMessage,hEditInfo,EM_REPLACESEL,0,lpMsg
popad
ret
_AddLine endp
;-------------------------------------------------
这样更加简练呵,原理是:将光标置于文本末尾,"替换掉"末尾的文本(实际没有选择任何文本),就实现了追加文本的效果,不过定义的字符串后面要有:0DH,0AH,0,因为要实现换行的.
(4)小技巧2
.if eax == WM_INITDIALOG ;选项对话框初始化
invoke GetPrivateProfileInt,addr IniSectionName,addr I_issaveset,0h,addr IniFileName
.if eax == 1
mov IsSaveSet,1
invoke CheckDlgButton,OptionhWnd,IDC_SAVESET,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_iscreatebak,0h,addr IniFileName
.if eax == 1
mov IsCreateBak,1
invoke CheckDlgButton,OptionhWnd,IDC_CREATEBAK,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_Ispackres,0h,addr IniFileName
.if eax == 1
mov IsPackRes,1
invoke CheckDlgButton,OptionhWnd,IDC_PACKRES,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_Issavesdata,0h,addr IniFileName
.if eax == 1
mov IsSaveSData,1
invoke CheckDlgButton,OptionhWnd,IDC_SDATA,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_Iscodeprot,1h,addr IniFileName
.if eax == 1
mov IsCodeProt,1
invoke CheckDlgButton,OptionhWnd,IDC_CODEPROT,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_Clssecname,1h,addr IniFileName
.if eax == 1
mov IsClsSecName,1
invoke CheckDlgButton,OptionhWnd,IDC_CLSSECNAME,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_Ismergesection,1h,addr IniFileName
.if eax == 1
mov IsMergeSection,1
invoke CheckDlgButton,OptionhWnd,IDC_MERGESECTION,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_Isprotimptable,1h,addr IniFileName
.if eax == 1
mov IsProtImpTable,1
invoke CheckDlgButton,OptionhWnd,IDC_APIPROT,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_IsReFileHead,1h,addr IniFileName
.if eax == 1
mov IsReFileHead,1
invoke CheckDlgButton,OptionhWnd,IDC_REFILEHEAD,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_IsFileAlignment200,1h,addr IniFileName
.if eax == 1
mov IsFileAlignment200,1
invoke CheckDlgButton,OptionhWnd,IDC_FA200,BST_CHECKED
.endif
invoke GetPrivateProfileInt,addr IniSectionName,addr I_IsNoRelocation,1h,addr IniFileName
.if eax == 1
mov IsNoRelocation,1
invoke CheckDlgButton,OptionhWnd,IDC_NOREL0,BST_CHECKED
.endif
以及
;***************保存设置***************************
SaveTheSet PROC
pushad
.if IsSaveSet == 1
.if IsSaveSet == 1
inc I_issaveset_D
.endif
.if IsCreateBak == 1
inc I_iscreatebak_D
.endif
.if IsMergeSection == 1
inc I_Ismergesection_D
.endif
.if IsPackRes == 1
inc I_Ispackres_D
.endif
.if IsSaveSData == 1
inc I_Issavesdata_D
.endif
.if IsProtImpTable == 1
inc I_Isprotimptable_D
.endif
.if IsCodeProt == 1
inc I_Iscodeprot_D
.endif
.if IsClsSecName == 1
inc I_Clssecname_D
.endif
.if IsReFileHead == 1
inc I_IsReFileHead_D
.endif
.if IsFileAlignment200 == 1
inc I_IsFileAlignment200_D
.endif
.if IsNoRelocation == 1
inc I_IsNoRelocation_D
.endif
invoke WritePrivateProfileString,addr IniSectionName,addr I_issaveset,addr I_issaveset_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_iscreatebak,addr I_iscreatebak_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_Ispackres,addr I_Ispackres_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_Issavesdata,addr I_Issavesdata_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_Iscodeprot,addr I_Iscodeprot_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_Clssecname,addr I_Clssecname_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_Ismergesection,addr I_Ismergesection_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_Isprotimptable,addr I_Isprotimptable_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_IsReFileHead,addr I_IsReFileHead_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_IsFileAlignment200,addr I_IsFileAlignment200_D,addr IniFileName
invoke WritePrivateProfileString,addr IniSectionName,addr I_IsNoRelocation,addr I_IsNoRelocation_D,addr IniFileName
.endif
popad
ret
SaveTheSet endp
是实现选项的加载与保存的,我实现的方法如下:
;-------------------------------------------------
;加载设置
invoke CreateFile,offset szIniFileName, GENERIC_READ,\
FILE_SHARE_READ,0, 3,FILE_ATTRIBUTE_NORMAL,NULL
push eax
invoke ReadFile,eax,offset dwSettings,4*(IDC_CHK_END - IDC_CHK_START + 1),offset dwBytesOfRW,NULL
pop eax
invoke CloseHandle,eax
lea esi,dwSettings
mov edi,IDC_CHK_START
mov ecx,IDC_CHK_END - IDC_CHK_START + 1
@@:
push ecx
lodsd
invoke CheckDlgButton,hWnd,edi,eax
inc edi
pop ecx
loop @B
;-------------------------------------------------
;保存设置
invoke CreateFile,offset szIniFileName, GENERIC_WRITE,\
FILE_SHARE_READ,0, OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
push eax
invoke WriteFile,eax,offset dwSettings,4*(IDC_CHK_END - IDC_CHK_START + 1),offset dwBytesOfRW,NULL
pop eax
invoke CloseHandle,eax
;-------------------------------------------------
(5)小技巧3
.if eax == IDC_OPTIONOK
;*******创建备份吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_CREATEBAK, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsCreateBak,1
.else
mov IsCreateBak,0
.endif
;*******清空段名吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_CLSSECNAME, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsClsSecName,1
.else
mov IsClsSecName,0
.endif
;*******合并区段吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_MERGESECTION, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsMergeSection,1
.else
mov IsMergeSection,0
.endif
;********压缩资源吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_PACKRES, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsPackRes,1
.else
mov IsPackRes,0
.endif
;********保留额外数据吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_SDATA, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsSaveSData,1
.else
mov IsSaveSData,0
.endif
;*******输入表加密吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_APIPROT, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsProtImpTable,1
.else
mov IsProtImpTable,0
.endif
;*******重算文件头大小吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_REFILEHEAD, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsReFileHead,1
.else
mov IsReFileHead,0
.endif
;*******强制文件对齐为200吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_FA200, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsFileAlignment200,1
.else
mov IsFileAlignment200,0
.endif
;*******去处重定位数据吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_NOREL0, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsNoRelocation,1
.else
mov IsNoRelocation,0
.endif
;********特殊代码加密吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_CODEPROT, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsCodeProt,1
.else
mov IsCodeProt,0
.endif
;********保存设置吗?
invoke SendDlgItemMessage, OptionhWnd, IDC_SAVESET, BM_GETCHECK, 0, 0
.if eax == BST_CHECKED
mov IsSaveSet,1
.else
mov IsSaveSet,0
.endif
.endif
是选择CheckBox时的事件处理代码,可以改写为:
;-------------------------------------------------
mov eax,wParam
.if ax>=IDC_CHK_START && ax<=IDC_CHK_END
lea edi,dwSettings
mov ecx,eax
sub ecx,IDC_CHK_START
shl ecx,2
add edi,ecx
invoke SendDlgItemMessage, hWnd, eax, BM_GETCHECK, 0, 0
stosd
.endif
;-------------------------------------------------
(6)小技巧4
打开文件:
.if eax == IDC_OPEN_BUTTON ;是打开文件命令吗?
invoke RtlZeroMemory,MessageBufferAddr,1000h
mov DebugMsgSize,0
invoke RtlZeroMemory,OFFSET FileName,MAXSIZE ;清空文件名缓冲
mov Openfilename.lStructSize,SIZEOF Openfilename
push ProthWnd
pop Openfilename.hWndOwner
push hInst
pop Openfilename.hInstance
mov Openfilename.lpstrFilter, OFFSET FilterString
mov Openfilename.lpstrFile, OFFSET FileName
mov Openfilename.nMaxFile,MAXSIZE
mov Openfilename.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR Openfilename
.IF eax==TRUE ;文件打开成功
invoke SetDlgItemText,ProthWnd,IDC_FILE_EDIT,ADDR FileName
invoke FileIsExe ;是合法的PE-EXE文件吗?
.if eax==1
invoke EnableWindow, hProtButton, TRUE
.else
invoke EnableWindow, hProtButton, FALSE
.endif
.ENDIF
...... 可以写成一个独立的函数:
;-------------------------------------------------
;打开PE文件,参数:hWinMain为主窗体句柄
;lpfilebuff为文件名缓冲区
;lpext文件默认扩展名
;lpfilter过滤器
_OpenFile proc lpfilebuff,lpfilter,lpext
local @stOF:OPENFILENAME
invoke RtlZeroMemory,addr @stOF,sizeof @stOF
mov @stOF.lStructSize,sizeof @stOF
push hWinMain
pop @stOF.hwndOwner
push lpfilter
pop @stOF.lpstrFilter
push lpfilebuff
pop @stOF.lpstrFile
mov @stOF.nMaxFile,MAX_PATH
mov @stOF.Flags,OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST
push lpext
pop @stOF.lpstrDefExt
invoke GetOpenFileName,addr @stOF
ret
_OpenFile endp
;-------------------------------------------------
(7)小技巧5
;***************移动字符串***************************
MoveString PROC SourceStringADDR:DWORD,ObjectStringADDR:DWORD
;将源地址指向的以'00'结尾的字符串复制到目标地址指向的空间,并加密
;除eax外所有寄存器都不变,输出eax的值为复制的字节数
push edi
push esi
push ecx
mov edi,ObjectStringADDR
mov esi,SourceStringADDR
xor eax,eax
xor ecx,ecx
MoveNextByte:
lodsb
.if eax!=0
stosb
inc ecx
.else
jmp AllByteMoved
.endif
xor eax,eax
jmp MoveNextByte
AllByteMoved:
mov eax,ecx
pop ecx
pop esi
pop edi
ret
MoveString endp
;********************清除一个字符串********************************
ClsString PROC SourceStringADDR:DWORD
;将一个以'00'结束的字符串都清为'00'
push esi
push edi
push ecx
mov esi,SourceStringADDR
mov edi,SourceStringADDR
xor eax,eax
xor ecx,ecx
ClsNextByte:
lodsb
.if eax!=0
xor al,al
stosb
inc ecx
.else
jmp AllByteCls
.endif
xor eax,eax
jmp ClsNextByte
AllByteCls:
mov eax,ecx
pop ecx
pop edi
pop esi
ret
ClsString endp
改进:MoveString可以将字符串末尾的空字符(NULL),也一并复制过去,字符串长度包含0.这样较好,调用者就不必关心字符串后面的结束符0了.
;***************移动字符串***************************
MoveString PROC uses esi edi ecx lpStrSrc:DWORD,lpStrDst:DWORD
;将源地址指向的以'00'结尾的字符串复制到目标地址指向的空间,并加密
;除eax外所有寄存器都不变,输出eax的值为复制的字节数
mov edi,lpStrDst
mov esi,lpStrSrc
xor ecx,ecx
MoveNextByte:
lodsb
test al,al
jz @Over
stosb
inc ecx
jmp MoveNextByte
@Over:
stosb ;存储最后一个0
inc ecx
mov eax,ecx
ret
MoveString endp
;********************清除一个字符串********************************
ClsString PROC lpStr
;将一个以'00'结束的字符串都清为'00'
pushad
mov esi,lpStr
mov edi,lpStr
ClsNextByte:
lodsb
test al,al
jz @Over
xor al,al
stosb
jmp ClsNextByte
@Over:
popad
ret
ClsString endp
(8)小技巧6
;***************产生垃圾指令***********************
MakeFunkCode PROC FuncCodeBase:DWORD
LOCAL FunkCodeSize:DWORD
pushad
mov FunkCodeSize,0h
mov ecx,100h
mov edi,FuncCodeBase
MakeNextFunkCode:
push ecx
rdtsc
and eax,7h
.if eax == 0
lea esi,Junk_Code_1_Start
mov ecx,Junk_Code_1_End-Junk_Code_1_Start
add FunkCodeSize,ecx
rep movsb
.elseif eax == 1
lea esi,Junk_Code_2_Start
mov ecx,Junk_Code_2_End-Junk_Code_2_Start
add FunkCodeSize,ecx
rep movsb
.elseif eax == 2
lea esi,Junk_Code_3_Start
mov ecx,Junk_Code_3_End-Junk_Code_3_Start
add FunkCodeSize,ecx
rep movsb
.elseif eax == 3
lea esi,Junk_Code_4_Start
mov ecx,Junk_Code_4_End-Junk_Code_4_Start
add FunkCodeSize,ecx
rep movsb
.elseif eax == 4
lea esi,Junk_Code_5_Start
mov ecx,Junk_Code_5_End-Junk_Code_5_Start
add FunkCodeSize,ecx
rep movsb
.elseif eax == 5
lea esi,Junk_Code_6_Start
mov ecx,Junk_Code_6_End-Junk_Code_6_Start
add FunkCodeSize,ecx
rep movsb
.elseif eax == 6
lea esi,Junk_Code_7_Start
mov ecx,Junk_Code_7_End-Junk_Code_7_Start
add FunkCodeSize,ecx
rep movsb
.elseif eax == 7
lea esi,Junk_Code_8_Start
mov ecx,Junk_Code_8_End-Junk_Code_8_Start
add FunkCodeSize,ecx
rep movsb
.endif
xor eax,eax
pop ecx
dec ecx
jnz MakeNextFunkCode
popad
mov eax,FunkCodeSize
ret
MakeFunkCode endp
由于代码很多有重复的,可以使用宏实现,不过我不推荐使用宏,改个组织结构:
;***************产生垃圾指令***********************
MakeFunkCode PROC FuncCodeBase:DWORD
LOCAL @FuckCodeSize:DWORD
pushad
and @FuckCodeSize,0h
mov ecx,100h
mov edi,FuncCodeBase
@@:
push ecx
rdtsc
and eax,7h
lea esi,lpFuckCode
shl eax,3
add esi,eax
mov ecx,dword ptr[esi+4]
add @FuckCodeSize,ecx
mov esi,dword ptr[esi]
rep movsb
pop ecx
loop @B
popad
mov eax,@FuckCodeSize
ret
MakeFunkCode endp
lpFuckCode equ this dword
FuckCode1_Addr dd Junk_Code_1_Start
FuckCode1_Size dd Junk_Code_1_End - Junk_Code_1_Start
FuckCode2_Addr dd Junk_Code_2_Start
FuckCode2_Size dd Junk_Code_2_End - Junk_Code_2_Start
FuckCode3_Addr dd Junk_Code_3_Start
FuckCode3_Size dd Junk_Code_3_End - Junk_Code_3_Start
FuckCode4_Addr dd Junk_Code_4_Start
FuckCode4_Size dd Junk_Code_4_End - Junk_Code_4_Start
FuckCode5_Addr dd Junk_Code_5_Start
FuckCode5_Size dd Junk_Code_5_End - Junk_Code_5_Start
FuckCode6_Addr dd Junk_Code_6_Start
FuckCode6_Size dd Junk_Code_6_End - Junk_Code_6_Start
FuckCode7_Addr dd Junk_Code_7_Start
FuckCode7_Size dd Junk_Code_7_End - Junk_Code_7_Start
FuckCode8_Addr dd Junk_Code_8_Start
FuckCode8_Size dd Junk_Code_8_End - Junk_Code_8_Start还在完善中,总之改过后生成的程序小了很多,而且加壳过后的程序也小了不少,继续努力!
永远支持看雪!
by Sing
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法