-
-
[看雪读书月]看雪论坛读书月第一题答案
-
发表于: 2008-7-12 01:06 6708
-
分析报告、源码、注册机都已经上传
一、 通过关键字“非法用户”找到这里:
这段代码主要检查用户名是否合法,用户名合法条件为:
1. 用户名由字母b~y组成;
2. 用户名为6个字符;
3. 由于判断不严谨,可以前6个字符满足要求1,第七个字符不满足要求1,就可以跳过这里的“非法用户”判断。
.text:00401D82 mov [ebp+i], 0 初始化变量,用于记录用户名长度
.text:00401D89
.text:00401D89 loc_401D89:
.text:00401D89 mov ecx, [ebp+UserName]
.text:00401D8C add ecx, [ebp+i]
.text:00401D8F movsx edx, byte ptr [ecx]
.text:00401D92 test edx, edx
.text:00401D94 jz short loc_401DBD 检查用户名是否结束
.text:00401D96 mov eax, [ebp+UserName]
.text:00401D99 add eax, [ebp+i]
.text:00401D9C movsx ecx, byte ptr [eax]
.text:00401D9F cmp ecx, 61h
.text:00401DA2 jle short loc_401DBD 检查用户名是否小于等于a
.text:00401DA4 mov edx, [ebp+UserName]
.text:00401DA7 add edx, [ebp+i]
.text:00401DAA movsx eax, byte ptr [edx]
.text:00401DAD cmp eax, 7Ah
.text:00401DB0 jge short loc_401DBD 检查用户名是否大于等于z
.text:00401DB2 mov ecx, [ebp+i]
.text:00401DB5 add ecx, 1
.text:00401DB8 mov [ebp+i], ecx
.text:00401DBB jmp short loc_401D89 循环执行
.text:00401DBD ; ---------------------------------------------------------------------------
.text:00401DBD
.text:00401DBD loc_401DBD:
.text:00401DBD cmp [ebp+i], 6 检查用户名长度是否为6
.text:00401DC1 jz short loc_401DD6 若等于6,跳过“非法用户”
.text:00401DC3 push 0
.text:00401DC5 push 0
.text:00401DC7 push offset aIZ ; "非法用户!"
.text:00401DCC mov ecx, [ebp+var_4]
.text:00401DCF call MessageBoxA
.text:00401DD6 loc_401DD6:
.text:00401DD6 mov edx, [ebp+UserName]
.text:00401DD9 push edx ; Source
.text:00401DDA push offset UserName ; Dest
.text:00401DDF call strcpy ;复制用户名
.text:00401DE4 add esp, 8
.text:00401DE7 mov eax, [ebp+Password]
.text:00401DEA push eax ; Source
.text:00401DEB push offset Password ; Dest
.text:00401DF0 call strcpy ;复制序列号
.text:00401DF5 add esp, 8
.text:00401DF8 mov [ebp+_ESP], esp
.text:00401DFB mov ecx, [ebp+Password]
.text:00401DFE push ecx
.text:00401DFF mov edx, [ebp+UserName]
.text:00401E02 push edx
.text:00401E03 mov eax, [ebp+_ESP]
.text:00401E06 push eax
.text:00401E07 call sub_401BB0 ;将用户名、序列号、ESP压栈用于验证帐号及序列号
.text:00401E0C add esp, 0Ch
二、通过压入的ESP找到父函数的返回地址,并将其覆盖为第三次验证序列号的函数地址:
.text:00401BD2 mov eax, [ebp+FATHER_ESP]
.text:00401BD5 sub eax, 10h
.text:00401BD8 mov [ebp+pRetAddr], eax
.text:00401BDB mov ecx, [ebp+pRetAddr]
.text:00401BDE mov dword ptr [ecx], offset Msg_Info
下面开始为一个循环体用于第二次验证用户名以及序列号,若验证失败,则将返回地址改成弹出错误窗口的函数地址:
转换为C语言函数如下:
for (int i = 0; i < 6; i++)
{
//当该条件不成立时,直接弹出错误并退出
if (UserName[i] != Password[2*i] + 0x1B)
{
call Msg_Error;
}
//当该条件不成立时,将返回地址覆盖为报错函数,结果一样
if (UserName[i] <= Password[2*i + 1] + 0x20)
{
pRetAddr = Msg_Error;
}
}
.text:00401BE4 mov [ebp+i_UserName], 0
.text:00401BEB mov [ebp+i_Password], 0
.text:00401BF2 ; hua_code
.text:00401BFE
.text:00401BFE loc_401BFE:
.text:00401BFE cmp [ebp+i_UserName], 6 检查变量
.text:00401C02 jge short loc_401C71 大于等于6时跳出循环
.text:00401C04 ; hua_code
.text:00401C10 mov edx, [ebp+UserName]
.text:00401C13 add edx, [ebp+i_UserName]
.text:00401C16 movsx eax, byte ptr [edx]
.text:00401C19 mov ecx, [ebp+Password]
.text:00401C1C add ecx, [ebp+i_Password]
.text:00401C1F movsx edx, byte ptr [ecx]
.text:00401C22 add edx, 1Bh
.text:00401C25 cmp eax, edx ; UserName[i] == Password[2*i] + 0x1B ?
.text:00401C27 jz short loc_401C2E ; 相同时跳过报错
.text:00401C29 call Msg_Error
.text:00401C2E ; hua_code
.text:00401C3A mov eax, [ebp+UserName]
.text:00401C3D add eax, [ebp+i_UserName]
.text:00401C40 movsx ecx, byte ptr [eax]
.text:00401C43 mov edx, [ebp+Password]
.text:00401C46 add edx, [ebp+i_Password]
.text:00401C49 movsx eax, byte ptr [edx+1]
.text:00401C4D add eax, 20h
.text:00401C50 cmp ecx, eax ; UserName[i] <= Password[2*i + 1] + 0x20 ?
.text:00401C52 jle short loc_401C5D ; 小于等于时跳过报错
.text:00401C54 mov ecx, [ebp+pRetAddr]
.text:00401C57 mov dword ptr [ecx], offset Msg_Error
.text:00401C5D
.text:00401C5D loc_401C5D:
.text:00401C5D mov edx, [ebp+i_Password] ; 密码左移2位
.text:00401C60 add edx, 2
.text:00401C63 mov [ebp+i_Password], edx
.text:00401C66 mov eax, [ebp+i_UserName] ; 用户名左移1位
.text:00401C69 add eax, 1
.text:00401C6C mov [ebp+i_UserName], eax
.text:00401C6F jmp short loc_401BFE
.text:00401C71 loc_401C71:
.text:00401C71 mov [ebp+var_4], 0
.text:00401C78 ; hua_code
三、这里人为造成一个异常,跳到异常处理函数中,其eax是指向_S_FUNCINFO结构体的指针,该结构体如下:
_S_FUNCINFO STRUCT
MagicNumber DWORD ? magic number
MaxState DWORD ? max state in the function
PUnwindMap DWORD ? unwind function list
NTryBlocks DWORD ? how many try blocks
PTryBlockMap DWORD ? try block list
_S_FUNCINFO ENDS
而我们在0x004036F8中看到的数据如下:
004036F8 19930520
004036FC 00000002
00403700 00403718 cm.00403718
00403704 00000001
00403708 00403728 cm.00403728
关键在于PTryBlockMap这个变量,即异常处理数组指针,也是一个结构体,其结构如下:
S_TRYBLOCKMAPENTRY STRUCT
TryLow DWORD ? begin state
TryHi DWORD ? end state
CatchHi DWORD ? catch end state
NCatches DWORD ? how many catches
PHandlerArray DWORD ? cathe block entry list
S_TRYBLOCKMAPENTRY ENDS
而我们在0x403728地址中看到的该结构体的数据为:
00403728 00000000
0040372C 00000000
00403730 00000001
00403734 00000001
00403738 00403740 cm.00403740
关键在于PHandlerArray这个变量,这个指针指向的结构体中即是我们所要的信息,结构体如下:
_S_HANDLERTYPE STRUCT
Adjectives DWORD ? properites
PType DWORD ? type_info pointer
DispCatchObj DWORD ? offset from ebp
AddressOfHandler DWORD ? handler address
_S_HANDLERTYPE ENDS
而我们在0x403740得到的数据如下:
00403740 00000000
00403744 00000000
00403748 00000000
0040374C 00401C93 cm.00401C93
在这里可以看出401C93地址为异常处理函数的地址,可以在网上找到相关资料:
.text:00401C84 mov [ebp+var_20], 29Ch
.text:00401C8B mov ecx, [ebp+var_20]
.text:00401C8E mov byte ptr [ecx], 6
……
.text:004026F0 SEH_401BB0 proc near ; DATA XREF: sub_401BB0+5 o
.text:004026F0 mov eax, offset stru_4036F8
.text:004026F5 jmp __CxxFrameHandler
.text:004026F5 SEH_401BB0 endp
四、该函数用于反调试(穷举当前进程ID,得到explorer的ID,检查本进程的父进程ID是否与explorer.exe的ID相同,若不同则关闭本程序),过程比较简单,就不贴代码了:
.text:00401C93 loc_401C93:
.text:00401C93 call anti_debug
五、这里通过将返回值赋值给EAX,使其跳转到401C9E:
.text:00401C98 mov eax, offset loc_401C9E
.text:00401C9D retn
六、将异常处理链的地址改成父函数的异常链,由于返回地址已经被修改,下面的retn返回到第三个处理用户名与序列号的函数:
.text:00401C9E loc_401C9E:
.text:00401C9E mov [ebp+var_4], 0FFFFFFFFh
.text:00401CA5 mov ecx, [ebp+var_C]
.text:00401CA8 mov large fs:0, ecx
.text:00401CAF pop edi
.text:00401CB0 pop esi
.text:00401CB1 pop ebx
.text:00401CB2 mov esp, ebp
.text:00401CB4 pop ebp
.text:00401CB5 retn
七、这里为一个循环体,通过用户名和一组顺序大写字符串A,算出一组字符串B用于检查序列号是否正确,下面用字符串A、B表示:
初始化部分代码:
.text:00401812 mov eax, [ebp+var0_ABCDE__WXY]
.text:00401815 mov [ebp+var1_ABCDE__WXY], eax
.text:00401818 mov [ebp+i], 0
.text:0040181F
检查变量是否为18h,即24,若等于,则跳出循环:
.text:0040181F loc_40181F:
.text:0040181F cmp [ebp+i], 18h
.text:00401823 jz loc_4018BB
.text:00401829 ; hua_code
检查用户名的首尾字符是否等于字符串A当前的首字母的小写,若相等,数组左移一位:
.text:00401835 movsx ecx, UserName
.text:0040183C mov edx, [ebp+var0_ABCDE__WXY]
.text:0040183F movsx eax, byte ptr [edx]
.text:00401842 add eax, 20h
.text:00401845 cmp ecx, eax
.text:00401847 jz short loc_40186A
.text:00401849 push offset UserName ; Str
.text:0040184E call strlen
.text:00401853 add esp, 4
.text:00401856 movsx ecx, byte_40416B[eax]
.text:0040185D mov edx, [ebp+var0_ABCDE__WXY]
.text:00401860 movsx eax, byte ptr [edx]
.text:00401863 add eax, 20h
.text:00401866 cmp ecx, eax
.text:00401868 jnz short loc_401873
.text:0040186A
.text:0040186A loc_40186A:
.text:0040186A mov ecx, [ebp+var0_ABCDE__WXY]
.text:0040186D add ecx, 1
.text:00401870 mov [ebp+var0_ABCDE__WXY], ecx
将当前字符串A的首字母放入字符串B中:
.text:00401873 loc_401873:
.text:00401873 mov edx, [ebp+i]
.text:00401876 mov eax, [ebp+var0_ABCDE__WXY]
.text:00401879 mov cl, [eax]
.text:0040187B mov [ebp+edx+var_CrackStr], cl
变量自增一,字符串A左移2位:
.text:0040187F mov edx, [ebp+i]
.text:00401882 add edx, 1
.text:00401885 mov [ebp+i], edx
.text:00401888 mov eax, [ebp+var0_ABCDE__WXY]
.text:0040188B add eax, 2
.text:0040188E mov [ebp+var0_ABCDE__WXY], eax
.text:00401891 ; hua_code
左移后检查字符串A是否已经移动到末尾,若未移动到末尾,则继续循环:
.text:0040189D mov ecx, [ebp+var0_ABCDE__WXY]
.text:004018A0 movsx edx, byte ptr [ecx]
.text:004018A3 test edx, edx
.text:004018A5 jnz short loc_4018B6
若已经移动到了末尾,则检查是否已经组成了24个字符长度的字符串B,若已经组成,则退出循环,否则将字符串B的地址自加一后赋值给字符串A,继续循环:
.text:004018A7 cmp [ebp+i], 18h
.text:004018AB jge short loc_4018B6
.text:004018AD mov eax, [ebp+var1_ABCDE__WXY]
.text:004018B0 add eax, 1
.text:004018B3 mov [ebp+var0_ABCDE__WXY], eax
.text:004018B6
.text:004018B6 loc_4018B6:
.text:004018B6 jmp loc_40181F
八、这一步注意修改MessageBox的句柄,即密码的第十一位一定要为 ”U”,否则就会失败:
.text:004018BB push 0 ; uType
.text:004018BD push offset aA ; "错了!"
.text:004018C2 push offset asc_403554 ; "继续努力!"
.text:004018C7 movsx ecx, byte_4040F6
.text:004018CE sub ecx, 55h 0x55 == “U”
.text:004018D1 neg ecx
.text:004018D3 sbb ecx, ecx
.text:004018D5 inc ecx
.text:004018D6 push ecx ; hWnd
.text:004018D7 call ds:MessageBoxA
九、这里是一个双层循环,也是最后的验证:
初始化变量:
.text:004018FB mov [ebp+i], 0
.text:00401902 mov [ebp+var_30], 5
.text:00401909 mov [ebp+j], 0
var_30作为基值,乘4减4后赋值给字符串B的偏移:
.text:00401910 loc_401910:
.text:00401910 cmp [ebp+i], 0Ch
.text:00401914 jz loc_4019D5
.text:0040191A mov eax, [ebp+var_30]
.text:0040191D lea ecx, ds:0FFFFFFFCh[eax*4] ; eax * 4 - 4
.text:00401924 mov [ebp+Str_Offset], ecx
这里为里面的循环,允许字符串B偏移为首地址的4个字符中任意一个为当前的序列号值:
.text:00401927 loc_401927:
.text:00401927 mov edx, [ebp+i]
.text:0040192A movsx eax, Password[edx]
.text:00401931 mov ecx, [ebp+Str_Offset]
.text:00401934 movsx edx, [ebp+ecx+var_CrackStr]
.text:00401939 mov ecx, [ebp+Str_Offset]
.text:0040193C add ecx, 1
.text:0040193F mov [ebp+Str_Offset], ecx
.text:00401942 cmp eax, edx
.text:00401944 jz short loc_401959
.text:00401946 mov edx, [ebp+j]
.text:00401949 add edx, 1
.text:0040194C mov [ebp+j], edx
.text:0040194F cmp [ebp+j], 4
.text:00401953 jle short loc_401957
.text:00401955 jmp short loc_401959
.text:00401957 loc_401957:
.text:00401957 jmp short loc_401927
.text:00401959 loc_401959:
.text:00401959 cmp [ebp+j], 5 ; 检查是不是超过5次
.text:0040195D jnz short loc_40199E ; 超过5次不对即报错
.text:00401999 call Msg_Error
序列号的序号自加2:
.text:0040199E loc_40199E:
.text:0040199E mov eax, [ebp+i]
.text:004019A1 add eax, 2
.text:004019A4 mov [ebp+i], eax
;var_30基值自减1,减到0时初始化为6:
.text:004019A7 mov ecx, [ebp+var_30]
.text:004019AA sub ecx, 1
.text:004019AD mov [ebp+var_30], ecx
.text:004019B0 ; hua_code
.text:004019BC cmp [ebp+var_30], 0
.text:004019C0 jnz short loc_4019C9
.text:004019C2 mov [ebp+var_30], 6
.text:004019C9 loc_4019C9:
.text:004019C9 mov [ebp+j], 0
.text:004019D0 jmp loc_401910 这是外循环的回跳处
.text:004019D5 ; ---------------------------------------------------------------------------
.text:004019D5 loc_4019D5: ; CODE XREF: Msg_Info+124 j
.text:004019D5 call Msg_Ok 最后报成功~~~~!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)