-
-
[原创]第四题 达芬奇密码 by 心学
-
发表于: 2019-6-17 21:38 4190
-
工具:IDA、Python、Notepad++、Resource_Hacker
一、题目主要流程:
1、获取用户输入字符串sn:16位
2、解密函数代码sub_4010E0
3、进入
sub_4010E0内
4、将输入字符串sn分割为两个8位字符串,变成两个超大整数sn[0]、sn[1],各8个字节长
5、两个超大整数与内置的两个超大整数异或运算,得到新的两个超大整数 X、Y
6、检测:X、Y内各个字节不能有00存在:目的是为了保证高位字节不能为00,也就是避免了多解的可能
7、检测:第7个字节(从0计算),需为01至0F之间,经过异或运算0x64还原,该输入字节只能为0x60-0x6F:即
` a b c ^ o
8、检测:X^2 - 7 * Y^2 =8;这个是重点,丢番图方程,通过搜索网页,才知道这个方程解法
9、通过,即为正确字符串,L3mZ2k9aZ0a36DMM
1、还原
sub_4010E0 ,Patch掉主程序,便于静态分析
1.1、找到关键CALL:004010E0
1.2、004010E0是由从00567B8拷贝0x330字节
1.3、00567B8的内容是通过XOR 0xAB算出来的
1.4、Patch文件,便于静态分析
1.4.1、修改 XOR 地址处的汇编为90
1.4.2、修改 00567B8的内容为解密的内容,即算出0xAB后,复原。
1.1、找到关键CALL:004010E0
1.2、004010E0是由从00567B8拷贝0x330字节
1.3、00567B8的内容是通过XOR 0xAB算出来的
1.4、Patch文件,便于静态分析
1.4.1、修改 XOR 地址处的汇编为90
1.4.2、修改 00567B8的内容为解密的内容,即算出0xAB后,复原。
2、学习丢番图方程的通解
X^2 - 7 * Y^2 =8 :找到满足条件的解
具体可以学习丢番图方程的解法,该通解是:( 6 + 2 * sqrt(7) ) ( 8 + 3 * sqrt(7) )^n;我们可以通过程序来列出在一定范围的解
3、根据上述流程,反推输入字符串
三、主要源代码分析过程(流程):
1、IDA内找到软件入口:
三、主要源代码分析过程(流程):
1、IDA内找到软件入口:
1、IDA内找到软件入口:
因为是MFC程序,所以我们要找到按钮的触发方法才行
方法:找到Button等控件的ID,通过
Resource_Hacker查找,打开Dialog表,
102 DIALOGEX 0, 0, 319, 117 STYLE DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION EXSTYLE WS_EX_APPWINDOW CAPTION "SequenceAdventure (CTF2019)" LANGUAGE LANG_CHINESE, 0x2 FONT 9, "MS Shell Dlg" { CONTROL "Check", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 7, 90, 305, 17 CONTROL "", 1000, EDIT, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 7, 18, 305, 67 CONTROL "Please Input The Serial:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 5, 123, 12 CONTROL "Close", 1002, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 241, 1, 50, 14 }
我们能发现 Button Check的ID为1(估计是作者故意的,搞那么小);附近有一个Close,其ID是1002(0x3EA),我们通过它来找到对应的dialog
102 DIALOGEX 0, 0, 319, 117 STYLE DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION EXSTYLE WS_EX_APPWINDOW CAPTION "SequenceAdventure (CTF2019)" LANGUAGE LANG_CHINESE, 0x2 FONT 9, "MS Shell Dlg" { CONTROL "Check", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 7, 90, 305, 17 CONTROL "", 1000, EDIT, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 7, 18, 305, 67 CONTROL "Please Input The Serial:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 5, 123, 12 CONTROL "Close", 1002, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 241, 1, 50, 14 }
我们能发现 Button Check的ID为1(估计是作者故意的,搞那么小);附近有一个Close,其ID是1002(0x3EA),我们通过它来找到对应的dialog
IDA:Search 搜索立即数:0x3EA,很快就在.rdata里定位到具体内容
大佬一般看到后,就能很快找到对应的处理方法,我们可以先添加具体的消息处理结果,便于理解,添加两个结构
AFX_MSGMAP_ENTRY和AFX_MSGMAP,主要先后添加:IDA→View→ Sub Views→Local Types:按Insert键,先后插入两个消息定义
struct AFX_MSGMAP_ENTRY { UINT nMessage; UINT nCode; UINT nID; UINT nLastID; UINT_PTR nSig; void (*pfn)(void); }; struct AFX_MSGMAP { const AFX_MSGMAP *(__stdcall *pfnGetBaseMap)(); const AFX_MSGMAP_ENTRY *lpEntries; };
选中刚才定义的消息(拖入到最下面,能看到最新添加的),然后右键,同步到 idb
现在定位到刚才的
.rdata 的0x3EA处。
struct AFX_MSGMAP_ENTRY { UINT nMessage; UINT nCode; UINT nID; UINT nLastID; UINT_PTR nSig; void (*pfn)(void); }; struct AFX_MSGMAP { const AFX_MSGMAP *(__stdcall *pfnGetBaseMap)(); const AFX_MSGMAP_ENTRY *lpEntries; };
选中刚才定义的消息(拖入到最下面,能看到最新添加的),然后右键,同步到 idb
现在定位到刚才的
.rdata 的0x3EA处。
.rdata:005456D8 db 11h .rdata:005456D9 db 1 .rdata:005456DA db 0 .rdata:005456DB db 0 .rdata:005456DC db 0 .rdata:005456DD db 0 .rdata:005456DE db 0 .rdata:005456DF db 0 .rdata:005456E0 db 0EAh .rdata:005456E1 db 3 .rdata:005456E2 db 0 .rdata:005456E3 db 0 .rdata:005456E4 db 0EAh .rdata:005456E5 db 3 .rdata:005456E6 db 0 .rdata:005456E7 db 0 .rdata:005456E8 db 39h ; 9 .rdata:005456E9 db 0 .rdata:005456EA db 0 .rdata:005456EB db 0 .rdata:005456EC dd offset sub_402000
在 .rdata:005456D8 按 Art +Q键盘,选择AFX_MSGMAP_ENTRY,
最后,形成的结构如下所述
.rdata:00545690 stru_545690 AFX_MSGMAP_ENTRY <0Fh, 0, 0, 0, 13h, offset sub_401DE0> .rdata:00545690 ; DATA XREF: .rdata:stru_545708↓o .rdata:005456A8 AFX_MSGMAP_ENTRY <37h, 0, 0, 0, 28h, offset sub_401E90> .rdata:005456C0 AFX_MSGMAP_ENTRY <111h, 0, 1, 1, 39h, offset sub_401EA0> .rdata:005456D8 AFX_MSGMAP_ENTRY <111h, 0, 3EAh, 3EAh, 39h, offset sub_402000> .rdata:005456F0 AFX_MSGMAP_ENTRY <0> .rdata:00545708 stru_545708 AFX_MSGMAP <offset sub_4055EC, offset stru_545690>
然后,我们比对一下刚才在
Resource_Hacker查找的Dialog内容,很容易定位到button的ID为1 ,即check对应的是.rdata:005456C0 AFX_MSGMAP_ENTRY <111h, 0, 1, 1, 39h, offset sub_401EA0>
,然后我们双击
offset sub_401EA0 进入到该方法,按F5,很容易判断即为button check的处理方法。
int __thiscall btnCheck(CWnd *this) { CWnd *v1; // esi int i; // eax WCHAR inputStr; // [esp+Ch] [ebp-310h] char v5; // [esp+Eh] [ebp-30Eh] char newInputStr; // [esp+20Ch] [ebp-110h] char v7; // [esp+20Dh] [ebp-10Fh] DWORD v8; // [esp+30Ch] [ebp-10h] CWnd *v9; // [esp+310h] [ebp-Ch] int v10; // [esp+314h] [ebp-8h] DWORD flOldProtect; // [esp+318h] [ebp-4h] v1 = this; v9 = this; inputStr = 0; memset(&v5, 0, 0x1FEu); newInputStr = 0; memset(&v7, 0, 0xFFu); CWnd::GetDlgItemTextW(v1, 1000, &inputStr, 20); if ( wcslen(&inputStr) == 16 ) // 输入字符串长度:16 { i = 0; while ( !(*(&inputStr + i) & 0xFF00) ) // 字符应该是 0001 至 00FF;不能为 FF00 和 0100 的宽字符 { *(&newInputStr + i) = *((_BYTE *)&inputStr + 2 * i); if ( ++i >= 16 ) { v8 = 0x40; flOldProtect = 0; VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);// 0x40:PAGE_EXECUTE_READWRITE:内存地址、长度、访问、保存旧的 if ( GetLastError() ) return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); qmemcpy(sub_4010E0, byte_5647B8, 0x330u); VirtualProtect(sub_4010E0, 0xD17u, flOldProtect, &v8); if ( !GetLastError() ) { v10 = 0; v10 = sub_4010E0(&newInputStr); if ( v10 == 1 ) return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0); } v1 = v9; return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); } } } return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); }
2、分析主函数
sub_401EA0
里面有一个VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);这里有修改
qmemcpy(sub_4010E0, byte_5647B8, 0x330u);
很明显,它将
byte_5647B8内容覆盖了
sub_4010E0,继续跟进
byte_5647B8
.data:005647B8 ; char byte_5647B8[816] .data:005647B8 2A byte_5647B8 db 2Ah ; DATA XREF: btnCheck+E0↑o .data:005647B9 47 db 47h ; G .data:005647BA 3B db 3Bh ; ; .data:005647BB AB db 0ABh .data:005647BC AB db 0ABh .data:005647BD AB db 0ABh .data:005647BE FD db 0FDh .data:005647BF 1B db 1Bh .data:005647C0 2A db 2Ah ; * .data:005647C1 FC db 0FCh .data:005647C2 20 db 20h .data:005647C3 17 db 17h
.data:005647B8 2A byte_5647B8 db 2Ah ; DATA XREF: sub_401D80:loc_401DC0↑w .data:005647B9 47 db 47h ; G .data:005647BA 3B db 3Bh ; ; .data:005647BB AB db 0ABh .data:005647BC AB db 0ABh .data:005647BD AB db 0ABh .data:005647BE FD db 0FDh .data:005647BF 1B db 1Bh .data:005647C0 2A db 2Ah ; * .data:005647C1 FC db 0FCh .data:005647C2 20 db 20h .data:005647C3 17 db 17h
很明显,这个不是代码,我们看看有哪些代码访问了它,按x,找到条目(我在原代码做了Patch,其实该代码处有两处引用),一处是check处理;一处是修改
byte_5647B8,我们跟进sub_401D80,查看
signed int __thiscall sub_401D80(CDialog *this) { CDialog *v1; // esi unsigned int v2; // eax v1 = this; CDialog::OnInitDialog(this); SendMessageW(*((HWND *)v1 + 8), 0x80u, 1u, *((_DWORD *)v1 + 29)); SendMessageW(*((HWND *)v1 + 8), 0x80u, 0, *((_DWORD *)v1 + 29)); v2 = 0; do { byte_5647B8[v2] ^= 0xABu; ++v2; } while ( v2 < 0x330 ); return 1; }
它相当于对
byte_5647B8做了异或处理后又保存了,相当于原来进行了加密,现在进行了解密
3、Patch代码:以使其更容易静态分析
我们做两件事:一是通过异或解密覆盖sub_4010E0;二是nop掉赋值语句
3.1、
异或解密覆盖sub_4010E0
使用IDC代码处理
static main(void) { auto fp, begin, end, dexbyte, sourceAdd; sourceAdd = 0x5647B8; begin = 0x4010E0; end = begin + 0x330; for ( dexbyte = begin; dexbyte < end; dexbyte ++ , sourceAdd ++ ) { PatchByte(dexbyte,Byte(sourceAdd) ^ 0xAB); } }
3.2、 nop掉赋值语句
.text:00401F8A F3 A5 ===> 90 90//也可以将
text:00401F80-
text:00401F8B处均nop
.text:00401F48 8B 1D 10 D2 51 00 mov ebx, ds:VirtualProtect .text:00401F4E 8D 45 FC lea eax, [ebp+flOldProtect] .text:00401F51 50 push eax ; lpflOldProtect .text:00401F52 6A 40 push 40h ; flNewProtect .text:00401F54 68 17 0D 00 00 push 0D17h ; dwSize .text:00401F59 68 E0 10 40 00 push offset sub_4010E0 ; lpAddress .text:00401F5E C7 45 F0 40 00 00 00 mov [ebp+var_10], 40h .text:00401F65 C7 45 FC 00 00 00 00 mov [ebp+flOldProtect], 0 .text:00401F6C FF D3 call ebx ; VirtualProtect .text:00401F6E FF 15 08 D4 51 00 call ds:GetLastError .text:00401F74 85 C0 test eax, eax .text:00401F76 75 62 jnz short loc_401FDA .text:00401F78 8B 55 FC mov edx, [ebp+flOldProtect] .text:00401F7B B9 CC 00 00 00 mov ecx, 0CCh .text:00401F80 90 nop .text:00401F81 90 nop .text:00401F82 90 nop .text:00401F83 90 nop .text:00401F84 90 nop .text:00401F85 90 nop .text:00401F86 90 nop .text:00401F87 90 nop .text:00401F88 90 nop .text:00401F89 90 nop .text:00401F8A 90 nop .text:00401F8B 90 nop .text:00401F8C 8D 4D F0 lea ecx, [ebp+var_10] .text:00401F8F 51 push ecx ; lpflOldProtect .text:00401F90 52 push edx ; flNewProtect .text:00401F91 68 17 0D 00 00 push 0D17h ; dwSize .text:00401F96 68 E0 10 40 00 push offset sub_4010E0 ; lpAddress
3.3、保存
IDA:Edit→Patch Program→Assembly
4、静态分析
sub_4010E0
.rdata:005456D8 db 11h .rdata:005456D9 db 1 .rdata:005456DA db 0 .rdata:005456DB db 0 .rdata:005456DC db 0 .rdata:005456DD db 0 .rdata:005456DE db 0 .rdata:005456DF db 0 .rdata:005456E0 db 0EAh .rdata:005456E1 db 3 .rdata:005456E2 db 0 .rdata:005456E3 db 0 .rdata:005456E4 db 0EAh .rdata:005456E5 db 3 .rdata:005456E6 db 0 .rdata:005456E7 db 0 .rdata:005456E8 db 39h ; 9 .rdata:005456E9 db 0 .rdata:005456EA db 0 .rdata:005456EB db 0 .rdata:005456EC dd offset sub_402000
在 .rdata:005456D8 按 Art +Q键盘,选择AFX_MSGMAP_ENTRY,
最后,形成的结构如下所述
.rdata:00545690 stru_545690 AFX_MSGMAP_ENTRY <0Fh, 0, 0, 0, 13h, offset sub_401DE0> .rdata:00545690 ; DATA XREF: .rdata:stru_545708↓o .rdata:005456A8 AFX_MSGMAP_ENTRY <37h, 0, 0, 0, 28h, offset sub_401E90> .rdata:005456C0 AFX_MSGMAP_ENTRY <111h, 0, 1, 1, 39h, offset sub_401EA0> .rdata:005456D8 AFX_MSGMAP_ENTRY <111h, 0, 3EAh, 3EAh, 39h, offset sub_402000> .rdata:005456F0 AFX_MSGMAP_ENTRY <0> .rdata:00545708 stru_545708 AFX_MSGMAP <offset sub_4055EC, offset stru_545690>
然后,我们比对一下刚才在
Resource_Hacker查找的Dialog内容,很容易定位到button的ID为1 ,即check对应的是.rdata:005456C0 AFX_MSGMAP_ENTRY <111h, 0, 1, 1, 39h, offset sub_401EA0>
,然后我们双击
offset sub_401EA0 进入到该方法,按F5,很容易判断即为button check的处理方法。
int __thiscall btnCheck(CWnd *this) { CWnd *v1; // esi int i; // eax WCHAR inputStr; // [esp+Ch] [ebp-310h] char v5; // [esp+Eh] [ebp-30Eh] char newInputStr; // [esp+20Ch] [ebp-110h] char v7; // [esp+20Dh] [ebp-10Fh] DWORD v8; // [esp+30Ch] [ebp-10h] CWnd *v9; // [esp+310h] [ebp-Ch] int v10; // [esp+314h] [ebp-8h] DWORD flOldProtect; // [esp+318h] [ebp-4h] v1 = this; v9 = this; inputStr = 0; memset(&v5, 0, 0x1FEu); newInputStr = 0; memset(&v7, 0, 0xFFu); CWnd::GetDlgItemTextW(v1, 1000, &inputStr, 20); if ( wcslen(&inputStr) == 16 ) // 输入字符串长度:16 { i = 0; while ( !(*(&inputStr + i) & 0xFF00) ) // 字符应该是 0001 至 00FF;不能为 FF00 和 0100 的宽字符 { *(&newInputStr + i) = *((_BYTE *)&inputStr + 2 * i); if ( ++i >= 16 ) { v8 = 0x40; flOldProtect = 0; VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);// 0x40:PAGE_EXECUTE_READWRITE:内存地址、长度、访问、保存旧的 if ( GetLastError() ) return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); qmemcpy(sub_4010E0, byte_5647B8, 0x330u); VirtualProtect(sub_4010E0, 0xD17u, flOldProtect, &v8); if ( !GetLastError() ) { v10 = 0; v10 = sub_4010E0(&newInputStr); if ( v10 == 1 ) return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0); } v1 = v9; return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); } } } return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); }
2、分析主函数
sub_401EA0
里面有一个VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);这里有修改
qmemcpy(sub_4010E0, byte_5647B8, 0x330u);
很明显,它将
byte_5647B8内容覆盖了
sub_4010E0,继续跟进
byte_5647B8
.data:005647B8 ; char byte_5647B8[816] .data:005647B8 2A byte_5647B8 db 2Ah ; DATA XREF: btnCheck+E0↑o .data:005647B9 47 db 47h ; G .data:005647BA 3B db 3Bh ; ; .data:005647BB AB db 0ABh .data:005647BC AB db 0ABh .data:005647BD AB db 0ABh .data:005647BE FD db 0FDh .data:005647BF 1B db 1Bh .data:005647C0 2A db 2Ah ; * .data:005647C1 FC db 0FCh .data:005647C2 20 db 20h .data:005647C3 17 db 17h
.data:005647B8 2A byte_5647B8 db 2Ah ; DATA XREF: sub_401D80:loc_401DC0↑w .data:005647B9 47 db 47h ; G .data:005647BA 3B db 3Bh ; ; .data:005647BB AB db 0ABh .data:005647BC AB db 0ABh .data:005647BD AB db 0ABh .data:005647BE FD db 0FDh .data:005647BF 1B db 1Bh .data:005647C0 2A db 2Ah ; * .data:005647C1 FC db 0FCh .data:005647C2 20 db 20h .data:005647C3 17 db 17h
很明显,这个不是代码,我们看看有哪些代码访问了它,按x,找到条目(我在原代码做了Patch,其实该代码处有两处引用),一处是check处理;一处是修改
byte_5647B8,我们跟进sub_401D80,查看
signed int __thiscall sub_401D80(CDialog *this) { CDialog *v1; // esi unsigned int v2; // eax v1 = this; CDialog::OnInitDialog(this); SendMessageW(*((HWND *)v1 + 8), 0x80u, 1u, *((_DWORD *)v1 + 29)); SendMessageW(*((HWND *)v1 + 8), 0x80u, 0, *((_DWORD *)v1 + 29)); v2 = 0; do { byte_5647B8[v2] ^= 0xABu; ++v2; } while ( v2 < 0x330 ); return 1; }
它相当于对
byte_5647B8做了异或处理后又保存了,相当于原来进行了加密,现在进行了解密
3、Patch代码:以使其更容易静态分析
我们做两件事:一是通过异或解密覆盖sub_4010E0;二是nop掉赋值语句
3.1、
异或解密覆盖sub_4010E0
使用IDC代码处理
static main(void) { auto fp, begin, end, dexbyte, sourceAdd; sourceAdd = 0x5647B8; begin = 0x4010E0; end = begin + 0x330; for ( dexbyte = begin; dexbyte < end; dexbyte ++ , sourceAdd ++ ) { PatchByte(dexbyte,Byte(sourceAdd) ^ 0xAB); } }
3.2、 nop掉赋值语句
.text:00401F8A F3 A5 ===> 90 90//也可以将
text:00401F80-
text:00401F8B处均nop
.text:00401F48 8B 1D 10 D2 51 00 mov ebx, ds:VirtualProtect .text:00401F4E 8D 45 FC lea eax, [ebp+flOldProtect] .text:00401F51 50 push eax ; lpflOldProtect .text:00401F52 6A 40 push 40h ; flNewProtect .text:00401F54 68 17 0D 00 00 push 0D17h ; dwSize .text:00401F59 68 E0 10 40 00 push offset sub_4010E0 ; lpAddress .text:00401F5E C7 45 F0 40 00 00 00 mov [ebp+var_10], 40h .text:00401F65 C7 45 FC 00 00 00 00 mov [ebp+flOldProtect], 0 .text:00401F6C FF D3 call ebx ; VirtualProtect .text:00401F6E FF 15 08 D4 51 00 call ds:GetLastError .text:00401F74 85 C0 test eax, eax .text:00401F76 75 62 jnz short loc_401FDA .text:00401F78 8B 55 FC mov edx, [ebp+flOldProtect] .text:00401F7B B9 CC 00 00 00 mov ecx, 0CCh .text:00401F80 90 nop .text:00401F81 90 nop .text:00401F82 90 nop .text:00401F83 90 nop .text:00401F84 90 nop .text:00401F85 90 nop .text:00401F86 90 nop .text:00401F87 90 nop .text:00401F88 90 nop .text:00401F89 90 nop .text:00401F8A 90 nop .text:00401F8B 90 nop .text:00401F8C 8D 4D F0 lea ecx, [ebp+var_10] .text:00401F8F 51 push ecx ; lpflOldProtect .text:00401F90 52 push edx ; flNewProtect .text:00401F91 68 17 0D 00 00 push 0D17h ; dwSize .text:00401F96 68 E0 10 40 00 push offset sub_4010E0 ; lpAddress
3.3、保存
IDA:Edit→Patch Program→Assembly
4、静态分析
sub_4010E0
.rdata:00545690 stru_545690 AFX_MSGMAP_ENTRY <0Fh, 0, 0, 0, 13h, offset sub_401DE0> .rdata:00545690 ; DATA XREF: .rdata:stru_545708↓o .rdata:005456A8 AFX_MSGMAP_ENTRY <37h, 0, 0, 0, 28h, offset sub_401E90> .rdata:005456C0 AFX_MSGMAP_ENTRY <111h, 0, 1, 1, 39h, offset sub_401EA0> .rdata:005456D8 AFX_MSGMAP_ENTRY <111h, 0, 3EAh, 3EAh, 39h, offset sub_402000> .rdata:005456F0 AFX_MSGMAP_ENTRY <0> .rdata:00545708 stru_545708 AFX_MSGMAP <offset sub_4055EC, offset stru_545690>
然后,我们比对一下刚才在
Resource_Hacker查找的Dialog内容,很容易定位到button的ID为1 ,即check对应的是.rdata:005456C0 AFX_MSGMAP_ENTRY <111h, 0, 1, 1, 39h, offset sub_401EA0>
,然后我们双击
offset sub_401EA0 进入到该方法,按F5,很容易判断即为button check的处理方法。
int __thiscall btnCheck(CWnd *this) { CWnd *v1; // esi int i; // eax WCHAR inputStr; // [esp+Ch] [ebp-310h] char v5; // [esp+Eh] [ebp-30Eh] char newInputStr; // [esp+20Ch] [ebp-110h] char v7; // [esp+20Dh] [ebp-10Fh] DWORD v8; // [esp+30Ch] [ebp-10h] CWnd *v9; // [esp+310h] [ebp-Ch] int v10; // [esp+314h] [ebp-8h] DWORD flOldProtect; // [esp+318h] [ebp-4h] v1 = this; v9 = this; inputStr = 0; memset(&v5, 0, 0x1FEu); newInputStr = 0; memset(&v7, 0, 0xFFu); CWnd::GetDlgItemTextW(v1, 1000, &inputStr, 20); if ( wcslen(&inputStr) == 16 ) // 输入字符串长度:16 { i = 0; while ( !(*(&inputStr + i) & 0xFF00) ) // 字符应该是 0001 至 00FF;不能为 FF00 和 0100 的宽字符 { *(&newInputStr + i) = *((_BYTE *)&inputStr + 2 * i); if ( ++i >= 16 ) { v8 = 0x40; flOldProtect = 0; VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);// 0x40:PAGE_EXECUTE_READWRITE:内存地址、长度、访问、保存旧的 if ( GetLastError() ) return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); qmemcpy(sub_4010E0, byte_5647B8, 0x330u); VirtualProtect(sub_4010E0, 0xD17u, flOldProtect, &v8); if ( !GetLastError() ) { v10 = 0; v10 = sub_4010E0(&newInputStr); if ( v10 == 1 ) return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0); } v1 = v9; return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); } } } return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); }
2、分析主函数
sub_401EA0
int __thiscall btnCheck(CWnd *this) { CWnd *v1; // esi int i; // eax WCHAR inputStr; // [esp+Ch] [ebp-310h] char v5; // [esp+Eh] [ebp-30Eh] char newInputStr; // [esp+20Ch] [ebp-110h] char v7; // [esp+20Dh] [ebp-10Fh] DWORD v8; // [esp+30Ch] [ebp-10h] CWnd *v9; // [esp+310h] [ebp-Ch] int v10; // [esp+314h] [ebp-8h] DWORD flOldProtect; // [esp+318h] [ebp-4h] v1 = this; v9 = this; inputStr = 0; memset(&v5, 0, 0x1FEu); newInputStr = 0; memset(&v7, 0, 0xFFu); CWnd::GetDlgItemTextW(v1, 1000, &inputStr, 20); if ( wcslen(&inputStr) == 16 ) // 输入字符串长度:16 { i = 0; while ( !(*(&inputStr + i) & 0xFF00) ) // 字符应该是 0001 至 00FF;不能为 FF00 和 0100 的宽字符 { *(&newInputStr + i) = *((_BYTE *)&inputStr + 2 * i); if ( ++i >= 16 ) { v8 = 0x40; flOldProtect = 0; VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);// 0x40:PAGE_EXECUTE_READWRITE:内存地址、长度、访问、保存旧的 if ( GetLastError() ) return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); qmemcpy(sub_4010E0, byte_5647B8, 0x330u); VirtualProtect(sub_4010E0, 0xD17u, flOldProtect, &v8); if ( !GetLastError() ) { v10 = 0; v10 = sub_4010E0(&newInputStr); if ( v10 == 1 ) return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0); } v1 = v9; return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); } } } return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); }
2、分析主函数
sub_401EA0
里面有一个VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);这里有修改
qmemcpy(sub_4010E0, byte_5647B8, 0x330u);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2019-6-17 21:49
被htg编辑
,原因: 修正错误
赞赏记录
参与人
雪币
留言
时间
一笑人间万事
为你点赞~
2022-7-27 01:41
心游尘世外
为你点赞~
2022-7-26 23:31
飘零丶
为你点赞~
2022-7-17 03:22
赞赏
他的文章
看原图
赞赏
雪币:
留言: