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...");

 

posted @ 2023-04-17 10:01  bodong  阅读(192)  评论(0编辑  收藏  举报