吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2554|回复: 3
收起左侧

[会员申请] 申请会员 superasd

[复制链接]
吾爱游客  发表于 2021-4-2 15:39
[md]申请会员id    superasd
申请邮箱   250581290@qq.com

技术文章:   VA插件破解

环境:
Win10 x64
VS2019
VisualAssist 插件最新版 2399
cheate engine
IDA pro
x32dbg


先上图为证,是本人作品,并且已经破解成功.
1.jpg
aaa.jpg


下面描述破解过程

之前用老版本的时候是下的别人的破解版,大家都知道是一个VA_X.dll 替换即可, 最近我的VA突然自动更新了,然后失效了,就尝试自己解决一下.

我的现在打开VS就提示一个过期的弹窗, 凭直觉先调试.
1用IDA启动VS, 发现不行,全是异常,由于IDA不能设置全部忽略异常,等VS启动后停在选择项目的界面时,用IDA附加调试,没有问题.  我们观察,进入vs主界面后只有一个VA对话框, 要么取消,要么选择注册. 如果选择取消的话, 这个对话框似乎再也没有地方查看了,只能重新启动vs, 这就很麻烦了, 因为vs非常庞大的软件,启动的话调试器要卡很久, 而且相关状态全没了; 而如果选择注册的话, 我浪费了一些时间去追踪她是如何解析字符串的,这里比较麻烦; 没有什么有用的信息

2 由于我也就是想尽快把va变成能用的状态即可, 并不是追求什么完美破解, 也没打算把注册机都给弄出来, 那么就换个思路, 我们看看这个对话框是怎么弹出来的, 如果你购买了正版, 那么应该是不会弹出对话框的, 所以我有一个大胆的想法, 把对话框直接给干掉是不是行了?  于是把对话框的相关API全部断点, 然后停在了CreateDialogIndirectParamA, 不过这里参数好像看不出什么线索; 创建对话框只需要传入资源id就行, 这些全是二进制, 这个思路又行不通了, 而且我搜索对话框的字符串,也没有得到什么信息.

Address        Module        Function
76711FF7        user32.dll        user32_CreateDialogIndirectParamA+7
1F33694B        VA_X.dll        jpt_1F331B39+0x4896
1F336020        VA_X.dll        jpt_1F331B39+0x3F6B
1F336178        VA_X.dll        jpt_1F331B39+0x40C3
1F3362DE        VA_X.dll        jpt_1F331B39+0x4229
1F2D88EA        VA_X.dll        jpt_1F2D35A8+0x51D6
1F2DF04B        VA_X.dll        jpt_1F2DE20B+0xCAB
1EFD00B0        VA_X.dll        jpt_1EFC81FD+0x7E9C
1F33EBFE        VA_X.dll        jpt_1F331B39+0xCB49
1F51AF3D        VA_X.dll        jpt_1F51991B+0x1602
749D6357        kernel32.dll        kernel32_BaseThreadInitThunk+17
771B8962        ntdll.dll        ntdll_RtlGetAppContainerNamedObjectPath+E2
771B892F        ntdll.dll        ntdll_RtlGetAppContainerNamedObjectPath+AF

4 于是我就只能硬上看代码了,  从这里我就开始了很繁琐的调试体力活,每次改动都要重启一次VS, 我不得不怀疑VA的开发组肯定是故意的, 想破解也要很有耐心才行...
先看第一个函数 也就是 1F33694B 这里, 只有一个很简单的if跳转, 屏蔽之, 再次启动还是弹框, 失败....

这里省略N次调试过程.

void __usercall sub_1F3361C5(int a1@<ebp>)
{
  _DWORD *v1; // ecx
  _DWORD *v2; // edi
  int v3; // ebx
  int v4; // esi
  int v5; // eax
  bool v6; // zf
  int v7; // eax
  int v8; // esi
  int v9; // eax
  int v10; // ebx
  int v11; // esi
  int v12; // eax
  int (__thiscall *v13)(_DWORD); // esi
  int v14; // eax
  int v15; // eax
  void (__thiscall *v16)(_DWORD *); // esi
  int v17; // [esp-20h] [ebp-20h]
  int v18; // [esp-Ch] [ebp-Ch]

  sub_1F47823B(36);
  v2 = v1;
  *(_DWORD *)(a1 - 36) = v1;
  v3 = v1[35];
  v4 = v1[34];
  *(_DWORD *)(a1 - 28) = v3;
  v5 = sub_1F3378ED();
  v6 = v2[33] == 0;
  *(_DWORD *)(a1 - 32) = *(_DWORD *)(v5 + 12);
  if ( !v6 )
  {
    v7 = sub_1F3378ED();
    v18 = v2[33];
    v8 = *(_DWORD *)(v7 + 12);
    *(_DWORD *)(a1 - 32) = v8;
    v9 = ((int (__cdecl *)(int, int, int))unk_1C0533B0)(v8, v18, 5);
    v4 = ((int (__cdecl *)(int, int))kernel32_LoadResource)(v8, v9);
  }
  if ( v4 )
  {

直到我分析到这里, 终于发现了一个有用的函数LoadResource, 我猜应该是用来加载对话框的吧, 我先做一个猜测, 这里函数如果进来的话 说明已经被判定为expire状态了, 所以这里往下全都不用看了.


[C] 纯文本查看 复制代码
int __stdcall sub_1F2D84A0(int a1, char a2)
{
  int (***v2)(); // edi
  int v4; // ebx
  int v5; // ecx
  int v6; // esi
  int v7; // ecx
  void **v8; // eax
  int v9; // eax
  void *v10; // esi
  int v11; // ebx
  int v12; // ecx
  int v13; // eax
  int v14; // esi
  int *v15; // eax
  int v16; // ecx
  int v17; // edx
  int v18; // eax
 

省略大段代码........

      v44 = 10;
      if ( !v45 )
      {
        v37 = ((int (*)(void))kernel32_GetLastError)();
        if ( dword_1F894000 )
          sub_1EF8B880("ERROR running dialog %d(%08x)", v37, v37);
        sub_1EE05370(dword_1F894490, &off_1F6E1158, &off_1F66419C, 16);
      }
    }
    if ( v44 == 10 )
    {
      if ( a2 )
      {
        sub_1ED9AF70(&v57);
        LOBYTE(v61) = 22;
        sub_1EDA0130(&v57, &off_1F6E1C40, v58);
        sub_1EDD7850(dword_1F894490, &v57, "Good-thru Date Expired", 0);

  省略.........

  return v14;
}



看到了又一个非常有用的字符串"ERROR running dialog",  好家伙, 所以之前的尝试全是错的. 说明开始就钻牛角尖了, 浪费大把时间.  我也是没想到VA弹个框掉了如此多层函数, 真的怕不是故意的混淆视线..

5  既然如此, 我们反过来看,  从系统dll第一次进入va模块开始找

int sub_1EFD0070()
{
  _DWORD v1[17]; // [esp-2Ch] [ebp-44h] BYREF

  ((void (__stdcall *)(_DWORD, int))combase_CoInitializeEx)(0, 2);
  if ( off_1F84BD48 && ((int (__thiscall *)(int (***)(), void ****))(*off_1F84BD48)[7])(off_1F84BD48, &off_1F84A9B4) )
  {
    v1[10] = 1;
    v1[13] = v1;
    v1[12] = v1;
    v1[9] = v1;
    v1[16] = -1;
    ((void (__cdecl *)(char, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD))sub_1F0737E0)(
      (char)&std::_Func_impl_no_alloc<_lambda_d57e0b89b7985f042def112d34043b4f_,void,>::`vftable',
      v1[1],
      v1[2],
      v1[3],
      v1[4],
      v1[5],
      v1[6],
      v1[7],
      v1[8]);
  }
  else
  {
    ((void (__thiscall *)(void ****))loc_1EFB5590)(&off_1F84A9B4);
  }
  ((void (*)(void))combase_CoUninitialize)();
  return 0;
}

这里有两个明显的api, 之前做过com编程,记得这是个初始化入口, 我们来尝试一下, 如果把这里整个bypass的话, 效果如何? 果然启动vs后没有任何提示, va菜单也是黑色; 果然是压根没加载


最终我们发现了一个超长函数,

int __thiscall sub_1F2DED30(int *this, int a2)
{
  void *v3; // ecx
  int (*v5)(void); // eax
  int v6; // esi
  int v7; // eax
  bool v8; // zf
  int v9; // eax
  int v10; // eax
  int v11; // eax
  int v12; // esi
  _DWORD *v13; // eax
  int v14; // edx
  int *v15; // eax
  int *v16; // ecx
  int *v17; // eax
  _DWORD *v18; // eax
  int v19; // esi
  _DWORD *v20; // eax
  int v21; // esi
  _DWORD *v22; // eax
  int v23; // esi
  int v24; // eax
  int v25; // [esp+10h] [ebp-60h] BYREF
  int v26; // [esp+14h] [ebp-5Ch] BYREF
  int v27; // [esp+18h] [ebp-58h] BYREF
  int v28; // [esp+1Ch] [ebp-54h]
  int v29; // [esp+20h] [ebp-50h] BYREF
  int v30; // [esp+24h] [ebp-4Ch]
  int v31; // [esp+28h] [ebp-48h] BYREF
  bool v32; // [esp+2Eh] [ebp-42h]
  bool v33; // [esp+2Fh] [ebp-41h]
  int v34[5]; // [esp+30h] [ebp-40h] BYREF
  unsigned int v35; // [esp+44h] [ebp-2Ch]
  int v36[5]; // [esp+48h] [ebp-28h] BYREF
  unsigned int v37; // [esp+5Ch] [ebp-14h]
  int v38; // [esp+6Ch] [ebp-4h]

  sub_1F2E0D90();
  if ( !off_1F8DFA28 )
  {
    if ( dword_1F894000 )
      sub_1EF8B880("ERROR: vaaux failed to load 1");
    goto LABEL_4;
  }
  v5 = (int (*)(void))sub_1EF14540("GetAuxDll");
  if ( !v5 )
  {
LABEL_4:
    v3 = off_1F8DFA04;
    goto LABEL_5;
  }
  v3 = (void *)v5();
  off_1F8DFA04 = v3;
LABEL_5:
  if ( !v3 )
  {
    if ( dword_1F894000 )
      sub_1EF8B880("ERROR: vaaux failed to load 2");
    return 0;
  }
  (*(void (__thiscall **)(void *, int (***)()))(*(_DWORD *)v3 + 4))(v3, &off_1F84BD50);
  sub_1F2E0C40(&v29, (int)"DAYSINSTALLED");
  v38 = 0;
  if ( *(_DWORD *)(v29 - 12) )
  {
    if ( *(_DWORD *)(sub_1F3378ED() + 12) )
      v7 = *(_DWORD *)(sub_1F3378ED() + 12);
    else
      v7 = *(_DWORD *)(sub_1F3378ED() + 8);
    sub_1F2EB310(v7);
    (*(void (__thiscall **)(int, int *, int *, int *))(*(_DWORD *)a2 + 12))(a2, &v25, &v26, &v27);
    sub_1F2EB2E0(v25, v26, v27);
    v8 = (*(int (__thiscall **)(int *))(*this + 56))(this) == 0;
    v9 = *this;
    LOBYTE(v30) = !v8;
    v10 = (*(int (__thiscall **)(int *, _DWORD))(v9 + 24))(this, 0);
    v32 = v10 != 0;
    if ( v10 )
      v11 = (*(int (__thiscall **)(int *))(*this + 36))(this);
    else
      v11 = sub_1F2EB800();
    v12 = v11;
    v13 = sub_1F2E0C40(&v31, (int)"EXPIRED");
    LOBYTE(v38) = 1;
    v28 = *(_DWORD *)(*v13 - 12);
    v33 = v28 > 0;
    LOBYTE(v38) = 0;
    v14 = v31 - 16;
    if ( _InterlockedDecrement((volatile signed __int32 *)(v31 - 16 + 12)) <= 0 )
      (*(void (__stdcall **)(int))(**(_DWORD **)v14 + 4))(v14);
    if ( v12 != 1 )
    {
LABEL_37:
      if ( v12 == 6 || v12 == 4 )
        goto LABEL_48;
      if ( v12 == 2 )
      {
LABEL_42:
        if ( !(_BYTE)v30 && (sub_1F51734E(v29) > 29 || v12 == 2) )
        {
          sub_1F2E6610("TrialLicense", "BD7D-A4CE-A835-1421-73DB-9D7F-603C-A3FB-91D2-A64F-BB7D-B26B");
          if ( v12 == 2 && v32 )
          {
LABEL_49:
            if ( sub_1F2E1D40() || sub_1F2E1A20() )
            {
              byte_1F84A964 = 1;
              ((void (__thiscall *)(int (***)()))(*off_1F84BD48)[18])(off_1F84BD48);
              if ( (*(int (__thiscall **)(int *, int))(*this + 24))(this, 1) )
                goto LABEL_68;
              v12 = sub_1F2EB800();
            }
            if ( v12 )
            {
              if ( !(*(int (__thiscall **)(int, int, int))(*(_DWORD *)a2 + 4))(a2, v12, v30) )
              {
                if ( v12 == 2 )
                {
                  sub_1F0104F0();
                  v6 = 0;
                  goto LABEL_70;
                }
                if ( v33 )
                {
                  sub_1F010580();
                  v6 = 0;
                  goto LABEL_70;
                }
LABEL_67:
                v6 = 0;
                goto LABEL_70;
              }
              if ( !(*(int (__thiscall **)(int *, int))(*this + 24))(this, 1) && sub_1F2EB800() )
              {
                v18 = sub_1F2E0C40(&v31, (int)"CLOCKBACK");
                LOBYTE(v38) = 5;
                v19 = *(_DWORD *)(*v18 - 12);
                LOBYTE(v38) = 0;
                sub_1ED984C0(&v31);
                if ( v19 )
                {
                  sub_1F0102B0();
                  (*(void (__thiscall **)(int, void ****, _DWORD))(*(_DWORD *)a2 + 8))(a2, &off_1F6E3A68, 0);
                  v6 = 0;
                  goto LABEL_70;
                }
                v20 = sub_1F2E0C40(&v31, (int)"CLOCKFORWARD");
                LOBYTE(v38) = 6;
                v21 = *(_DWORD *)(*v20 - 12);
                LOBYTE(v38) = 0;
                sub_1ED984C0(&v31);
                if ( v21 )
                {
                  sub_1F010340();
                  (*(void (__thiscall **)(int, void ****, _DWORD))(*(_DWORD *)a2 + 8))(a2, &off_1F6E3B28, 0);
                  v6 = 0;
                  goto LABEL_70;
                }
                v22 = sub_1F2E0C40(&v31, (int)"EXPIRED");
                LOBYTE(v38) = 7;
                v23 = *(_DWORD *)(*v22 - 12);
                LOBYTE(v38) = 0;
                sub_1ED984C0(&v31);
                if ( v23 )
                {
                  sub_1F0103D0();
                  (*(void (__thiscall **)(int, int (__stdcall ***)(int, int, int, int, int), _DWORD))(*(_DWORD *)a2 + 8))(
                    a2,
                    &off_1F6E3BE8,
                    0);
                  goto LABEL_67;
                }
LABEL_69:
                v6 = 1;
                goto LABEL_70;
              }
            }
LABEL_68:
            (*(void (__thiscall **)(int))(*(_DWORD *)a2 + 16))(a2);
            goto LABEL_69;
          }
          v12 = sub_1F2EB800();
        }
LABEL_48:
        if ( !v12 )
          goto LABEL_68;
        goto LABEL_49;
      }
LABEL_40:
      if ( !v33 )
        goto LABEL_48;
      if ( !v12 )
        goto LABEL_68;
      goto LABEL_42;
    }
    if ( (_BYTE)v30 )
      goto LABEL_40;
    v36[4] = 0;
    v37 = 15;
    LOBYTE(v36[0]) = 0;
    v34[4] = 0;
    v35 = 15;
    LOBYTE(v34[0]) = 0;
    LOBYTE(v38) = 4;
    if ( sub_1F2EB110((int)v36, (int)v34) )
    {
      if ( v28 > 0 )
      {
LABEL_30:
        v16 = v34;
        v17 = v36;
        if ( v35 >= 0x10 )
          v16 = (int *)v34[0];
        if ( v37 >= 0x10 )
          v17 = (int *)v36[0];
        if ( !sub_1F2EA460(v17, v16) )
          v12 = sub_1F2EB800();
        goto LABEL_36;
      }
      v15 = v36;
      if ( v37 >= 0x10 )
        v15 = (int *)v36[0];
      if ( (unsigned __int8)sub_1F2EAF50(v15) )
      {
        v33 = 1;
        goto LABEL_30;
      }
      if ( v28 > 0 )
        goto LABEL_30;
    }
LABEL_36:
    LOBYTE(v38) = 3;
    sub_1ED87DE0(v34);
    LOBYTE(v38) = 0;
    sub_1ED87DE0(v36);
    goto LABEL_37;
  }
  sub_1F010460();
  v6 = 0;
LABEL_70:
  v38 = -1;
  v24 = v29 - 16;
  if ( _InterlockedDecrement((volatile signed __int32 *)(v29 - 16 + 12)) <= 0 )
    (*(void (__stdcall **)(int))(**(_DWORD **)v24 + 4))(v24);
  return v6;
}

先吐槽一下ida, 虽然是输出了伪c代码, 这跳转满天飞看的实在是糟糕....

7 在85行看到了明显的expire, trial license , 然后是clockback clockforward, 不太清楚具体目的,在这里开始做体力活了,找到关键跳转;

8 由于代码很长,需要排除一些错误的地方,由于我们现在就是过期状态; 很好办,让ida在这里单步走一遍,剩下的没走到的函数那就是重点了;

9  直接说结论;  修改92行的v12变量为0就行了;

10 验证过程:
vs2019启动, 停在选择界面; 打开任意调试器或者cheate engine, 定位 1f2dee79, 在这里我们把esi置0, 为了字节对齐, 修改为xor esi,esi; 选择项目打开主界面,果然成功!

总结部分:

va 肯定是用了什么壳, 有一定反调试, 检测api是否被hook, 启动时自校验 等等功能, 只不过没有vmp那些很明显.  
结合开头说的,
ida ,  不能直接启动,否则全是异常, 只能附加调试, 但是ida分析又慢的要死, 如果先分析保存文件下次复用又会莫名其妙无响应或者崩溃. 而且有时候断点久了 vs自动退出, 还不知道怎么回事
x32dbg, 可以直接启动调试, 比较快, 可惜没有反编译插件
OllyDbg, 直接启动可以... 不过卡的要死, 放弃
cheate engine ,虽然原本是个游戏修改器, 不过作为小工具非常好用, 附加超快, 改内存查数据比调试器好用, 自带简易调试器功能


本着能用就行的原则, 就自行编写一个dll 来启动的时候自动修改字节;

dll 不上传了, 放代码

void* addr = (void*)(va + 0x5dee79);
    DWORD old;
    if (!VirtualProtect(addr, 2, PAGE_EXECUTE_READWRITE, &old))
    {
        err("vp fail");
        return;
    }

    uint16_t* p = (uint16_t*)addr;
    if (*p != 0xf08b)
    {
        return;
    }
    *p = 0xf631;

    if (!VirtualProtect(addr, 2, old, &old))
    {
        err("vp fail");
        return;
    }

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2021-4-8 10:28
我记得vax是穿山甲的壳,现在还是吗?这么简单的爆破可能不太行?记得有暗桩好像。
吾爱游客  发表于 2021-4-10 14:24
va的版本 和修改位置写的清清楚楚, 不如你花2分钟验证一下.

可能是太长了, 我再贴一下 :  vs2019启动, 停在选择界面; 打开任意调试器,内存修改器 ,cheate engine都可以, 定位 1f2dee79, 在这里我们把esi置0, 为了字节对齐, 修改为xor esi,esi; 选择项目打开主界面,果然成功!


这是我第一次破解VA, 我不知道什么穿山甲, 我觉得只要了解汇编运行本质, 什么壳都是一样的破解, 叫什么名字我不关心.

吾爱游客  发表于 2021-4-16 21:05
Hmily 发表于 2021-4-8 10:28
我记得vax是穿山甲的壳,现在还是吗?这么简单的爆破可能不太行?记得有暗桩好像。

用户名 林小呆

绑定的q号 1737596421

几周不上线,被无故丰号,请说明理由,不然就给我注销帐号,清除我绑定的各人信息!别留着我的各人信息!


https://www.52pojie.cn/?1328093
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-25 18:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表