发表于 2021-4-2 15:39

申请会员 superasd

申请会员id    superasd
申请邮箱   250581290@qq.com

技术文章:   VA插件破解

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


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




下面描述破解过程

之前用老版本的时候是下的别人的破解版,大家都知道是一个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; //
int v18; //

sub_1F47823B(36);
v2 = v1;
*(_DWORD *)(a1 - 36) = v1;
v3 = v1;
v4 = v1;
*(_DWORD *)(a1 - 28) = v3;
v5 = sub_1F3378ED();
v6 = v2 == 0;
*(_DWORD *)(a1 - 32) = *(_DWORD *)(v5 + 12);
if ( !v6 )
{
    v7 = sub_1F3378ED();
    v18 = v2;
    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状态了, 所以这里往下全都不用看了.


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; // BYREF

((void (__stdcall *)(_DWORD, int))combase_CoInitializeEx)(0, 2);
if ( off_1F84BD48 && ((int (__thiscall *)(int (***)(), void ****))(*off_1F84BD48))(off_1F84BD48, &off_1F84A9B4) )
{
    v1 = 1;
    v1 = v1;
    v1 = v1;
    v1 = v1;
    v1 = -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,
      v1,
      v1,
      v1,
      v1,
      v1,
      v1,
      v1);
}
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; // BYREF
int v26; // BYREF
int v27; // BYREF
int v28; //
int v29; // BYREF
int v30; //
int v31; // BYREF
bool v32; //
bool v33; //
int v34; // BYREF
unsigned int v35; //
int v36; // BYREF
unsigned int v37; //
int v38; //

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))(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 = 0;
    v37 = 15;
    LOBYTE(v36) = 0;
    v34 = 0;
    v35 = 15;
    LOBYTE(v34) = 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;
      if ( v37 >= 0x10 )
          v17 = (int *)v36;
      if ( !sub_1F2EA460(v17, v16) )
          v12 = sub_1F2EB800();
      goto LABEL_36;
      }
      v15 = v36;
      if ( v37 >= 0x10 )
      v15 = (int *)v36;
      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
页: [1]
查看完整版本: 申请会员 superasd