首页
社区
课程
招聘
[原创]Wibu Codemeter 7.3学习笔记——AxProtector壳初探
发表于: 2022-12-31 17:00 40179

[原创]Wibu Codemeter 7.3学习笔记——AxProtector壳初探

2022-12-31 17:00
40179

前一篇 [原创]Wibu Codemeter 7.3学习笔记

看完了回个贴呗,你们的关注是我更新的动力

求一个Wibu AxProtector的license,或者是一个能用的AxProtector,后续研究需要

0x00工具

Fake Wibu Codemeter Server

Wireshark

IDA Pro

0x01最近研究的成果,发现

decrypt_telegram函数 可以接受15s以内的通信延迟,因此可以做一个Codemeter协议解析器
这里我修改了一点Wireshark的源码,让Wireshark将Codemeter的数据包转发给假服务器,这样可以让我们更加清晰直观的观察Codemeter的通讯过程,帮助我们在假服务器上实现那堆API

存在大量未公开的api 例如
APICODE 119:用于帮助文档的解析
APICODE 32 与Codemeter Control Center交换数据
APICODE 130 添加WibuCmLif

进度

    1.已完成数据包发送函数的构建

    2.已完成数据包解析框架,实现绝大部分的授权,管理API

    3.用Qt简单写了个GUI,未完成

TODO:

Fake Wibu Codemeter Server

实现CmGetBoxContents2 CmGetInfo(ENTRYDATA ENTRYINFO)等license相关API 

实现CmCrypt等加密相关API

实现HCMSE 句柄,及CmContainner CmBox的结构,管理


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-1-27 00:36 被ericyudatou编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (24)
雪    币: 29182
活跃值: (63621)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
期待完整版
2022-12-31 19:07
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3

0x02 初步分析


从样本一进行跟踪分析,CmCalculateSignature发现在这里被调用

  ……
  memset((__m128i *)&cmAuth, 0, sizeof(cmAuth));
  v15 = v4[1];
  if ( v15 == 512 )
  {
    mflCtrl = cmAuth.mflCtrl;
    cmAuth.mulKeyExtType = 0;
    if ( v23 )
      mflCtrl = 5;
    cmAuth.mflCtrl = mflCtrl;
  }
  else if ( v15 == 1024 )
  {
    cmAuth.mflCtrl = 2;
    cmAuth.mulKeyExtType = 136;
  }
  pbPubKey = (unsigned int)(v11 + 8);
  memcpy(v21[190], cmAuth.mabDigest, 32);
  if ( !sub_25971B0(v4) )
    return 0;
  v17 = sub_2596C00();
  hcmse = v20[1];
  memset(pbSignature, 0, sizeof(pbSignature));
  if ( !(*(int (__cdecl **)(int, CMAUTHENTICATE *, __m128i *, int))(v17 + 56))(hcmse, &cmAuth, pbSignature, 64) )// cm_calc_signature
  {
    v4[42] = sub_269E220(v17, hcmse);
    return 0;
  }
  if ( (*(int (__cdecl **)(CMAUTHENTICATE *, __m128i *, int, unsigned int, int))(v17 + 60))(// CmValidateSignature
         &cmAuth,
         pbSignature,
         64,
         pbPubKey,
         64) )
  {
    return 0;
  }
  result = sub_269E220(v17, hcmse);
  v4[42] = result;
  return result;
}

在通过服务器计算签名后在本地用CmValidateSignature验证签名。根据相关帮助文档易知CmValidateSignature运用SHA256 - ECDSA算法

CmCrypt在这里调用

    v40 = a5;
    v38 = v16 & 0xFF00FFFF;
    if ( sub_108F760((char *)&v26, pvDest_, 16) )
    {
      CmCrypt = (int (__cdecl *)(int, _DWORD, CMCRYPT2 *, __int128 *, int))this[5];
      if ( CmCrypt && len >= 0x41E )
      {
        memset((__m128i *)&pcmCrypt_40, 0, sizeof(pcmCrypt_40));
        pcmCrypt_40.mcmBaseCrypt.mulEncryptionCode = EncryptionCode;
        pcmCrypt_40.mcmBaseCrypt.mulEncryptionCodeOptions = v31 ^ v38;
        pcmCrypt_40.mcmBaseCrypt.mflCtrl = 0x6000000;
        pcmCrypt_40.mcmBaseCrypt.mulFeatureCode = v37 ^ v28;
        cbDest = sub_10907A0(&v26);
        res = CmCrypt(hcmse, 0, &pcmCrypt_40, pvDest, cbDest);// CM_CRYPT_DIRECT_ENC
      }
      else
      {
        if ( !v29 || (cbDest_1 = 56, v33 == 0x3000000) )
          cbDest_1 = 40;
        *(_QWORD *)&pcmCrypt.mcmBaseCrypt.mflCtrl = 0i64;
        pcmCrypt.mcmBaseCrypt.mulEncryptionCode = EncryptionCode;
        pcmCrypt.mcmBaseCrypt.mulEncryptionCodeOptions = v31 ^ v38;
        memset(&pcmCrypt.mcmBaseCrypt.mulFeatureCode, 0, 24);
        pcmCrypt.mcmBaseCrypt.mulFeatureCode = v37 ^ v28;
        CmCrypt2 = (int (__cdecl *)(int, _DWORD, CMCRYPT *, __int128 *, int))this[4];
        pcmCrypt.mcmBaseCrypt.mflCtrl = 0x6000000;
        res = CmCrypt2(hcmse, 0, &pcmCrypt, pvDest, cbDest_1);// CM_CRYPT_DIRECT_ENC
      }
      res_ = res;
      if ( res )
      {
        sub_FF3DB0((int)aes);
        *pvDest_ = pvDest[0];
      }
    }

让这两个函数在此地不要走动,咱们解决了这些加密API再来找她

2022-12-31 20:47
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
4

0x03 授权 & 加密 API分析

这些API,咱一个个来

CmCalculateDigest

CODEMETER_API int CMAPIENTRY CmCalculateDigest(const CMBYTE *pbInput, CMUINT cbInput,
    CMBYTE *pbDigest, CMUINT cbDigest);

本地实现,不需要服务器。输入一个二进制串,返回唯一对应的32位二进制串。

int __thiscall CmCalculateDigest_0(int *this, BYTE *pbInput, int cbInput, BYTE *pbDigest, unsigned int cbDigest)
{
  int v6; // eax
  _DWORD v8[5]; // [esp+0h] [ebp-98h] BYREF
  int sha256[33]; // [esp+14h] [ebp-84h] BYREF

  sha256[29] = (int)v8;
  v6 = *this;
  v8[4] = this;
  (*(void (__thiscall **)(int *, _DWORD))(v6 + 4))(this, 0);
  if ( cbDigest >= 0x20 )
  {
    sha256[32] = 0;
    memset(sha256, 0, 112u);
    init_sha256((int)sha256);
    sha256_update(sha256, pbInput, cbInput);
    sha256_final(pbDigest, (int)sha256);
  }
  else
  {
    (*(void (__thiscall **)(int *, int))(*this + 4))(this, 112);// 传递给CodeMeter驱动程序的数据段太小, 错误 112.
  }
  return 32;
}

平淡无奇的sha256套皮,没什么可说的。

CmCalculateSignature

需要用服务器,核心科技在CodeMeter.exe上,咱先分析一下客户端上的

#define CM_DIGEST_LEN                 32
typedef struct __CMAUTHENTICATE {
  CMULONG mflCtrl;
  CMULONG mulKeyExtType;
  CMULONG mulFirmCode;
  CMULONG mulProductCode;
  CMULONG mulEncryptionCodeOptions;
  CMULONG mulFeatureCode;
  CMBOXINFO mcmBoxInfo;
  CMBYTE mabDigest[CM_DIGEST_LEN];
} CMAUTHENTICATE;

CODEMETER_API int CMAPIENTRY CmCalculateSignature(HCMSysEntry hcmse,
    const CMAUTHENTICATE *pcmAuth, CMBYTE *pbSignature, CMUINT cbSignature);

功能:通过CmDongle(狗)中的私钥用ECDSA算法对一个32位的二进制串进行签名,返回签名,不唯一,长度64位

客户端:

// CODEMETER_API int CMAPIENTRY CmCalculateSignature(HCMSysEntry hcmse,
//     const CMAUTHENTICATE *pcmAuth, CMBYTE *pbSignature, CMUINT cbSignature);
int __thiscall CmCalculateSignature_1(
        LPCRITICAL_SECTION *this,
        HCMSysEntry hcmse,
        CMAUTHENTICATE *pcmAuth,
        BYTE *pbSignature,
        int cbSignature)
{
  int cbSignature_1; // eax
  _OWORD *v7; // eax
  _DWORD *v8; // eax
  unsigned int Ticket_2; // eax
  int res; // esi
  size_t newsize; // [esp+18h] [ebp-120h]
  HCMSysEntry hcmse_1; // [esp+20h] [ebp-118h] BYREF
  int buf[65]; // [esp+24h] [ebp-114h] BYREF
  int v16; // [esp+134h] [ebp-4h]

  hcmse_1 = hcmse;
  if ( !isBadHandle(this, &hcmse_1) || !isBadAddress(this, pcmAuth, 0xC8u) )
    return 0;
  if ( (unsigned int)cbSignature < 0x40 || !pbSignature )
  {
    ((void (__thiscall *)(LPCRITICAL_SECTION *, int))(*this)->LockCount)(this, 105);
    return 0;
  }
  cbSignature_1 = 64;
  if ( (unsigned int)cbSignature <= 0x40 )
    cbSignature_1 = cbSignature;
  newsize = cbSignature_1;
  if ( !isBadAddress(this, pbSignature, cbSignature_1) )
    return 0;
  memset(buf, 0, sizeof(buf));
  buf[1] = -1;
  buf[2] = 0;
  LOBYTE(buf[3]) = 0;
  buf[4] = 0;
  buf[5] = 0;
  buf[6] = -1;
  buf[7] = 0;
  LOBYTE(buf[8]) = 0;
  v16 = 0;
  buf[0] = (int)&YS0039::`vftable';
  LOBYTE(buf[9]) = 90;
  memset(&buf[10], 0, 0xD8u);
  v7 = operator new(0x24u);
  LOBYTE(v16) = 1;
  if ( v7 )
  {
    *v7 = 0i64;
    v7[1] = 0i64;
    *((_DWORD *)v7 + 8) = 0;
    v8 = sub_67602DB0(v7, 1u, 0);
  }
  else
  {
    v8 = 0;
  }
  buf[64] = (int)v8;
  v16 = 2;
  Ticket_2 = CmGetTicket_2(this + 23, (unsigned __int16)hcmse_1);
  buf[10] = Ticket_2 | (unsigned int)hcmse_1;
  qmemcpy(&buf[11], pcmAuth, 0xC8u);
  buf[61] = newsize;
  reallocateMem((struct_reallocateMem *)buf[64], newsize);
  if ( *(_DWORD *)(buf[64] + 8) )
    buf[63] = *(_DWORD *)(buf[64] + 4);
  else
    buf[63] = 0;
  if ( send_cm_socket_Req(this + 6, buf, 0xD4u, newsize + 12, 0) )// 发出请求
  {
    memmove(pbSignature, (const void *)buf[63], buf[61]);
    res = buf[62];
    FREE(buf);
    return res;
  }
  else
  {
    FREE(buf);
    return 0;
  }
}

也没什么能说的,捣鼓捣鼓参数塞请求包里。结构如下

struct{
    int apicode;//90
    HCMSysEntry hcmse;
    CMAUTHENTICATE *pcmAuth;
    CMUINT cbSignature;
}

服务端:(**占位**)

CmGetPublicKey

CODEMETER_API int CMAPIENTRY CmGetPublicKey(HCMSysEntry hcmse,
    const CMAUTHENTICATE *pcmAuth, CMBYTE *pbPubKey, CMUINT cbPubKey);

CmContainner中获得公钥,因为CmContainner中只存放私钥故需要通过私钥计算公钥

int __thiscall CmGetPublicKey_1(
        LPCRITICAL_SECTION *this,
        HCMSysEntry hcmse,
        CMAUTHENTICATE *pcmAuth,
        char *pbPubKey,
        int cbPubKey)
{
  int cbPublicKey; // edi
  unsigned int v7; // eax
  unsigned int keysrc; // ecx
  LPCRITICAL_SECTION v9; // ecx
  int Ticket_2; // eax
  int v12; // esi
  void *v13; // ecx
  unsigned __int16 mulKeyExtType; // [esp-8h] [ebp-2Ch]
  int mulEncryptionCodeOptions; // [esp-4h] [ebp-28h]
  HCMSysEntry hcmse_1; // [esp+24h] [ebp+0h] BYREF
  int buf[67]; // [esp+28h] [ebp+4h] BYREF
  void *Src[2]; // [esp+134h] [ebp+110h] BYREF
  int v19; // [esp+13Ch] [ebp+118h]

  hcmse_1 = hcmse;
  if ( !isBadHandle(this, &hcmse_1) )
    return 0;
  if ( !isBadAddress(this, pcmAuth, 0xC8u) )
    return 0;
  cbPublicKey = cbPubKey;
  if ( !isBadAddress(this, pbPubKey, cbPubKey) )
    return 0;
  v7 = 64;
  keysrc = pcmAuth->mflCtrl & 7;                // Key Source
  if ( keysrc == 6 )                            // CM_AUTH_UNIVERSALDATA
    v7 = 1044;
  if ( cbPubKey <= v7 )
  {
    if ( cbPubKey < v7 )
    {
LABEL_22:
      ((void (__thiscall *)(LPCRITICAL_SECTION *, int))(*this)->LockCount)(this, 105);// Message Text
                                                // 
                                                // The specified parameter is invalid, Error 105.
      return 0;
    }
  }
  else
  {
    cbPublicKey = v7;
  }
  if ( !pbPubKey )
    goto LABEL_22;
  if ( keysrc == 6 )                            // CM_AUTH_UNIVERSALDATA
  {
    mulEncryptionCodeOptions = pcmAuth->mulEncryptionCodeOptions;
    v9 = this[111];
    mulKeyExtType = pcmAuth->mulKeyExtType;
    *(_QWORD *)Src = 0i64;
    v19 = 0;
    sub_676388E0(v9, Src, (int)hcmse_1, mulKeyExtType, mulEncryptionCodeOptions); //UVD Get Public Key
    if ( Src[0] == Src[1] )
    {
      free(Src);
      return 0;
    }
    else
    {
      memmove(pbPubKey, Src[0], Src[1] - Src[0]);
      free(Src);
      return 1;
    }
  }
  else
  {
    memset(buf, 0, sizeof(buf));
    buf[1] = -1;
    buf[2] = 0;
    LOBYTE(buf[3]) = 0;
    buf[4] = 0;
    buf[5] = 0;
    buf[6] = -1;
    buf[7] = 0;
    LOBYTE(buf[8]) = 0;
    buf[0] = (int)&YS0038::`vftable';
    memset(&buf[10], 0, 228);
    LOBYTE(buf[9]) = 91;
    Ticket_2 = CmGetTicket_2(this + 23, (unsigned __int16)hcmse_1);
    sub_67614390(buf, (int)hcmse_1, pcmAuth, cbPublicKey, Ticket_2);
    if ( send_cm_socket_Req(this + 6, buf, 0xD4u, cbPublicKey + 12, 0) )
    {
      memmove(pbPubKey, (const void *)buf[66], buf[64]);
      v12 = buf[65];
    }
    else
    {
      v12 = 0;
    }
    v13 = (void *)buf[10];
    if ( buf[10] )
    {
      if ( (unsigned int)(buf[12] - buf[10]) >= 0x1000 )
      {
        v13 = *(void **)(buf[10] - 4);
        if ( (unsigned int)(buf[10] - (_DWORD)v13 - 4) > 0x1F )
          _invalid_parameter_noinfo_noreturn();
      }
      sub_67690A3E(v13);
      memset(&buf[10], 0, 12);
    }
    sub_6763FEC0(buf);
    return v12;
  }
}

整体上大同小异,数据包跟CmCalculateSignature一样

struct{
    int apicode;//91
    HCMSysEntry hcmse;
    CMAUTHENTICATE *pcmAuth;
    CMUINT cbPublicKey;
}

当Key Source 为 Universal Data时会转发到CmGetPublicKeyUVD,处理流程不太一样,后面说CmGetPublicKeyUVD再提

CmValidateSignature

CODEMETER_API int CMAPIENTRY CmValidateSignature(const CMAUTHENTICATE *pcmAuth,
    const CMBYTE *pbSignature, CMUINT cbSignature,
    const CMBYTE *pbPubKey, CMUINT cbPubKey);

本地实现,用ECDSA验证签名,签名由CmCalculateSignature生成,pcmAuth-->mabDigest(被签名的sha256值)必须存在

int __thiscall CmValidateSignature(
        int *this,
        CMAUTHENTICATE *pcmAuth,
        BYTE *pbSignature,
        unsigned int cbSignature,
        BYTE *pbPubKey,
        unsigned int cbPubKey)
{
  int v7; // eax
  void *v8; // eax
  CMBOXINFO *boxinfo_1; // eax
  bool res; // al
  int mulEncryptionCodeOptions; // [esp-4h] [ebp-20Ch]
  int v13; // [esp+0h] [ebp-208h] BYREF
  CMBOXINFO boxinfo; // [esp+10h] [ebp-1F8h] BYREF
  int *pbPubKey_1; // [esp+A0h] [ebp-168h]
  void *v16[65]; // [esp+A4h] [ebp-164h] BYREF
  int signature[17]; // [esp+1A8h] [ebp-60h] BYREF
  int len; // [esp+1ECh] [ebp-1Ch] BYREF
  int key_source[3]; // [esp+1F0h] [ebp-18h] BYREF
  int v20; // [esp+204h] [ebp-4h]

  key_source[2] = (int)&v13;
  pbPubKey_1 = (int *)pbPubKey;
  v7 = *this;
  boxinfo.mulExtendedSerial = (unsigned int)this;
  (*(void (__thiscall **)(int *, _DWORD))(v7 + 4))(this, 0);
  if ( cbPubKey < 0x40 || cbSignature < 0x40 )
  {
    (*(void (__thiscall **)(int *, int))(*this + 4))(this, 105);// 指定了一个无效的参数, 错误 105.
  }
  else
  {
    memset(signature, 0, sizeof(signature));
    v8 = sub_6760F7E0();
    sub_67656310((int)signature, (int)v8);
    v20 = 0;
    init_this((int)v16);
    LOBYTE(v20) = 1;
    boxinfo_1 = copy_boxinfo(&boxinfo, &pcmAuth->mcmBoxInfo);
    copy_boxinfo_to_this((int)v16, boxinfo_1);
    copy_firm_code_to_this((int)v16, pcmAuth->mulFirmCode, 0, 0, 0);
    sub_676588C0(signature, (int)v16);
    len = 0;
    key_source[0] = 0;
    if ( explain_mflCtrl(pcmAuth->mflCtrl, &len, key_source) )
    {
      mulEncryptionCodeOptions = pcmAuth->mulEncryptionCodeOptions;
      LOBYTE(v20) = 2;
      res = doValidateSignature(
              signature,
              len,
              pcmAuth->mabDigest,
              (int)pbSignature,
              key_source[0],
              pbPubKey_1,
              pcmAuth->mulProductCode,
              pcmAuth->mulFeatureCode,
              mulEncryptionCodeOptions);
      v20 = 1;
      if ( res )
      {
        clean_s(v16);                           // 成功
        clean_s_0(signature);
        return 1;
      }
      (*(void (__thiscall **)(int *, int))(*this + 4))(this, 202);// 对该序列的加/解密操作失败, 错误 202.
    }
    else
    {
      (*(void (__thiscall **)(int *, int))(*this + 4))(this, 105);// 指定了一个无效的参数, 错误 105.
    }
    clean_s(v16);
    clean_s_0(signature);
  }
  return 0;
}
bool __thiscall doValidateSignature(
        int *signature,
        int len,
        _OWORD *mabDigest,
        unsigned __int8 *pbSignature,
        int key_source,
        int *pbPubKey,
        int productCode,
        int featureCode,
        int mulEncryptionCodeOptions)
{
  int *pubKey; // ebx
  int v11; // eax
  __int128 v12; // xmm0
  int v13; // eax
  int v15[35]; // [esp+Ch] [ebp-20Ch] BYREF
  int arr[31]; // [esp+98h] [ebp-180h] BYREF
  int ecdsa[24]; // [esp+114h] [ebp-104h] BYREF
  int pubkey[16]; // [esp+174h] [ebp-A4h] BYREF
  int mabDigest_1[4]; // [esp+1B4h] [ebp-64h] BYREF
  __int128 v20; // [esp+1C4h] [ebp-54h]
  int v21[16]; // [esp+1D4h] [ebp-44h] BYREF

  pubKey = pbPubKey;
  *(_OWORD *)mabDigest_1 = *mabDigest;
  v20 = mabDigest[1];
  memset(v21, 0, sizeof(v21));
  memset(pubkey, 0, sizeof(pubkey));
  if ( len == 16 || len == 32 || len == 64 )
  {
    memset(&arr[1], 0, 0x77u);                  // 16
    v11 = *signature;
    arr[0] = 55;
    arr[5] = *(_DWORD *)((*(int (__thiscall **)(int *))(v11 + 40))(signature) + 4);
    arr[6] = productCode;
    *(_OWORD *)&arr[1] = *(_OWORD *)mabDigest_1;
    *(_OWORD *)((char *)&arr[10] + 3) = v20;
    if ( len == 16 || len == 64 )               // 64
    {
      arr[7] = featureCode;
      arr[9] = mulEncryptionCodeOptions;
      calcSHA256((BYTE *)arr, (BYTE *)mabDigest_1);
      if ( len == 64 )
      {
        v12 = *(_OWORD *)pbPubKey;
        pubkey[6] = pbPubKey[6];
        v13 = pbPubKey[14];
        *(_OWORD *)pubkey = v12;
        pubkey[13] = v13;
        *(_QWORD *)&pubkey[4] = *((_QWORD *)pbPubKey + 2);
        *(_OWORD *)&pubkey[7] = *((_OWORD *)pbPubKey + 2);
        *(_QWORD *)&pubkey[11] = *((_QWORD *)pbPubKey + 6);
        sub_67687130((unsigned int *)ecdsa, (unsigned __int8 *)pubkey, 1, (int)v21);
        pubKey = pubkey;
        pubkey[6] = v21[6];
        *(_OWORD *)pubkey = *(_OWORD *)v21;
        pubkey[7] = 0;
        *(_QWORD *)&pubkey[4] = *(_QWORD *)&v21[4];
        pubkey[14] = v21[13];
        pubkey[15] = 0;
        *(_OWORD *)&pubkey[8] = *(_OWORD *)&v21[7];
        *(_QWORD *)&pubkey[12] = *(_QWORD *)&v21[11];
      }
    }
    else
    {
      qmemcpy(v15, (const void *)(*(int (__thiscall **)(int *))(*signature + 44))(signature), sizeof(v15));// 32
      arr[7] = v15[0];
      LOWORD(arr[8]) = v15[1];
      arr[9] = mulEncryptionCodeOptions;
      calcSHA256((BYTE *)arr, (BYTE *)mabDigest_1);
    }
  }
  return checkECDSASignature((char *)ecdsa, (unsigned __int8 *)pubKey, pbSignature, (unsigned __int8 *)mabDigest_1);
}

逻辑很简单,爆掉checkECDSASignature即可

最后于 2023-1-3 19:04 被ericyudatou编辑 ,原因:
2022-12-31 23:06
0
雪    币: 2909
活跃值: (2847)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
高手,明白一下大神
2023-1-7 14:08
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
6

CmCrypt加密相关

在我们干掉Axprotector的CmValiateSignature校验之后,程序在进行一次CmCrypt2请求之后紫砂,请求如下

客户端CmCrypt2下断

int __thiscall func_0(
        _DWORD *this,
        void *hcmse,
        unsigned int a3,
        _DWORD *key,
        int a5,
        unsigned int a6,
        int a7,
        int a8,
        int a9,
        _OWORD *a10,
        int mulEncryptionCode_1,
        char a12)
{
  _DWORD *v12; // edi
  int v13; // esi
  int v14; // edi
  __int128 v15; // xmm0
  unsigned int v16; // esi
  int (__cdecl *cmcrypt2)(HCMSysEntry, int, CMCRYPT2 *, void *, int); // esi
  int cbDest; // eax
  int len; // eax
  int v20; // ecx
  int (__cdecl *v21)(void *, _DWORD, CMCRYPT *, __int128 *, int); // eax
  int len_1; // esi
  int (__cdecl *v23)(void *, unsigned int, int *, _OWORD *, int); // eax
  _BYTE *v26; // [esp+24h] [ebp-29Ch] BYREF
  __m128i v27[19]; // [esp+28h] [ebp-298h] BYREF
  int v28; // [esp+158h] [ebp-168h]
  int v29; // [esp+15Ch] [ebp-164h]
  int v30; // [esp+160h] [ebp-160h]
  int v31; // [esp+164h] [ebp-15Ch]
  unsigned int v32; // [esp+168h] [ebp-158h]
  unsigned int v33; // [esp+16Ch] [ebp-154h]
  int v34; // [esp+170h] [ebp-150h]
  int v35; // [esp+174h] [ebp-14Ch]
  unsigned int mulEncryptionCode; // [esp+178h] [ebp-148h]
  unsigned int v37; // [esp+17Ch] [ebp-144h]
  unsigned int v38; // [esp+180h] [ebp-140h]
  __int128 v39; // [esp+184h] [ebp-13Ch]
  int v40; // [esp+194h] [ebp-12Ch]
  CMCRYPT v41; // [esp+198h] [ebp-128h] BYREF
  CMCRYPT2 pmacc; // [esp+1C0h] [ebp-100h] BYREF
  __int128 pbDest[3]; // [esp+250h] [ebp-70h] BYREF
  int v44[5]; // [esp+288h] [ebp-38h] BYREF
  __int64 v45; // [esp+29Ch] [ebp-24h]
  __int64 v46; // [esp+2A4h] [ebp-1Ch]
  int v47; // [esp+2ACh] [ebp-14h]
  int v48; // [esp+2BCh] [ebp-4h]

  v12 = this;
  if ( a12 )
  {
    memset((__m128i *)&v26, 0, 0x264u);
    LOBYTE(v26) = 0;
    v13 = key[6];
    v14 = key[5];
    memset(v27, 0, 0x128u);
    init_aes(v27, 0);
    sub_269EAB0((int)&v26, v14, v13);
    v12 = this;
    v15 = *(_OWORD *)(mulEncryptionCode_1 + 4);
    mulEncryptionCode = *(_DWORD *)mulEncryptionCode_1;
    v28 = a7;
    v48 = 0;
    v39 = v15;
    v34 = 0;
    v29 = key[8];
    v30 = a8 ^ this[21];
    v31 = a9;
    v32 = a3 & 1;
    v33 = ((a3 >> 8) & 1) << 26;
    v35 = 0;
    v16 = sub_25FA190(a5, -1);
    v37 = sub_25FA190(a5, -1);
    v40 = a5;
    v38 = v16 & 0xFF00FFFF;
    if ( sub_269DE30((char *)&v26, a10, 16) )
    {
      cmcrypt2 = (int (__cdecl *)(HCMSysEntry, int, CMCRYPT2 *, void *, int))this[5];
      if ( cmcrypt2 && a6 >= 0x41E )
      {
        memset((__m128i *)&pmacc, 0, sizeof(pmacc));
        pmacc.mcmBaseCrypt.mulEncryptionCode = mulEncryptionCode;
        pmacc.mcmBaseCrypt.mulEncryptionCodeOptions = v31 ^ v38;
        pmacc.mcmBaseCrypt.mflCtrl = 0x6000000;
        pmacc.mcmBaseCrypt.mulFeatureCode = v37 ^ v28;
        cbDest = sub_269EE70(&v26);
        len = cmcrypt2(hcmse, 0, &pmacc, pbDest, cbDest);// cmcrypt2
      }
      else
      {
        if ( !v29 || (v20 = 56, v33 == 0x3000000) )
          v20 = 40;
        *(_QWORD *)&v41.mcmBaseCrypt.mflCtrl = 0i64;
        v41.mcmBaseCrypt.mulEncryptionCode = mulEncryptionCode;
        v41.mcmBaseCrypt.mulEncryptionCodeOptions = v31 ^ v38;
        memset(&v41.mcmBaseCrypt.mulFeatureCode, 0, 24);
        v41.mcmBaseCrypt.mulFeatureCode = v37 ^ v28;
        v21 = (int (__cdecl *)(void *, _DWORD, CMCRYPT *, __int128 *, int))this[4];
        v41.mcmBaseCrypt.mflCtrl = 100663296;
        len = v21(hcmse, 0, &v41, pbDest, v20);
      }
      len_1 = len;
      if ( len )
      {
        sub_25F97F0((int)v27);                  // success
        *a10 = pbDest[0];                       // pDest
      }
    }
    else
    {
      len_1 = 0;
    }
    sub_269EAB0((int)&v26, 0, 0);
    v48 = 1;
    aes_final((int)v27);
    v48 = -1;
  }
  else
  {
    v44[2] = a8 ^ this[21];
    v44[3] = a9;
    v44[4] = a7;
    v23 = (int (__cdecl *)(void *, unsigned int, int *, _OWORD *, int))this[4];
    v44[1] = 0;
    v45 = 0i64;
    v46 = 0i64;
    v47 = 0;
    v44[0] = ((a3 >> 8) & 1) << 26;
    len_1 = v23(hcmse, a3 & 1, v44, a10, 16);
  }
  if ( !len_1 )
    return sub_269E220((int)v12, (int)hcmse);
  *(_BYTE *)a10 ^= *((_BYTE *)v12 + 84);
  return 0;
}

发现是在

len = cmcrypt2(hcmse, 0, &pmacc, pbDest, cbDest);

处调用,观察CmCrypt2函数的参数会发现有几个疑点(这几个疑点将在服务端分析上揭晓)

  1. mcmBaseCrypt.mflCtrl = 0x6000000; 查看手册发现Wibu并未开放这个算法,姑且称之为AxProtector解密算法

  2. mcmBaseCrypt.mulEncryptionCodeOptions  mcmBaseCrypt.mulFeatureCode并未传入相应数据,推测为AxProtector解密算法的私有参数

  3. IV key,AES Decrypt Key均未设置,cbDest长度恒为40,mcmBaseCrypt.mulEncryptionCode为固定值0x1337

现在有两个方向,向上去找对func_0的调用过程,向下通过CmCrypt2去服务端上找实现的算法

实际运行抓包发现

  1. 这是壳启动阶段最后两次调用Codemeter API,在调用完两次CmCrypt2之后程序即可启动,胜利就在眼前

  2. CmCrypt2请求时附加了长度为0x28的待加密数据,服务器返回长度0x10的加密后的数据,加密后的数据覆盖在之前的数据上

最后于 2023-1-9 00:08 被ericyudatou编辑 ,原因:
2023-1-7 23:46
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7

AxProtectot所特有的0x600000Cmcrypt在服务器上的行为分析

参见Wibu Codemeter 7.3学习笔记——Codemeter服务端在api_cm_crypt处下断

void __cdecl api_cm_crypt2(
        int flctrl,
        CMCRYPT2 *cmcrypt2,
        HCMSysEntry hcmse,
        size_t cbDest,
        size_t *a5,
        void *pbDest,
        void *pbDest_1,
        int *a8,
        char a9)
{
  unsigned int len_1; // esi
  int v10; // ecx
  unsigned int v11; // edx
  int v12; // eax
  int *v13; // ecx
  int mulKeyExtType; // esi
  int *v15; // eax
  int *v16; // esi
  struct_cmcrypt *v17; // eax
  bool v18; // zf
  int v19; // ecx
  int v20; // eax
  __m128d v21; // xmm1
  void *v22; // eax
  _DWORD *v23; // eax
  __int128 v24; // xmm0
  void (__thiscall ***v25)(_DWORD, int); // ecx
  _BYTE *v26; // ecx
  size_t v27; // esi
  void *v28; // eax
  _DWORD *v29; // eax
  void *v30; // edi
  void ***v31; // eax
  void ***v32; // eax
  void ***v33; // eax
  void ***v34; // eax
  void ***v35; // eax
  void ***v36; // eax
  void ***v37; // eax
  void ***v38; // esi
  void ***v39; // edi
  void ***v40; // eax
  int errcode; // eax
  void ***v42; // eax
  HCMSysEntry hcmse_1; // [esp-10h] [ebp-794h]
  int v44; // [esp-10h] [ebp-794h]
  int v45; // [esp-Ch] [ebp-790h]
  void *v46; // [esp-Ch] [ebp-790h]
  void *v47; // [esp-Ch] [ebp-790h]
  void ***v48; // [esp-Ch] [ebp-790h]
  void ***v49; // [esp-Ch] [ebp-790h]
  void ***v50; // [esp-Ch] [ebp-790h]
  void ***v51; // [esp-Ch] [ebp-790h]
  void ***v52; // [esp-Ch] [ebp-790h]
  void ***v53; // [esp-Ch] [ebp-790h]
  void ***v54; // [esp-Ch] [ebp-790h]
  int v55; // [esp-Ch] [ebp-790h]
  void ***v56; // [esp-Ch] [ebp-790h]
  const void *v57; // [esp-8h] [ebp-78Ch]
  const void *v58; // [esp-8h] [ebp-78Ch]
  void ***v59; // [esp-8h] [ebp-78Ch]
  void ***v60; // [esp-8h] [ebp-78Ch]
  void ***v61; // [esp-8h] [ebp-78Ch]
  void ***v62; // [esp-8h] [ebp-78Ch]
  void ***v63; // [esp-8h] [ebp-78Ch]
  void ***v64; // [esp-8h] [ebp-78Ch]
  void ***v65; // [esp-8h] [ebp-78Ch]
  void ***v66; // [esp-8h] [ebp-78Ch]
  size_t v67; // [esp-4h] [ebp-788h]
  size_t v68; // [esp-4h] [ebp-788h]
  void ***v69; // [esp-4h] [ebp-788h]
  void ***v70; // [esp-4h] [ebp-788h]
  void ***v71; // [esp-4h] [ebp-788h]
  void ***v72; // [esp-4h] [ebp-788h]
  void ***v73; // [esp-4h] [ebp-788h]
  void ***v74; // [esp-4h] [ebp-788h]
  void ***v75; // [esp-4h] [ebp-788h]
  void ***v76; // [esp-4h] [ebp-788h]
  int v77; // [esp+0h] [ebp-784h] BYREF
  char pExceptionObject[140]; // [esp+10h] [ebp-774h] BYREF
  char v79[140]; // [esp+128h] [ebp-65Ch] BYREF
  char v80[140]; // [esp+1B4h] [ebp-5D0h] BYREF
  char v81[140]; // [esp+240h] [ebp-544h] BYREF
  char v82[140]; // [esp+2CCh] [ebp-4B8h] BYREF
  char v83[140]; // [esp+358h] [ebp-42Ch] BYREF
  char v84[140]; // [esp+3E4h] [ebp-3A0h] BYREF
  char v85[140]; // [esp+470h] [ebp-314h] BYREF
  char v86[140]; // [esp+4FCh] [ebp-288h] BYREF
  __int128 v87; // [esp+58Ch] [ebp-1F8h] BYREF
  char v88[24]; // [esp+59Ch] [ebp-1E8h]
  size_t len; // [esp+5B4h] [ebp-1D0h]
  void *pbDest_3; // [esp+5B8h] [ebp-1CCh]
  int v91; // [esp+5BCh] [ebp-1C8h]
  int flctrl_1; // [esp+5C0h] [ebp-1C4h]
  int *v93; // [esp+5C4h] [ebp-1C0h]
  size_t *v94; // [esp+5C8h] [ebp-1BCh]
  _DWORD *hcmse_2; // [esp+5CCh] [ebp-1B8h]
  int v96; // [esp+5D0h] [ebp-1B4h]
  _BYTE v97[356]; // [esp+5D4h] [ebp-1B0h] BYREF
  struct_cmcrypt algorithms; // [esp+738h] [ebp-4Ch] BYREF
  void *v99[2]; // [esp+75Ch] [ebp-28h] BYREF
  void *pbDest_2; // [esp+764h] [ebp-20h] BYREF
  void *encrypt_option; // [esp+768h] [ebp-1Ch] BYREF
  unsigned int hcmse_3[3]; // [esp+76Ch] [ebp-18h] BYREF
  int v103; // [esp+780h] [ebp-4h]

  hcmse_3[2] = (unsigned int)&v77;
  len_1 = cbDest;
  flctrl_1 = flctrl;
  hcmse_1 = *(HCMSysEntry *)hcmse;
  hcmse_2 = hcmse;
  v94 = a5;
  pbDest_2 = pbDest;
  v10 = *(_DWORD *)(global_1360FC4 + 196);
  pbDest_3 = pbDest_1;
  encrypt_option = cmcrypt2;
  len = cbDest;
  v93 = a8;
  v11 = check_hcmse_handle(v10, hcmse_1, 0x10000, 1, 1);
  hcmse_3[0] = v11;
  if ( a9 )
    ++*(_DWORD *)(global_1360FC4 + 460);
  else
    ++*(_DWORD *)(global_1360FC4 + 456);
  *v94 = 0;
  if ( (unsigned __int8)isReleaseMode(v11 + 328) )
  {
    if ( cbDest < 0x10 )                        // 指定了一个无效的参数, 错误 105.cbDest太短
    {
      v70 = sub_BDE030();
      v60 = sub_BDE030();
      v49 = sub_BDE030();
      v32 = sub_BDE030();
      construct_Exception(v86, 105, (int)v32, (int)v49, (int)v60, (int)v70);
      _CxxThrowException(v86, (_ThrowInfo *)&_TI2_AVException_wbs__);
    }
    v91 = sub_BDB5A0((_DWORD *)(hcmse_3[0] + 328));
    sub_C0EC70((_BYTE *)(hcmse_3[0] + 328));
    v12 = v91;
    if ( v91 )
    {
      v13 = *(int **)(v91 + 380);
      v93 = v13;
      v96 = 0;
      hcmse_2 = v13;
      if ( v13 )
      {
        v96 = v13[21];
        v93 = v13;
        hcmse_2 = v13;
      }
    }
    else
    {
      v93 = 0;
      hcmse_2 = 0;
      v96 = 0;
      v12 = 0;
    }
    if ( (*(_DWORD *)(v12 + 56) & 0x200) != 0
      && !sub_BFEE40((_BYTE *)global_1360FC4)
      && (*(_DWORD *)(hcmse_3[0] + 644) & 0x10000) != 0 )// TMP许可不支持此命令, 错误134.
    {
      v71 = sub_BDE030();
      v61 = sub_BDE030();
      v50 = sub_BDE030();
      v33 = sub_BDE030();
      construct_Exception(v85, 134, (int)v33, (int)v50, (int)v61, (int)v71);
      _CxxThrowException(v85, (_ThrowInfo *)&_TI2_AVException_wbs__);
    }
    if ( (cmcrypt2->mcmBaseCrypt.mflCtrl & 0xF) == 8 )// CM_CRYPT_UNIVERSALDATA
    {                                           // UVD加密,不是研究的重点,忽略即可
	/*
      resolve_crypt_alg((unsigned int *)&encrypt_option, cmcrypt2->mcmBaseCrypt.mflCtrl & 0xFF000000);
      mulKeyExtType = cmcrypt2->mcmBaseCrypt.mulKeyExtType;
      sub_C59B10(v91, v99);
      v103 = 0;
      v15 = sub_D46E60((int ***)v99, mulKeyExtType);
      v16 = v15;
      if ( !v15 )                               // 无法找到要求的产品码选项, 错误 26.
      {
        v72 = sub_BDE030();
        v62 = sub_BDE030();
        v51 = sub_BDE030();
        v34 = sub_BDE030();
        construct_Exception(v84, 26, (int)v34, (int)v51, (int)v62, (int)v72);
        _CxxThrowException(v84, (_ThrowInfo *)&_TI2_AVException_wbs__);
      }
      if ( !sub_D478C0((int)v15, (int)encrypt_option) )// 指定的产品码选项参数错误, 错误 25.
      {
        v73 = sub_BDE030();
        v63 = sub_BDE030();
        v52 = sub_BDE030();
        v35 = sub_BDE030();
        construct_Exception(v83, 25, (int)v35, (int)v52, (int)v63, (int)v73);
        _CxxThrowException(v83, (_ThrowInfo *)&_TI2_AVException_wbs__);
      }
      v45 = (int)v16;
      len_1 = len;
      v17 = sub_D46EB0(
              (struct_cmcrypt *)((char *)&v87 + 4),
              flctrl_1,
              cmcrypt2,
              len,
              *(_DWORD *)(*(_DWORD *)(v96 + 244) + 832),
              v45,
              0,
              0);
      v103 = -1;
      algorithms = *v17;
      sub_BF8660(v99);
	  */
    }
    else
    {
		//非UVD正常获取加密算法
      algorithms = *(struct_cmcrypt *)cm_crypt_get_alg(
                                        (int)&v87 + 4,
                                        flctrl_1,
                                        cmcrypt2,
                                        cbDest,
                                        *(_DWORD *)(*(_DWORD *)(v96 + 244) + 832),
                                        0,
                                        0);
    }
    hcmse_3[0] = sub_C0EC30((_DWORD *)(hcmse_3[0] + 328), 0x700);
    encrypt_option = (void *)(cmcrypt2->mcmBaseCrypt.mulEncryptionCodeOptions & 0xF0000);
    if ( sub_BFEE40((_BYTE *)global_1360FC4) )
      goto LABEL_27;
    if ( encrypt_option == (void *)0x20000 )    // CM_CRYPT_SASTATIONSHARE
    {
      v18 = hcmse_3[0] == 768;
    }
    else
    {
      if ( encrypt_option != (void *)0x40000 )
      {
        if ( encrypt_option == (void *)0x80000 )
        {
          v18 = hcmse_3[0] == 512;
          goto LABEL_26;
        }
LABEL_27:
        if ( (cmcrypt2->mcmBaseCrypt.mflCtrl & 0x20000) != 0 )// calc crc
        {
          init_crc(hcmse_3);
          crc32(hcmse_3, (char *)pbDest_3, algorithms.cbDest_);
          cmcrypt2->mcmBaseCrypt.mulCrc = bit_negration(hcmse_3);
        }
        if ( (flctrl_1 & 0xF) == 2 )
        {
          v19 = *(_DWORD *)(v96 + 20);
          if ( !v19 || v19 != v93[1] )
          {
            v75 = sub_BDE030();
            v65 = sub_BDE030();
            v54 = sub_BDE030();
            v37 = sub_BDE030();
            construct_Exception(v81, 123, (int)v37, (int)v54, (int)v65, (int)v75);// CmDongle没有CTSB功能模块, 错误 123.
            _CxxThrowException(v81, (_ThrowInfo *)&_TI2_AVException_wbs__);
          }
        }
        v103 = 1;
        if ( !do_crypt_job(v96, v91, &algorithms, pbDest_2, pbDest_2) ) //进行加解密
        {
          v38 = sub_BDE030();
          v39 = sub_BDE030();
          encrypt_option = sub_BDE030();
          v40 = sub_BDE030();
          v55 = (int)encrypt_option;
          v44 = (int)v40;
          errcode = sub_C4B310(*(_DWORD **)(v96 + 244));
          construct_Exception(v80, errcode, v44, v55, (int)v39, (int)v38);
          _CxxThrowException(v80, (_ThrowInfo *)&_TI2_AVException_wbs__);
        }
        v103 = -1;
        if ( algorithms.algorithms == 2 || algorithms.algorithms == 3 )
          *v94 = len_1;
        else
          *v94 = algorithms.cbDest_;
        if ( (cmcrypt2->mcmBaseCrypt.mflCtrl & 0x10000) != 0 )// CM_CRYPT_CHKCRC
        {
          init_crc(&pbDest_2);
          crc32((unsigned int *)&pbDest_2, (char *)pbDest_3, len_1);
          if ( cmcrypt2->mcmBaseCrypt.mulCrc != bit_negration(&pbDest_2) )
          {
            *v94 = 0;
            v76 = sub_BDE030();
            v66 = sub_BDE030();
            v56 = sub_BDE030();
            v42 = sub_BDE030();
            construct_Exception(v79, 203, (int)v42, (int)v56, (int)v66, (int)v76);// 校验数据检测失败, 错误 203.
            _CxxThrowException(v79, (_ThrowInfo *)&_TI2_AVException_wbs__);
          }
        }
        return;
      }
      if ( !hcmse_3[0] )
        goto LABEL_27;
      v18 = hcmse_3[0] == 1024;
    }
LABEL_26:
    if ( !v18 )
    {
      v74 = sub_BDE030();
      v64 = sub_BDE030();
      v53 = sub_BDE030();
      v36 = sub_BDE030();
      construct_Exception(v82, 223, (int)v36, (int)v53, (int)v64, (int)v74);// 所使用的访问模式与EncryptionCodeOption集冲突, 错误 223.
      _CxxThrowException(v82, (_ThrowInfo *)&_TI2_AVException_wbs__);
    }
    goto LABEL_27;
  }
  if ( !sub_C0ED00((_DWORD *)(hcmse_3[0] + 328)) )
  {
    v69 = sub_BDE030();
    v59 = sub_BDE030();
    v48 = sub_BDE030();
    v31 = sub_BDE030();
    construct_Exception(pExceptionObject, 116, (int)v31, (int)v48, (int)v59, (int)v69);// 给出的句柄指向了一个未定义的子系统, 错误 116.
    _CxxThrowException(pExceptionObject, (_ThrowInfo *)&_TI2_AVException_wbs__);
  }
  v20 = sub_C0EC20((_DWORD *)(hcmse_3[0] + 328));
  *hcmse_2 = v20;
  //Wubu Codemeter内部调试时用的代码,忽略
  /*
  if ( a9 )
  {
    memset(v97, 0, sizeof(v97));
    sub_CCC4F0(v97);
    v103 = 4;
    *(_DWORD *)&v97[40] = *hcmse_2;
    *(_DWORD *)&v97[44] = flctrl_1;
    qmemcpy(&v97[48], cmcrypt2, 0x90u);
    v27 = len;
    *(_DWORD *)&v97[192] = len;
    sub_C37B30(*(int *)&v97[352], len);
    if ( *(_DWORD *)(*(_DWORD *)&v97[352] + 8) )
      v28 = *(void **)(*(_DWORD *)&v97[352] + 4);
    else
      v28 = 0;
    *(_DWORD *)&v97[196] = v28;
    memmove(v28, pbDest_2, *(size_t *)&v97[192]);
    *(_DWORD *)&v97[348] = *(_DWORD *)&v97[196];
    v29 = (_DWORD *)sub_C0EC10((_DWORD *)(hcmse_3[0] + 328));
    cm_client_encrypt_req(v29, v97, 0xA1u, v27 + 164, v27 + 156);
    v68 = *(_DWORD *)&v97[192];
    v58 = *(const void **)&v97[348];
    v30 = encrypt_option;
    v47 = pbDest_3;
    *v93 = *(_DWORD *)&v97[8];
    qmemcpy(v30, &v97[204], 0x90u);
    memmove(v47, v58, v68);
    *(_DWORD *)&v97[348] = 0;
    *(_DWORD *)v97 = &YS0534::`vftable';
    *v94 = *(_DWORD *)&v97[200];
    if ( *(_DWORD *)&v97[352] )
      (***(void (__thiscall ****)(_DWORD, int))&v97[352])(*(_DWORD *)&v97[352], 1);
    *(_DWORD *)&v97[348] = 0;
    v26 = v97;
  }
  else
  {
    memset(&v97[208], 0, 0x94u);
    sub_CCC160();
    v21 = *(__m128d *)cmcrypt2->mabInitKey;
    LODWORD(v87) = cmcrypt2->mcmBaseCrypt.mflCtrl;
    *(_DWORD *)&v88[4] = cmcrypt2->mcmBaseCrypt.mulCrc;
    *((_QWORD *)&v87 + 1) = *(_QWORD *)&cmcrypt2->mcmBaseCrypt.mulEncryptionCode;
    *(_DWORD *)v88 = cmcrypt2->mcmBaseCrypt.mulFeatureCode;
    DWORD1(v87) = cmcrypt2->mcmBaseCrypt.mulKeyExtType;
    *(__m128d *)&v88[8] = v21;
    *(_OWORD *)&v97[256] = v87;
    *(_DWORD *)&v97[248] = *hcmse_2;
    v103 = 3;
    *(_DWORD *)&v97[252] = flctrl_1;
    *(_OWORD *)&v97[272] = *(_OWORD *)v88;
    *(_DWORD *)&v97[296] = cbDest;
    *(_QWORD *)&v97[288] = *(_OWORD *)&_mm_unpackhi_pd(v21, v21);
    sub_C37B30(*(int *)&v97[352], cbDest);
    if ( *(_DWORD *)(*(_DWORD *)&v97[352] + 8) )
      v22 = *(void **)(*(_DWORD *)&v97[352] + 4);
    else
      v22 = 0;
    *(_DWORD *)&v97[300] = v22;
    memmove(v22, pbDest_2, *(size_t *)&v97[296]);
    *(_DWORD *)&v97[348] = *(_DWORD *)&v97[300];
    v23 = (_DWORD *)sub_C0EC10((_DWORD *)(hcmse_3[0] + 328));
    cm_client_encrypt_req(v23, &v97[208], 0xA1u, cbDest + 60, cbDest + 52);
    v67 = *(_DWORD *)&v97[296];
    v57 = *(const void **)&v97[348];
    v46 = pbDest_3;
    *v93 = *(_DWORD *)&v97[216];
    memmove(v46, v57, v67);
    v24 = *(_OWORD *)&v97[332];
    *(_DWORD *)&v97[348] = 0;
    *v94 = *(_DWORD *)&v97[304];
    v25 = *(void (__thiscall ****)(_DWORD, int))&v97[352];
    cmcrypt2->mcmBaseCrypt.mflCtrl = *(_DWORD *)&v97[308];
    cmcrypt2->mcmBaseCrypt.mulCrc = *(_DWORD *)&v97[328];
    cmcrypt2->mcmBaseCrypt.mulEncryptionCode = *(_DWORD *)&v97[316];
    cmcrypt2->mcmBaseCrypt.mulEncryptionCodeOptions = *(_DWORD *)&v97[320];
    cmcrypt2->mcmBaseCrypt.mulFeatureCode = *(_DWORD *)&v97[324];
    cmcrypt2->mcmBaseCrypt.mulKeyExtType = *(_DWORD *)&v97[312];
    *(_DWORD *)&v97[208] = &YS0059::`vftable';
    *(_OWORD *)cmcrypt2->mabInitKey = v24;
    if ( v25 )
      (**v25)(v25, 1);
    *(_DWORD *)&v97[348] = 0;
    v26 = &v97[208];
  }
  */
  sub_D28150(v26);
}
/* Orphan comments:
Key Source FIRMKEY HIDDENDATA SECRETDATA
*/

参数等经过经过简单的处理后被传入do_crypt_job中

int __thiscall do_crypt_job(int this, int a2, struct_cmcrypt *algorithms, void *len, void *pdest_1)
{
  __int16 *v7; // ebp
  int v8; // ebp
  int v10; // [esp+14h] [ebp-4h]
  int v11; // [esp+1Ch] [ebp+4h]

  if ( a2 )
    v7 = *(__int16 **)(a2 + 380);
  else
    v7 = 0;
  v11 = sub_CAFC00(a2, (unsigned __int8 (__thiscall *)(int))sub_C5B150);
  sub_CAFC00(a2, (unsigned __int8 (__thiscall *)(int))sub_C5B140);
  v10 = *(_DWORD *)(v11 + 16);
  v8 = do_crypt_job_0(
         *(void **)(this + 244),
         *v7,
         *(_WORD *)(a2 + 4),
         algorithms,
         len,
         pdest_1,
         v7 + 8,
         (_DWORD *)(v11 + 16));
  if ( !v8 )
    return v8;
  sub_C58C50(v11);
  sub_C589C0();
  sub_C80590(a2, algorithms, this);
  if ( v10 != *(_DWORD *)(v11 + 16) )
    sub_C0DE40(2048);
  if ( !*(_BYTE *)(this + 241) && !*(_BYTE *)(this + 240) )
    return v8;
  if ( len )
    *(_WORD *)len = 0;
  *(_DWORD *)(*(_DWORD *)(this + 244) + 908) = 21;
  return 0;
}
int __thiscall do_crypt_job_0(
        void *this,
        __int16 a2,
        __int16 a3,
        struct_cmcrypt *algorithms,
        void *len,
        void *pdest_1,
        _WORD *a7,
        _DWORD *a8)
{
  int algorithms_1; // eax
  int v9; // esi
  struct_v18 *v10; // ecx
  unsigned int v11; // eax
  __int16 v12; // si
  __int16 v13; // cx
  __int16 cbDest; // ax
  unsigned int v15; // ecx
  struct_v18 *Block; // [esp+1Ch] [ebp-60h]
  struct_v18 *v18; // [esp+24h] [ebp-58h]
  unsigned int v19; // [esp+28h] [ebp-54h]
  int v21; // [esp+2Ch] [ebp-50h]
  void *Src; // [esp+34h] [ebp-48h]
  char v23; // [esp+3Bh] [ebp-41h]

  v23 = 0;
  (*(void (__thiscall **)(void *))(*(_DWORD *)this + 56))(this);
  algorithms_1 = algorithms->algorithms;
  v9 = algorithms->cbDest + 27;
  if ( (!algorithms->algorithms || algorithms_1 == 1 || algorithms_1 == 5)
    && (algorithms->mulSecondsSince01_01_2000 || (algorithms->mulEncryptionCodeOptions & 0xF00000) == 0x100000) )
  {
    v9 = algorithms->cbDest + 31;
    v23 = 1;
  }
  v19 = (v9 + 19) & 0xFFFFFFF0;
  if ( v19 )
  {
    v10 = (struct_v18 *)operator new[]((v9 + 19) & 0xFFFFFFF0);
    v11 = (v9 + 19) & 0xFFFFFFF0;
  }
  else
  {
    v10 = 0;
    v11 = 0;
  }
  Block = v10;
  if ( !v11 )
    v10 = 0;
  v18 = v10;
  *(_WORD *)v10->gap0 = v9;
  v12 = 1;
  v10->gap0[4] = 2;
  v13 = 1;
  if ( !BYTE2(algorithms->mulKeyExtType) )
    v13 = a3;
  if ( !BYTE2(algorithms->mulKeyExtType) )
    v12 = a2;
  v18->word5 = v12;
  v18->word7 = v13;
  v18->keySource = algorithms->keySource;
  if ( BYTE1(algorithms->mulKeyExtType) )
    v18->keySource |= 0x8000u;
  v18->mulKeyExtType = algorithms->mulKeyExtType;
  v18->algorithms = algorithms->algorithms;
  v18->mulFeatureCode = algorithms->mulFeatureCode;
  v18->mulEncryptionCode = algorithms->mulEncryptionCode;
  v18->mulEncryptionCodeOptions = algorithms->mulEncryptionCodeOptions;
  cbDest = algorithms->cbDest;
  v18->cbDest = cbDest;
  if ( v23 )
  {
    v18->keySource |= 0x4000u;
    v18->cbDest = cbDest + 4;
    *(_DWORD *)&v18->pbDest = algorithms->mulSecondsSince01_01_2000;
    memmove(&v18->pbDest1, pdest_1, algorithms->cbDest);
  }
  else
  {
    memmove(&v18->pbDest, pdest_1, algorithms->cbDest);
  }
  v15 = (algorithms->cbDest_ + 29) & 0xFFFFFFF0;
  Src = (void *)v15;
  if ( v15 )
  {
    if ( v15 > 0x7FFFFFFF )
      sub_BD9DE0();
    sub_BED070(v15);
    memset(0, 0, (size_t)Src);
    v15 = (unsigned int)Src;
  }
  if ( (*(int (__thiscall **)(void *, struct_v18 *, unsigned int, _DWORD, unsigned int, int, _DWORD, _DWORD, _DWORD, _DWORD))(*(_DWORD *)this + 12))(// cm_encrypt_alg
                                                // func_interested
         this,
         v18,
         v19,
         0,
         v15,
         0x400000,
         0,
         0,
         0,
         0) )
  {
    *a7 = MEMORY[6];
    *a8 = MEMORY[2];
    memmove(len, (const void *)0xA, algorithms->cbDest_);
    v21 = MEMORY[8];
  }
  else
  {
    v21 = 0;
  }
  if ( Block )
    j_j__free(Block);
  sub_BDCCA0();
  return v21;
}

do_crypt_job中做事不多,转交给do_crypt_job_0,再由do_crypt_job_0进行转发至相应的算法。

值得注意的是,在正常的请求下(mflCtrl为CM_CRYPT_AES,CM_CRYPT_ECIES等),会被转发到cm_encrypt_alg。服务端大多数注意安全性的算法都转发到这个函数进行加解密操作。这种正常的CmCrypt之后会在Wibu Codemeter 7.3学习笔记——Codemeter服务端上说

而对于mfCtrl = 0x6000000这个AxProtector壳特有的则被转发到func_interested(暂且这么命名)进行处理。

unsigned int __thiscall func_interested(
        _BYTE *this,
        struct_v18 *a2,
        size_t a3,
        void *a4,
        int a5,
        unsigned int a6,
        int a7,
        char a8,
        int a9,
        char a10)
{
  int v11; // eax
  unsigned int v12; // esi
  int v14; // [esp+0h] [ebp-3Ch] BYREF
  struct_v18 *v15; // [esp+10h] [ebp-2Ch]
  void *v16; // [esp+14h] [ebp-28h]
  int v17; // [esp+18h] [ebp-24h]
  _BYTE *v18; // [esp+1Ch] [ebp-20h]
  int v19; // [esp+20h] [ebp-1Ch] BYREF
  int v20[3]; // [esp+24h] [ebp-18h] BYREF
  int v21; // [esp+38h] [ebp-4h]

  v20[2] = (int)&v14;
  v15 = a2;
  v16 = a4;
  v18 = this;
  v17 = a9;
  if ( !a8 && !this[964] && !(*(unsigned __int8 (__thiscall **)(_BYTE *))(*(_DWORD *)this + 48))(this) )
  {
    v20[0] = 0;
    v11 = *(_DWORD *)this;
    v21 = 0;
    LOBYTE(v19) = 0;
    (*(void (__thiscall **)(_BYTE *, int *))(v11 + 32))(this, &v19);
    sub_D51FD0(v19);
    this[964] = 1;
    v21 = -1;
  }
  v20[0] = 0;
  sub_BE9A20((int)(this + 904));
  v21 = 2;
  v12 = func_1(this, v15, a3, v16, a5, a6, a7, a8, v17, a10);
  sub_BE9A90(v20);
  return v12;
}

而func_interested又转发到func_1(暂且这么命名),这个func_1便是真正做事的地方

(func_1挺大的,过几天更)

最后于 2023-1-9 00:47 被ericyudatou编辑 ,原因:
2023-1-9 00:08
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
8

func_1

大工程,慢慢更

对传入的pbDest下内存断点,断下三次后在对func_1复制过的pbDest下内存断点,跟踪,运行,这里显示的值是pbDest的第一位

在这次运行中,原始传入的40字节pbDest如下图蓝框中,第一位为0x6E

加密后的pbDest如下图,前是16为加密后的结果(红框),后面的则是原始pbDest填充,第一位为0x55

一眼丁真的发现(其实是经历很多测试),加密前后的pbDest是跟内存断点跟踪的结果是相同的,并且是有个这样的过程

0x6E(原始)-->0x64 --->0x59 -->0x55(结果)

大概这个算法分为三个阶段,并且不太可能使用太过复杂的算法

(未完待续)

最后于 2023-1-10 00:37 被ericyudatou编辑 ,原因:
2023-1-9 00:35
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
9

书接上回,通过内存断点发现真正做事的函数func_2,对于func_1和func_1是怎么调用到func_2不必深究,只需要知道ecx+399是pbDest,ecx+397是cbDest即可。整个函数挺大的,不方便贴伪代码贴全。分析可得,整个加密过程分为两轮。因为我也不是专业做逆向的,找不到趁手的跟踪方法,故只能边录屏边F8,视频放最后。

第一轮

         case 7:                               // axprotector  0x6000000 round 1
            p_pbDest_1 = pbDest;
            sub_7FA840(this, 0, 0);
            sub_753900(this->pvoid5E4, this->dword13E);
            sub_753800(this->pvoid5E4, this->arr.mulEncryptionCode);
            sub_753870(this->pvoid5E4, 2);
            nullsub_1(this->pvoid5E4);
            sub_753B00(this->pvoid5E4);
            nullsub_1(this->pvoid5E4);
            arg_interested = sub_7536C0(this->pvoid5E4);
            pbDest_1_1 = p_pbDest;
            *arg_interested = *p_pbDest;
            *(arg_interested + 1) = *(pbDest_1_1 + 4);
            *(arg_interested + 2) = this->dword144;
            *(arg_interested + 3) = this->dword13E;
            sub_7533E0(this->pvoid5E4, arg_interested);
            nullsub_1(this->pvoid5E4);
            nullsub_1(this->pvoid5E4);
            sub_7537E0(arg_interested);         // aes job
            sub_753720(this->pvoid5E4);
            crypt_job_1(this->pvoid5E4, p_pbDest + 8, len_4 - 8);// crypt job 1,进行加密   change round 1 9~40
            nullsub_1(this->pvoid5E4);
            pbDest_4 = p_pbDest;
            this->arr.keySource = *(p_pbDest + 4);  //准备下一轮参数
            this->arr.mulKeyExtType = *(pbDest_4 + 10);
            this->arr.algorithms = *(pbDest_4 + 11);
            this->arr.mulFeatureCode ^= *(pbDest_4 + 12);
            this->arr.mulEncryptionCode = *(pbDest_4 + 16);
            this->arr.mulEncryptionCodeOptions ^= *(pbDest_4 + 20);// init arg for round 2
            len_3 = cbDest_1;
            if ( v116 )
            {
              pbDest = &this->arr.pbDest1;
              cbDest_1 += 65496;
              memmove(&this->arr.pbDest1, (pbDest_4 + 40), (len_3 - 40));
              pbDest_5 = p_pbDest;
              p_pbDest = &this->arr.pbDest1;
              *&this->arr.pbDest = pbDest_5[6];
            }
            else
            {
              pbDest = &this->arr.pbDest;       // here
              cbDest_1 += 65512;
              p_pbDest = &this->arr.pbDest;
              memmove(&this->arr.pbDest, p_pbDest_1 + 24, (len_3 - 24));// changed 2 0~15
                                                // 把后16位复制到前16位
            }
            v131 = -1;                          // changed_1
            nullsub_1(&v121);
            v6 = cbDest_1;
            continue;                           // to round 2

简单的分析可知,round 1做的事就是通过crypt_job_1加密pbDest的9~40位,然后再将操作后的最后16位复制到前16位。并且准备下一轮的参数。

对于其中第一轮加密,首先对一个结构体(暂称之为round1aes1)前55位求SHA256散列值的前16位作为AES加密的密钥,再将pDest前8位拼接Firm Code,Product Code作为明文,进行AES_CBC_128的加密(iv = 0),其结果的前16位作为参数参与到crypt_job_1中

			struct round1aes1{
				DWORD constant1 = 0x77****E5;//<-----------与firm code相关
				DWORD constant2 = 0xB5****9F;
				DWORD constant3 = 0x66****C0;
				DWORD constant4 = 0x52****66;
				DWORD FirmCode = 0;
				DWORD ProductCode = 0;
				DWORD gap1 = 0;
				DWORD EncryptionCode = 0x1337;
				DWORD gap2 = 0;
				DWORD constant_round = 2;
				DWORD gap3 = 0;
				DWORD gap4 = 0;
				DWORD gap5 = 0;
				DWORD gap6 = 0x00000000;
			}round1aes1;

值得注意的是,对于同一个公司的产品(Firm Code相同),所对应的constant1~4相同,不随机器特征码改变,为常量。且不与Firm Code存在关系,为公司指定的(随机生成的)数据。不存在与客户端AxProtector程序上,只存在服务器Codemeter上。因此,对AxProtector保护的程序脱壳必须有一个合法的授权才能对程序进行解密

第一轮:crypt_job_1

void __thiscall crypt_job_1(void *this, _OWORD *pBuf, int len)
{
  char *v3; // ebp
  unsigned int v5; // ebx
  char *v6; // esi
  int v7; // ecx
  int v8; // edx
  char v9; // al
  char *lena; // [esp+Ch] [ebp+8h]

  if ( len )
  {
    v3 = this + 320;
    v5 = ((len - 1) >> 4) + 1;
    lena = this + 2422;
    do
    {
      v6 = sub_5A8310(v3);
      *v6 = *pBuf;
      sub_5A8350(v3);
      v7 = 16;
      v8 = lena - v6;
      do
      {
        v9 = (v6++)[v8];
        *(v6 - 1) ^= v9;
        v6[v8 - 1] = *pBuf;
        *pBuf = *(v6 - 1);
        pBuf = (pBuf + 1);
        --v7;
      }
      while ( v7 );
      --v5;
    }
    while ( v5 );
  }
}

crypt_job_1这个函数比较简单,不多赘述。传入pDest 9~40位(32)和上一步AES加密出来的前16位,简单的位运算,然后准备下一轮的参数,并将pDest最后16位复制到前16位。

            this->arr.keySource = *(p_pbDest + 4);  //准备下一轮参数
            this->arr.mulKeyExtType = *(pbDest_4 + 10);
            this->arr.algorithms = *(pbDest_4 + 11);
            this->arr.mulFeatureCode ^= *(pbDest_4 + 12);
            this->arr.mulEncryptionCode = *(pbDest_4 + 16);
            this->arr.mulEncryptionCodeOptions ^= *(pbDest_4 + 20);// init arg for round 1

视频

最后于 2023-1-25 03:13 被ericyudatou编辑 ,原因:
2023-1-16 00:23
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
10

第二轮

……
        if ( !gen_firm_product_code(this, this->arr.word7, this->arr.keySource) )// set seed1 seed2
                                                // 
                                                // seed1 4C4D0Eh 来自firm item
                                                // seed2 100000h
……
LABEL_101:
            gen_aes_constant(this, 0, flag_sd_b_1 ? 59 : 0);// round 2
            set_aes_productcode(*(void **)((char *)&this->aes_job + 2), this->produccode);// product code
            set_aes_featurcode(
              *(void **)((char *)&this->aes_job + 2),
              *(_DWORD *)((char *)&this->arr.mulFeatureCode + 2));
            set_aes_encryptioncode(
              *(void **)((char *)&this->aes_job + 2),
              *(_DWORD *)((char *)&this->mulEncryptionCode + 2));
            set_aes_encryptioncodeoption(
              *(void **)((char *)&this->aes_job + 2),
              *(_DWORD *)((char *)&this->mulEncryptionCodeOptions + 2));
            if ( flag_sd_b_1 )                  // round2 0
              sub_C33950(*(int **)((char *)&this->aes_job + 2), *(_DWORD *)&this->arr.gap1C[1]);
            if ( this->algorithms == 5 )
              set_aes_type_constant(*(void **)((char *)&this->aes_job + 2), 1);
            v32 = *(void **)((char *)&this->aes_job + 2);
            if ( *(__int16 *)&this->arr.mulKeyExtType >= 0 )
            {
              nullsub_1(v32);
              aes_job_1 = *(BYTE **)((char *)&this->aes_job + 2);
              if ( algorithms_3 )
              {
                if ( this->algorithms == 5 )
                  doAesKeySHA_0(aes_job_1, this->constant_round2aes1);
                else
                  doAesKeySHA(aes_job_1);
              }
              else
              {
                doAesKey(aes_job_1);
              }
              if ( (*(_DWORD *)((_BYTE *)&this->mulEncryptionCodeOptions + 2) & 0x200000) != 0 )
              {
                v35 = *(void **)((char *)&this->aes_job + 2);
                this->word4E = 40;
                nullsub_1(v35);
                nullsub_1(&v122);
                return;
              }
              if ( this->algorithms == 5 && func_key_changed_3(pbDest, (int)this->constant_round2aes1, 16) )
              {
                *(_WORD *)this->gap166 = 0;
                gen_rand_arr(pbDest, 0x10u);    // fuck up
              }
              func2_aes_encrypt(*(char **)((char *)&this->aes_job + 2), pbDest);
              nullsub_1(*(void **)((char *)&this->aes_job + 2));
            }
            else
            {
              nullsub_1(v32);
              aes_job_2 = *(int (__cdecl ***)(int, int, int, int, int))((char *)&this->aes_job + 2);
              if ( algorithms_3 )
              {
                if ( this->algorithms == 5 )
                  doAesKeySHA_2((int)aes_job_2, this->constant_round2aes1);// here
                else
                  doAesKeySHA_1((int)aes_job_2);
              }
              else
              {
                sub_C33AC0(aes_job_2);
              }
              func_key_changed_2(*(char **)((char *)&this->aes_job + 2), pbDest);// round2 key change 1 前16位
              nullsub_1(*(void **)((char *)&this->aes_job + 2));
              if ( this->algorithms == 5 && func_key_changed_3(pbDest, (int)this->constant_round2aes1, 16) )
              {
                *(_WORD *)this->gap166 = 0;
                gen_rand_arr(pbDest, 0x10u);    // fuck up
              }
            }
            if ( algorithms_org == 7 )
            {
              key2 = sub_C336C0(*(char **)((char *)&this->aes_job + 2));// final
              nullsub_1(*(void **)((char *)&this->aes_job + 2));
              set_aes_key(*(int (__cdecl ***)(int, int, int, int, int))((char *)&this->aes_job + 2), (int)key2);
              nullsub_1(*(void **)((char *)&this->aes_job + 2));
              pbDest = p_pbDest;
              func_key_changed_2(*(char **)((char *)&this->aes_job + 2), p_pbDest);// round2 key change 2
              nullsub_1(*(void **)((char *)&this->aes_job + 2));
            }
 crypt_job_2(this, pbDest, *(unsigned __int16 *)len_5);
 sub_CD8B20(this);
 LOBYTE(this->aes_job) = 0;
 return;

crypt_job_2并没有参与到加密数据的加密过程中,故不研究

首先还是声明一个结构体round2aes1,对其前55位求SHA256散列值,取散列值前16位作为第一次AES解密的密钥

接着是对pDest前16位进行第一次AES_CBC_128解密(iv=0),密钥为散列值前16位。

坠吼是对pDest前16位进行第一次AES_CBC_128解密(iv=0),密钥为第一轮第一次AES加密所生成的密文的前16位。

此时pDest的前16位就是我们想要的数据了。

			struct round2aes1 {
				DWORD constant1 = 0x4E****74; //CmAct私钥
				DWORD constant2 = 0x4E****7C;
				DWORD constant3 = 0x78****C1;
				DWORD constant4 = 0xFE****22;
				DWORD FirmCode = 0;
				DWORD ProductCode = 0;
				DWORD FeatureCode = 0;
				DWORD EncryptionCode = 0;
				DWORD EncryptionCodeOption = 0;
				DWORD constant_round = 1;
				DWORD gap3 = 0;
				DWORD gap4 = 0;
				DWORD gap5 = 0;
				DWORD gap6 = 0x80000000;
			}round2aes1;

需要注意的是这里的FirmCode,Product Code等参数都是经过第一轮加密变换的。

值得注意的是,对于同一个公司的同一个产品(Firm Code,Product Code相同),所对应的constant1~4相同,为这个产品的ECDSA私钥前16位。

AxProtectot-Codemeter保护系统的强度不体现在其纸老虎似的ECDSA,唬人的反调试,哈人的"毁狗",还是那略显迷惑的通信算法,而是在于公司的私钥和产品的私钥。

这两个私钥不仅仅参与到他们所对应的非对称加密算法(ECDSA),还参与到普通的对称加密算法,可执行文件的解密过程中,使得没有可用授权的破解变为不可能,破解这个AxProtectot-Codemeter保护系统必须得有一个可用的授权

附加一份加密过程的日志

[*]Client encrypted package
[*]Encrypted package decrypt:SUCCESS,len:204,time:47754
[*]Function Request:CmCrypt2
[*]HCMSE 2,flCtrl 0,mflCtrl 06000000,feature code 2924a1dd,crc 0,mulEncryptionCode 00001337 mulEncryptionCodeOptions 51023a51
[*]pDest:(40)
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffc2 ffffffe1 ffffffd4 34 30 47 ffffffa9 ffffff94 ffffff99 40 ffffffe2 26 ffffffd3 ffffffcd 75 ffffffcb
[*]ffffffe3 ffffff96 ffffffe0 67 0d ffffffc2 21 ffffffba 23 44 fffffff6 ffffff95 0b 7c ffffffa2 ffffffb4
[*]3f ffffff91 72 25 4b 7a 28 ffffffd1
[*]----------------------------------------------END------------------------------------------------------
[*]InitKey:(16)
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]
[*]----------------------------------------------END------------------------------------------------------
[*]DirectAesKey:(16)
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]
[*]----------------------------------------------END------------------------------------------------------
[*]Mode:Direct encryption
[*]Key Source: Firmware Key
[*]Encryption Algorithm: Unknown 100663296
[*][CmCrypt2]AxProtectot CmCrypt 0x6000000 function start,Firm Code 5000462,Product Code 3
[*][CmCrypt2]AxProtectot C6F Round 1 AES JOB 1 Start
[*][CmCrypt2]AxProtectot C6F Round 1 AES JOB 1 SHA256
[*][CmCrypt2]AxProtectot C6F Round 1 AES JOB 1 AES key:
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]3e fffffffb 7d 2e ffffff95 21 ffffff96 7d ffffffa5 1e ffffff82 ffffffa5 ffffffdc 5a 5f 4b
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 AES JOB 1 AES plain text:
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffc2 ffffffe1 ffffffd4 34 30 47 ffffffa9 ffffff94 0e 4d 4c 00 03 00 00 00
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 AES JOB 1 AES result(Also Round 1 AES JOB 2 Key):
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffc0 69 ffffffe1 ffffffa2 ffffffd1 ffffffa6 51 ffffffc9 16 ffffffb2 68 58 41 ffffffa4 0c 59
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 start
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Start
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffff99 40 ffffffe2 26 ffffffd3 ffffffcd 75 ffffffcb ffffffe3 ffffff96 ffffffe0 67 0d ffffffc2 21 ffffffba
[*]23 44 fffffff6 ffffff95 0b 7c ffffffa2 ffffffb4 3f ffffff91 72 25 4b 7a 28 ffffffd1
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 1 AES reslut->pplaintext
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]00 ffffff80 00 05 ffffffdd ffffffa3 24 29 11 ffffff84 6b 36 51 3a 00 51
[*]00 ffffff80 00 05 ffffffdd ffffffa3 24 29 11 ffffff84 6b 36 51 3a 00 51
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 1 Result->pInBufOrg
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]00 ffffff80 00 05 ffffffdd ffffffa3 24 29 11 ffffff84 6b 36 51 3a 00 51
[*]23 44 fffffff6 ffffff95 0b 7c ffffffa2 ffffffb4 3f ffffff91 72 25 4b 7a 28 ffffffd1
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 1 Result->pplaintext
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]00 ffffff80 00 05 ffffffdd ffffffa3 24 29 11 ffffff84 6b 36 51 3a 00 51
[*]45 ffffffe8 ffffff9b ffffffd6 08 fffffffb 2c 03 ffffffd5 ffffff83 1d 00 fffffff4 fffffffa 2c 03
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 1 Result->pbuf
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffff99 40 ffffffe2 26 ffffffd3 ffffffcd 75 ffffffcb ffffffe3 ffffff96 ffffffe0 67 0d ffffffc2 21 ffffffba
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 2 AES reslut->pplaintext
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffc9 0b 32 ffffff92 27 47 71 54 ffffffd4 ffffffc0 26 ffffff8a 7a 78 ffffffc6 46
[*]ffffffc9 0b 32 ffffff92 27 47 71 54 ffffffd4 ffffffc0 26 ffffff8a 7a 78 ffffffc6 46
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 2 Result->pInBufOrg
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]00 ffffff80 00 05 ffffffdd ffffffa3 24 29 11 ffffff84 6b 36 51 3a 00 51
[*]50 4b ffffffd0 ffffffb4 fffffff4 ffffff8a 04 ffffff9f 37 56 ffffffc6 ffffffed 77 ffffffba ffffffe7 fffffffc
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 2 Result->pplaintext
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]45 ffffffe8 ffffff9b ffffffd6 08 fffffffb 2c 03 ffffffd5 ffffff83 1d 00 fffffff4 fffffffa 2c 03
[*]7c fffffff2 2c 03 20 00 00 00 10 41 1d 00 10 41 1d 00
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 Round 2 Result->pbuf
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]23 44 fffffff6 ffffff95 0b 7c ffffffa2 ffffffb4 3f ffffff91 72 25 4b 7a 28 ffffffd1
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 crypt_job_1 done!result:
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffc2 ffffffe1 ffffffd4 34 30 47 ffffffa9 ffffff94 00 ffffff80 00 05 ffffffdd ffffffa3 24 29
[*]11 ffffff84 6b 36 51 3a 00 51 50 4b ffffffd0 ffffffb4 fffffff4 ffffff8a 04 ffffff9f
[*]37 56 ffffffc6 ffffffed 77 ffffffba ffffffe7 fffffffc
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 1 result:
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]50 4b ffffffd0 ffffffb4 fffffff4 ffffff8a 04 ffffff9f 37 56 ffffffc6 ffffffed 77 ffffffba ffffffe7 fffffffc
[*]11 ffffff84 6b 36 51 3a 00 51 50 4b ffffffd0 ffffffb4 fffffff4 ffffff8a 04 ffffff9f
[*]37 56 ffffffc6 ffffffed 77 ffffffba ffffffe7 fffffffc
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 2 Start,keySource 8000 KeyExtType 00 FeatureCode 200 EncryptionCode 366b8411 EncryptionCodeOptions 20000 alogrithm 05
[*][CmCrypt2]AxProtectot C6F Round 2 Serving Gasturb Smooth C
[*][CmCrypt2]AxProtectot C6F Round 2 AES JOB 1 SHA256
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffcc ffffff8e 62 60 ffffffae ffffff8c ffffffad ffffff91 ffffffca 3b ffffffbd ffffffa8 fffffff4 7d 31 ffffffa5
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 2 AES JOB 1 pDest (16):
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]50 4b ffffffd0 ffffffb4 fffffff4 ffffff8a 04 ffffff9f 37 56 ffffffc6 ffffffed 77 ffffffba ffffffe7 fffffffc
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 2 AES JOB 1 Result:
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]17 fffffff0 7c ffffff99 ffffffca ffffffca ffffffbf 25 10 54 ffffffd0 46 ffffffe4 ffffffa2 63 ffffff94
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 2 AES JOB 2 Key:
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffc0 69 ffffffe1 ffffffa2 ffffffd1 ffffffa6 51 ffffffc9 16 ffffffb2 68 58 41 ffffffa4 0c 59
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 2 AES JOB 2 pDest (16):
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]17 fffffff0 7c ffffff99 ffffffca ffffffca ffffffbf 25 10 54 ffffffd0 46 ffffffe4 ffffffa2 63 ffffff94
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmCrypt2]AxProtectot C6F Round 2 AES JOB 2 Result (16),AxProtectot C6F END,16 byte payload:
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]ffffffd8 ffffffc9 2c 56 61 ffffff90 ffffffbd ffffff94 0f 1c 64 4c 5f ffffff80 21 ffffffa2
[*]
[*]----------------------------------------------END------------------------------------------------------
[*][CmSendMessage]ENCRYPT SUCCESS
[*][CmSendMessage]Message send(196):
[*]-------------------------------------------PRINT HEX---------------------------------------------------
[*]00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[*]-------------------------------------------------------------------------------------------------------
[*]00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*]00 00 00 00 00 00 00 00 ffffffd8 ffffffc9 2c 56 61 ffffff90 ffffffbd ffffff94
[*]0f 1c 64 4c 5f ffffff80 21 ffffffa2 ffffffe3 ffffff96 ffffffe0 67 0d ffffffc2 21 ffffffba
[*]23 44 fffffff6 ffffff95 0b 7c ffffffa2 ffffffb4 3f ffffff91 72 25 4b 7a 28 ffffffd1
[*]00 00 00 00
[*]----------------------------------------------END------------------------------------------------------


最后于 2023-1-25 09:20 被ericyudatou编辑 ,原因:
2023-1-25 09:13
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
11
吃羊杂去了,晚上更过自校验
2023-1-25 11:00
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
12

自校验

在实现了CmCrypt2 Axprotector 0x6000000之后,理论上程序可以运行。但一旦设了(软)断点或者是修改了代码,程序就跑不起来,报错说代码完整性检查失败。

随便在代码段设个硬件断点

发现断在memmove,简单的看了一下,发现调用链如下

doMemCheck->memcheck_doSha_0->memcheck_doSha->sha256_update->memmove

bool __thiscall doMemCheck(int this, int a2, unsigned __int8 *a3, int a4, int a5, int a6, int a7)
{
  unsigned __int8 *v7; // esi
  _DWORD *v10; // edx
  unsigned int v11; // esi
  int *v12; // ecx
  bool v13; // cf
  bool v14; // bl
  _DWORD *v15; // [esp+Ch] [ebp-90h]
  int v16[4]; // [esp+18h] [ebp-84h] BYREF
  __int128 v17; // [esp+28h] [ebp-74h]
  char v18[96]; // [esp+38h] [ebp-64h] BYREF

  v7 = a3;
  if ( !a4 || !(unsigned __int8)sub_A01D00(a2, a4) )
    return 0;
  v15 = (_DWORD *)(this + 88);
  if ( (unsigned __int8)memcheck_doSha_0(a5, a6, this + 88) )
  {
    *(_OWORD *)v16 = 0i64;
    v17 = 0i64;
    sub_B13040(this + 88, v16);
    *(_DWORD *)(this + 92) = *(_DWORD *)(this + 88);
    if ( *(_BYTE *)(this + 4) )
    {
      memset(v18, 0, sizeof(v18));
      if ( !a3 )
        v7 = (unsigned __int8 *)(this + 5);
      v14 = checkECDSASignature(v18, v7, (unsigned __int8 *)(a4 + 64), (unsigned __int8 *)v16);
    }
    else
    {
      v10 = (_DWORD *)(a4 + 128);
      v11 = 28;
      v12 = v16;
      while ( *v12 == *v10 )
      {
        ++v12;
        ++v10;
        v13 = v11 < 4;
        v11 -= 4;
        if ( v13 )
        {
          v14 = 1;
          goto LABEL_15;
        }
      }
      v14 = 0;
    }
LABEL_15:
    LOBYTE(v15) = v14;
    nullsub_6(a7, a5, a6, v15, a4 + 128, v16);
    return v14;
  }
  else
  {
    *(_DWORD *)(this + 92) = *v15;
    return 0;
  }
}
char __thiscall memcheck_doSha_0(_DWORD *this, int a2, int a3, _DWORD *a4)
{
  int v5; // ebx
  unsigned int v6; // ecx
  unsigned int v7; // eax
  int v8; // edi
  _OWORD *v9; // eax
  __int128 v11; // [esp+18h] [ebp-94h] BYREF
  __int128 v12; // [esp+28h] [ebp-84h]
  char v13[112]; // [esp+38h] [ebp-74h] BYREF

  memset(v13, 0, sizeof(v13));
  init_sha256((int)v13);
  v5 = this[4];
  v6 = (v5 - this[3]) >> 4;
  v7 = (a4[2] - *a4) >> 5;
  v11 = 0i64;
  v12 = 0i64;
  if ( v6 > v7 )
  {
    if ( v6 > 0x7FFFFFF )
      sub_9FAD60();
    sub_9FA900(v6);
    v5 = this[4];
  }
  v8 = this[3];
  if ( v8 == v5 )
    return 1;
  while ( 1 )
  {
    init_sha256((int)v13);
    if ( !(*(unsigned __int8 (__thiscall **)(_DWORD *, int, int, int, char *, _DWORD, _DWORD))(*this + 28))(
            this,
            v8,
            a2,
            a3,
            v13,
            0,
            0) )
      break;
    sha256_final((BYTE *)&v11, (int)v13);
    v9 = (_OWORD *)a4[1];
    if ( v9 == (_OWORD *)a4[2] )
    {
      sub_B12D10(v9, &v11);
    }
    else
    {
      *v9 = v11;
      v9[1] = v12;
      a4[1] += 32;
    }
    v8 += 16;
    if ( v8 == v5 )
      return 1;
  }
  return 0;
}
char __thiscall memcheck_doSha(_DWORD *this, int a2, __int64 a3, int *a4, int a5, int a6)
{
  int *v7; // ecx
  int v8; // ebp
  unsigned int v9; // edi
  int (__thiscall *v10)(_DWORD *); // esi
  unsigned int v11; // ebx
  unsigned int v12; // edi
  _QWORD *v13; // esi
  unsigned int v14; // ebx
  unsigned int v15; // edx
  unsigned int v16; // ebp
  int (__thiscall *v17)(_DWORD *); // edi
  int v18; // eax
  int (__thiscall *v19)(_DWORD *); // edi
  int (__thiscall *v20)(_DWORD *); // edi
  int v21; // eax
  int (__thiscall *v22)(_DWORD *); // edi
  unsigned int v23; // edi
  int (__thiscall *v24)(_DWORD *); // esi
  BYTE *v25; // eax
  char result; // al
  int (__thiscall *v27)(_DWORD *); // esi
  BYTE *v28; // [esp-10h] [ebp-3Ch]
  unsigned int v29; // [esp-Ch] [ebp-38h]
  unsigned int v30; // [esp+8h] [ebp-24h]
  _DWORD *v31; // [esp+Ch] [ebp-20h]
  int *v32; // [esp+18h] [ebp-14h]
  int v33; // [esp+1Ch] [ebp-10h] BYREF
  __int64 v34; // [esp+20h] [ebp-Ch] BYREF

  v7 = &dword_B92C80;
  v8 = a2;
  if ( this[14] )
    v7 = (int *)this[14];
  v31 = this;
  v32 = v7;
  if ( *v7 == v7[1] )
  {
    v9 = *(_DWORD *)(a2 + 12);
    v10 = *(int (__thiscall **)(_DWORD *))(*this + 8);
    nullsub_1();
    v28 = (BYTE *)(*(_DWORD *)(a2 + 4) + v10(v31));
    sha256_update(a4, v28, v9);
    return 1;
  }
  v11 = *(_DWORD *)(a2 + 4);
  v33 = 0;
  v12 = v11;
  v34 = 0i64;
  v13 = (_QWORD *)*v7;
  v30 = v11;
  if ( *v7 == v7[1] )
  {
LABEL_15:
    v23 = *(_DWORD *)(v8 + 12) + v12;
    if ( v23 <= v11 )
      return 1;
    v24 = *(int (__thiscall **)(_DWORD *))(*this + 8);
    nullsub_1();
    v25 = (BYTE *)(v11 + v24(v31));
    v29 = v23 - v11;
LABEL_17:
    sha256_update(a4, v25, v29);
    return 1;
  }
  while ( 1 )
  {
    v14 = (*v13 >> 4) ^ ((unsigned __int16)*(_DWORD *)v13 ^ (unsigned __int16)(*v13 >> 4)) & 0xFFF;
    v15 = *(_DWORD *)(v8 + 4);
    if ( v14 >= v15 )
      break;
LABEL_12:
    v11 = v30;
LABEL_13:
    if ( ++v13 == (_QWORD *)v32[1] )
    {
      v12 = *(_DWORD *)(v8 + 4);
      this = v31;
      goto LABEL_15;
    }
  }
  v16 = v15 + *(_DWORD *)(v8 + 12);
  if ( v14 >= v16 )
  {
    if ( v16 <= v30 )
      return 1;
    v27 = *(int (__thiscall **)(_DWORD *))(*v31 + 8);
    nullsub_1();
    v25 = (BYTE *)(v30 + v27(v31));
    v29 = v16 - v30;
    goto LABEL_17;
  }
  switch ( (*v13 >> 12) & 0xF )
  {
    case 0i64:
    case 1i64:
    case 2i64:
    case 4i64:
    case 9i64:
      v8 = a2;
      goto LABEL_12;
    case 3i64:
      v17 = *(int (__thiscall **)(_DWORD *))(*v31 + 8);
      nullsub_1();
      v18 = v17(v31);
      sha256_update(a4, (BYTE *)(v30 + v18), v14 - v30);
      v19 = *(int (__thiscall **)(_DWORD *))(*v31 + 8);
      nullsub_1();
      v33 = a3 + *(_DWORD *)(v19(v31) + v14);
      sha256_update(a4, (BYTE *)&v33, 4u);
      v8 = a2;
      v11 = v14 + 4;
      v30 = v11;
      goto LABEL_13;
    case 0xAi64:
      v20 = *(int (__thiscall **)(_DWORD *))(*v31 + 8);
      nullsub_1();
      v21 = v20(v31);
      sha256_update(a4, (BYTE *)(v30 + v21), v14 - v30);
      v22 = *(int (__thiscall **)(_DWORD *))(*v31 + 8);
      nullsub_1();
      v34 = *(_QWORD *)(v22(v31) + v14) + a3;
      sha256_update(a4, (BYTE *)&v34, 8u);
      v8 = a2;
      v11 = v14 + 8;
      v30 = v11;
      goto LABEL_13;
    default:
      result = 0;
      break;
  }
  return result;
}

发现其对代码段分段求SHA256散列值,再将上一步的散列值再进行一次SHA256,然后用ECDSA验证签名

定位方法:

查看checkECDSASignature串式参考,就两个引用。除开CmValiateSignature以为另外一个就是doMemCheck

和谐方法:

把最后的堆栈检查函数改成mov   eax,1

2023-1-26 11:59
0
雪    币: 101
活跃值: (97)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
硬件狗内的授权可以提取吗?实现脱狗行。
2023-1-27 21:05
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
14
CHYX 硬件狗内的授权可以提取吗?实现脱狗行。
理论上可行 我手头没狗做不了测试 但有那两组秘钥就应该能做
2023-1-27 21:51
0
雪    币: 101
活跃值: (97)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
是checkECDSASignature的密钥,还是,我感觉这里可以伪造一个,暴破checkECDSASignature应该就可以过了。对于后面的自校验,感觉应该是针对调试器而引起的软件不能正常开启吧。
2023-1-28 08:18
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
16
CHYX 是checkECDSASignature的密钥,还是,我感觉这里可以伪造一个,暴破checkECDSASignature应该就可以过了。对于后面的自校验,感觉应该是针对调试器而引起的软件不能正常开启吧 ...
对,理论上把checkECDSASignature这个函数伪造了就可以无补丁运行,但我是懒狗,实现有、麻烦
2023-1-28 08:39
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
17
调试器不下断点就没事,下了断点解完密就自杀
2023-1-28 08:40
0
雪    币: 12
活跃值: (671)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
大神,能否分享一下您修改的Wireshark
2023-1-28 11:13
0
雪    币: 101
活跃值: (97)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
谢谢,估计如果脱壳的话,还有很常的路要走。
2023-1-28 14:40
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
20

想要研究CodeMeter的话可以连上ookkaa 搭建的lumina服务器

分享一个搭好的ida lumina服务器
pull request,我把Codemeter 7.2的分析结果都推上去了

2023-1-28 18:35
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
21

2023年2月5日更新


上文的方法只适用于CmActLicense,即firm code 5000xxxx的,而对于用狗的CmDongle则是另外一套的授权系统

CmDongle所对应的CmCrypt2 0x6000000算法跟CmActLicense有所不同,提供一下目前的线索

  1. 没有调用func2,致使第一组key没有暴露在内存中(暂时未发现)

  2. 在func1讲参数传递给狗,狗的结果解密后便是pbDest

  3. 第二组私钥key会在CmCalculateSignature时暴露在内存中

  4. 因为AxProtector壳在解密中只有一套算法,没有区分CmDongle和CmActLicense,所以可以大胆推测狗里头的算法跟上文提到的func2中的算法大差不差

2023-2-5 13:42
0
雪    币: 1189
活跃值: (4817)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
22

对于用AxProtector保护的.exe .dll(不是.NET)程序的完整dump方法

在源程序未用IxProtector保护的情况下,所有被加密的部分全都在启动后解密完成

  1. 过掉反调试
    a.ScyllaHide全开即可

    b.下任何断点都需要下成硬件断点,AxProtector没有对硬件断点所需要的寄存器进行检查,但是会对软件断点进行检查,用硬件断点即可完美解决这一问题

    c.关于AxProtector杀狗

       只要不瞎搞,这个保护也形同虚设

    已知的两种会杀狗的可能

    1.CmCrypt2 0x6000000未返回正确的授权
    2.调试器被查到
    杀狗则是通过CmControl这个API进行的这个API没有进行文档化,对此知之甚少,可以通过tcp环路过滤掉这个API(TODO)

  2. Dump

    exe

    找到壳的入口点 axengine_entry,从入口点开始找,找到最大的一个函数即可,可以通过壳的数据段解密的key和api来进行定位,确认

    有两处代码会识别不出来

  3. __wibu04:00475EA5                 jnz     short loc_475ED1
    __wibu04:00475EA7                 mov     eax, ds:dword_623B60
    __wibu04:00475EAC                 add     eax, 0F8h
    __wibu04:00475EB1                 mov     ecx, eax
    __wibu04:00475EB3                 mov     [ebp+var_58C], eax
    __wibu04:00475EB9                 call    sub_45E1A0
    __wibu04:00475EB9 ; ---------------------------------------------------------------------------
    __wibu04:00475EBE                 db 0FFh         ;<----------此处
    __wibu04:00475EBF                 db 0FFh
    __wibu04:00475EC0                 db  8Bh
    __wibu04:00475EC1                 db  8Dh
    __wibu04:00475EC2                 db  74h ; t
    __wibu04:00475EC3                 db 0FAh
    __wibu04:00475EC4                 db 0FFh
    __wibu04:00475EC5                 db 0FFh
    __wibu04:00475EC6 ; ---------------------------------------------------------------------------
    __wibu04:00475EC6                 call    sub_45E230
    __wibu04:00475ECB                 mov     edi, [ebp+var_598]
    __wibu04:00475ED1
    __wibu04:00475ED1 loc_475ED1:                             ; CODE XREF: axengine_entry+4D5↑j
    __wibu04:00475ED1                 push    offset Address
    __wibu04:00475ED6                 mov     ecx, edi
    __wibu04:00475ED8                 call    sub_46D490
    __wibu04:00475EDD                 test    al, al
    __wibu04:00475EDF                 jnz     loc_475FD2
    .........
    __wibu04:00475EA7                 mov     eax, ds:dword_623B60
    __wibu04:00475EAC                 add     eax, 0F8h
    __wibu04:00475EB1                 mov     ecx, eax
    __wibu04:00475EB3                 mov     [ebp+var_58C], eax
    __wibu04:00475EB9                 call    sub_45E1A0
    __wibu04:00475EB9 ; ---------------------------------------------------------------------------
    __wibu04:00475EBE                 db 0FFh       ;<----------此处
    __wibu04:00475EBF                 db 0FFh
    __wibu04:00475EC0                 db  8Bh
    __wibu04:00475EC1                 db  8Dh
    __wibu04:00475EC2                 db  74h ; t
    __wibu04:00475EC3                 db 0FAh
    __wibu04:00475EC4                 db 0FFh
    __wibu04:00475EC5                 db 0FFh
    __wibu04:00475EC6 ; ---------------------------------------------------------------------------
    __wibu04:00475EC6                 call    sub_45E230
    __wibu04:00475ECB                 mov     edi, [ebp+var_598]

    那两个0xFF是用不可识别指令异常来反调试的,调试的时候爆这两处的异常必须交由应用程序处理

  4. 从axengine_entry往下找,找到in      ax, 64h 
    在此处下硬件断点。在这里执行特权指令,通过SEH跳到程序真正的入口点。断下之后就可以对源程序进行dump

2023-2-5 14:17
0
雪    币: 179
活跃值: (380)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
楼主可以提供下,壳的程序?
2023-2-6 18:19
0
雪    币: 6360
活跃值: (1502)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
狗壳的CmCrypt2 0x6000000 有没有办法计算出正确的cbDest,或者壳内有没有可以Patch的地方呢
00 00 00 06 00 00 00 00 37 13 00 00 68 e6 02 b5 6c 0e 0b e5 00 00 00 00 00 00 00 00
pvDest=
bc 8d bc 56 55 6c 71 76 b5 78 11 ea 2a 3a b9 7e 26 e0 cd 0e b5 ff dd ce f6 09 3f b7 53 c5 78 e4 a3 71 30 36 e5 8c e3 ab 
cbDest=  
a9 13 33 07 53 b1 35 24 9e d2 ef 02 8c 2d 4e c8 26 e0 cd 0e b5 ff dd ce f6 09 3f b7 53 c5 78 e4 a3 71 30 36 e5 8c e3 ab
计算结果是前10,不过,转了几次,都没有找到大佬说的解码的玩意,还没玩懂
2023-3-12 18:41
0
雪    币: 74
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
大神,找到壳的入口点 axengine_entry呢?
2023-6-21 20:23
0
游客
登录 | 注册 方可回帖
返回
//