Araxis Merge 2023.5848漏洞分析
这个app使用MFC制作,未加密。所以直接使用x64dbg或者idapro都可以直接调试。在idapro中可以直接在CDialog::DoModal中下断点,当未注册版本启动时,第一个界面就是注册对话框。因此这是最佳切入点。
在调用堆栈中可以轻松找到检查注册状态的代码:
__int64 __fastcall sub_1401D3AD0(int *a1) { int v2; // ebx __int64 v3; // rax unsigned __int8 v4; // si unsigned __int64 v5; // rdx __int64 v6; // rcx unsigned __int64 v7; // rdx __int64 v8; // rcx unsigned __int64 v9; // rdx __int64 v10; // rcx __int64 v11; // rbx int v13; // [rsp+30h] [rbp-29h] BYREF __int128 v14; // [rsp+38h] [rbp-21h] BYREF __int128 v15; // [rsp+48h] [rbp-11h] BYREF __m128i v16; // [rsp+58h] [rbp-1h] __int128 v17; // [rsp+68h] [rbp+Fh] BYREF __m128i v18; // [rsp+78h] [rbp+1Fh] __int128 v19; // [rsp+88h] [rbp+2Fh] BYREF __m128i si128; // [rsp+98h] [rbp+3Fh] v14 = 0i64; v13 = 0; v19 = 0i64; si128 = _mm_load_si128((const __m128i *)&xmmword_14079DBA0); LOWORD(v19) = 0; v17 = 0i64; v18 = si128; LOWORD(v17) = 0; v15 = 0i64; v16 = si128; LOWORD(v15) = 0; sub_1401D3540(&v14, &v13, &v19, &v17, &v15); // v13保存的就是注册状态,调试这个函数就可以得到你想要的东西 v2 = v13; switch ( v13 ) { case 2: case 3: goto LABEL_3; case 4: v3 = (*(__int64 (__fastcall **)(_QWORD))(*(_QWORD *)v14 + 24i64))(v14); sub_1401D3120(&qword_1408B3E80, v3); LABEL_3: *a1 = v2; v4 = 1; break; default: v4 = sub_1401D6B20((__int64)a1); break; } if ( v16.m128i_i64[1] >= 8ui64 ) { v5 = 2 * v16.m128i_i64[1] + 2; v6 = v15; if ( v5 >= 0x1000 ) { v5 = 2 * v16.m128i_i64[1] + 41; v6 = *(_QWORD *)(v15 - 8); if ( (unsigned __int64)(v15 - v6 - 8) > 0x1F ) invalid_parameter_noinfo_noreturn(v6, v5); } j_j_free(v6, v5); } if ( v18.m128i_i64[1] >= 8ui64 ) { v7 = 2 * v18.m128i_i64[1] + 2; v8 = v17; if ( v7 >= 0x1000 ) { v7 = 2 * v18.m128i_i64[1] + 41; v8 = *(_QWORD *)(v17 - 8); if ( (unsigned __int64)(v17 - v8 - 8) > 0x1F ) invalid_parameter_noinfo_noreturn(v8, v7); } j_j_free(v8, v7); } if ( si128.m128i_i64[1] >= 8ui64 ) { v9 = 2 * si128.m128i_i64[1] + 2; v10 = v19; if ( v9 >= 0x1000 ) { v9 = 2 * si128.m128i_i64[1] + 41; v10 = *(_QWORD *)(v19 - 8); if ( (unsigned __int64)(v19 - v10 - 8) > 0x1F ) invalid_parameter_noinfo_noreturn(v10, v9); } j_j_free(v10, v9); } if ( *((_QWORD *)&v14 + 1) ) { if ( _InterlockedExchangeAdd((volatile signed __int32 *)(*((_QWORD *)&v14 + 1) + 8i64), 0xFFFFFFFF) == 1 ) { v11 = *((_QWORD *)&v14 + 1); (***((void (__fastcall ****)(_QWORD))&v14 + 1))(*((_QWORD *)&v14 + 1)); if ( _InterlockedExchangeAdd((volatile signed __int32 *)(v11 + 12), 0xFFFFFFFF) == 1 ) (*(void (__fastcall **)(_QWORD))(**((_QWORD **)&v14 + 1) + 8i64))(*((_QWORD *)&v14 + 1)); } } return v4; }
.text:00000001401D384D loc_1401D384D: ; CODE XREF: sub_1401D3540+308↑j .text:00000001401D384D 48 8D 7B 10 lea rdi, [rbx+10h] .text:00000001401D3851 48 89 7C 24 40 mov [rsp+180h+var_140], rdi .text:00000001401D3856 48 89 5C 24 48 mov [rsp+180h+var_138], rbx .text:00000001401D385B 48 8B 07 mov rax, [rdi] .text:00000001401D385E 48 8B CF mov rcx, rdi .text:00000001401D3861 ; try { .text:00000001401D3861 FF 50 08 call qword ptr [rax+8] // 跟进去,就是那个代码 .text:00000001401D3861 .text:00000001401D3864 48 63 C8 movsxd rcx, eax .text:00000001401D3867 48 8D 15 32 47 59 00 lea rdx, dword_140767FA0 .text:00000001401D386E 8B 14 8A mov edx, [rdx+rcx*4] .text:00000001401D3871 3B 54 24 20 cmp edx, [rsp+180h+var_160] .text:00000001401D3875 0F 8E C5 00 00 00 jle loc_1401D3940
找到目标函数后,直接将其始终返回2或3即可。
>merge.exe 0000000000274400:48->B8 0000000000274401:89->03 0000000000274402:5C->00 0000000000274403:24->00 0000000000274404:10->00 0000000000274405:48->C3 0000000000274406:89->90 0000000000274407:6C->90 0000000000274408:24->90 0000000000274409:18->90
接下来就可以使用现有的序列号注册了,本身序列号还有自己校验算法,且到期时间也打包在序列号中,搞定这个就可以自己算序列号了,当然也可以用现存的,比如:
llllllDB:llINllll:llBOBOOl:lllBOlll:llBOBOOl:lllBDlll
最后,app的注册信息存放在注册表中,所以如果你想搞一个patcher,那么需要做两件事:
1. 通过标志代码找到目标函数并直接patch二进制文件
2.将注册信息写入到注册表中:
// hive is RegistryHive.LocalMachine and RegistryHive.CurrentUser
var root = RegistryKey.OpenBaseKey(hive, RegistryView.Registry64); var key = root.OpenSubKey("Software\\Araxis\\Merge\\7.1", true); if(key == null) { key = root.CreateSubKey("Software\\Araxis\\Merge\\7.1", true); } key.SetValue("LicensedUser", Environment.UserName); key.SetValue("SerialNumber", licenseKey); key.SetValue("CompanyName", "your info...");