Native安全之破解与补丁

Native安全之破解与补丁

Native程序

Native程序指能被计算机系统直接加载并由CPU执行的程序。其文件格式常见有Windows的PE(如.exe, .dll)、Linux的ELF、macOS的Mach-O。这些文件包含特定处理器指令集(如x86, amd64, arm, arm64)的机器码, 由CPU直接执行这些指令。C/C++、Go、Rust等语言编译后通常生成此类程序;部分虚拟机语言(如特定Java/.NET实现)通过AOT编译也能生成Native程序。

许多人因机器码人眼难以阅读而认为Native程序很安全,但这一观点存在局限性。掌握汇编语言的技术人员配合反汇编器、调试器等工具,可将机器码转换为可分析的汇编代码,从而理解程序行为并定位关键逻辑点。这将带来实际安全风险:

  1. 绕过功能限制:修改许可证或注册状态验证代码,实现未授权使用。

  2. 窃取核心逻辑:通过分析汇编代码推导关键算法实现细节。

  3. 植入恶意代码:覆盖原始机器码指令,注入病毒或后门功能。

  4. 威胁知识产权:程序核心功能与设计经逆向分析暴露,增加侵权风险。

破解

实例程序代码

CPP 复制代码
#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

string keygen(const string& userName)
{
  string key;
  uint8_t xorValue = userName.at(0);
  for_each(begin(userName) + 1, end(userName), [&xorValue](char ch) {
    xorValue ^= ch;
    });

  for (int i = userName.size() - 1; i >= 0; --i)
  {
    char uch = userName.at(i);

    int idx = ((uch - i ^ xorValue) ^ xorValue) % 26;
    key.insert(key.begin(), (char)('a' + idx));
  }

  return key;
}

void print_cat()
{
  cout << "    /\\_/\\   " << endl;
  cout << "   ( o.o )  " << endl;
  cout << "    > ^ <   " << endl;
  cout << " You got a kitten!" << endl;
}

bool verifyAccount(const string& userName, const string& key)
{
  if (userName.size() != key.size())
    return false;


  string key;
  uint8_t xorValue = userName.at(0);
  for_each(begin(userName) + 1, end(userName), [&xorValue](char ch) {
    xorValue ^= ch;
  });

  for (int i = userName.size() - 1; i >= 0; --i)
  {
    char uCh = userName.at(i);
    char keyCh = key.at(i);

    int idx = ((uCh - i ^ xorValue) ^ xorValue) % 26;
    if ('a' + idx != keyCh)
      return false;
  }
  return true;
}

int main()
{
  string userName, key;

  cout << "Enter username: ";
  cin >> userName;

  cout << "Enter key: ";
  cin >> key;

  if (verifyAccount(userName, key))
  {
    cout << "Verification Successful" << endl;
    print_cat();
  }
  else
  {
    cout << "Verification Failed" << endl;
  }

  cin.ignore();
  getchar();
  return 0;
}

这里提供一个正确的UserName和Key: plmqazvbnm - iddgpninyw

成功的情况:

复制代码
Enter username: plmqazvbnm
Enter key: iddgpninyw
Verification Successful
    /\_/\
   ( o.o )
    > ^ <
 You got a kitten!

失败的情况:

复制代码
Enter username: plmqazvbnm
Enter key: 0
Verification Failed

反汇编

使用反汇编工具反编译编译后的程序,结果如下:

CPP 复制代码
int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // r8
  void **v4; // r8
  unsigned __int64 v5; // r15
  char *v6; // rbx
  __int32 v7; // r10d
  unsigned __int64 v8; // r9
  __int128 *v9; // rax
  __int64 v10; // rax
  __int64 v11; // r8
  __int64 v12; // rax
  __int64 v13; // r8
  __int64 v14; // rax
  __int64 v15; // r8
  __int64 v16; // rax
  const char *v17; // rdx
  __int64 v18; // rax
  char *v19; // rax
  void *v20; // rcx
  __int128 v22; // [rsp+50h] [rbp-78h] BYREF
  __m128i si128; // [rsp+60h] [rbp-68h]
  void *Block[2]; // [rsp+70h] [rbp-58h] BYREF
  __m128i v25; // [rsp+80h] [rbp-48h]

  v22 = 0LL;
  si128 = _mm_load_si128((const __m128i *)&xmmword_140021720);
  LOBYTE(v22) = 0;
  *(_OWORD *)Block = 0LL;
  v25 = si128;
  LOBYTE(Block[0]) = 0;
  sub_140001470(&qword_140033540, "Enter username: ", envp);
  sub_140001220(&qword_1400334A0, &v22);
  sub_140001470(&qword_140033540, "Enter password: ", v3);
  sub_140001220(&qword_1400334A0, Block);
  v5 = v25.m128i_u64[1];
  v6 = (char *)Block[0];
  if ( si128.m128i_i64[0] == v25.m128i_i64[0] )
  {
    if ( !si128.m128i_i64[0] )
      goto LABEL_25;
    v7 = si128.m128i_i32[0] - 1;
    v8 = si128.m128i_i32[0] - 1;
    if ( si128.m128i_i32[0] - 1 >= 0 )
    {
      while ( si128.m128i_i64[0] > v8 )
      {
        v9 = &v22;
        if ( si128.m128i_i64[1] > 0xFuLL )
          v9 = (__int128 *)v22;
        if ( v25.m128i_i64[0] <= v8 )
          break;
        v4 = Block;
        if ( v25.m128i_i64[1] > 0xFuLL )
          v4 = (void **)Block[0];
        if ( (*((char *)v9 + v8) - v7) % 26 + 97 != *((char *)v4 + v8) )
          goto LABEL_13;
        --v7;
        if ( (--v8 & 0x8000000000000000uLL) != 0LL )
          goto LABEL_12;
      }
LABEL_25:
      unknown_libname_4();
    }
LABEL_12:
    v10 = sub_140001470(&qword_140033540, "Verification Successful", v4);
    sub_140001880(v10);
    v12 = sub_140001470(&qword_140033540, "    /\\_/\\   ", v11);
    sub_140001880(v12);
    v14 = sub_140001470(&qword_140033540, "   ( o.o )  ", v13);
    sub_140001880(v14);
    v16 = sub_140001470(&qword_140033540, "    > ^ <   ", v15);
    sub_140001880(v16);
    v17 = " You got a kitten!";
  }
  else
  {
LABEL_13:
    v17 = "Verification Failed";
  }
  v18 = sub_140001470(&qword_140033540, v17, v4);
  sub_140001880(v18);
  sub_140002DD0(&qword_1400334A0, 1LL, 0xFFFFFFFFLL);
  fgetchar();
  if ( v5 > 0xF )
  {
    v19 = v6;
    if ( v5 + 1 >= 0x1000 )
    {
      v6 = (char *)*((_QWORD *)v6 - 1);
      if ( (unsigned __int64)(v19 - v6 - 8) > 0x1F )
        invoke_watson(0LL, 0LL, 0LL, 0, 0LL);
    }
    j_j_j__free_base(v6);
  }
  if ( si128.m128i_i64[1] > 0xFuLL )
  {
    v20 = (void *)v22;
    if ( (unsigned __int64)(si128.m128i_i64[1] + 1) >= 0x1000 )
    {
      v20 = *(void **)(v22 - 8);
      if ( (unsigned __int64)(v22 - (_QWORD)v20 - 8) > 0x1F )
        invoke_watson(0LL, 0LL, 0LL, 0, 0LL);
    }
    j_j_j__free_base(v20);
  }
  return 0;
}

修改代码逻辑-动态补丁

反汇编代码和源代码确实差别很大,但是代码执行逻辑还是能看出来的,成功跳转LABEL_12,不成功跳转LABEL_13,所以只要在验证逻辑无条件跳转到LABEL_12就破解成功了。找到对应的汇编代码, 将jnz LABEL_13修改成jmp LABEL_12

程序补丁

根据前文分析,仅需修改验证流程中的单个关键指令(如条件跳转指令),即可绕过验证逻辑实现暴力破解,无需逆向实际算法。这种补丁技术分为两类核心实现方式:

动态补丁(内存补丁)

  • 操作时机:在目标程序运行时实施

  • 技术原理:通过调试器(如x64dbg/OllyDbg)定位内存中的指令地址

  • 典型操作:将JNZ(非零跳转)改为JMP(无条件跳转)或NOP(空操作)

  • 优势:无需修改原始文件,规避文件校验

  • 局限:补丁效果仅持续到进程结束

静态补丁(文件补丁)

  • 操作对象:直接修改磁盘上的可执行文件(如PE/ELF)

  • 技术流程:

    1. 使用反汇编器(IDA Pro/Ghidra)定位目标函数偏移

    2. 计算新指令的机器码(如90对应NOP)

    3. 用十六进制编辑器(HxD/010 Editor)覆盖原指令

  • 持久性:补丁永久生效

  • 风险:可能破坏文件签名

安全影响:此类补丁技术可完全绕过许可证验证,使收费功能未授权可用,同时暴露核心控制流弱点。开发者需结合代码混淆、反调试校验等技术强化防护。

Native代码安全

这里推荐一款专注于 Native 代码安全的防护工具:Virbox Protector。该工具通过代码混淆和虚拟化技术,有效隐藏程序核心逻辑和执行路径。混淆技术使代码结构复杂化,增加逆向分析的难度;虚拟化技术将关键指令转化为私有虚拟指令集,防止直接解析代码逻辑。同时,结合实时内存校验机制,能够阻止静态补丁篡改;调试器检测功能也在一定程度上防止逆向者进行动态分析,从而最大程度地保护软件代码安全。