[原创]Wibu Codemeter 7.3学习笔记——AxProtector壳初探
2023-1-1 15:8:0 Author: bbs.pediy.com(查看原文) 阅读量:12 收藏

活跃值: 活跃值 (44892)

能力值:

(RANK:115 )

在线值:

2

0

期待完整版

活跃值: 活跃值 (2735)

能力值:

( LV5,RANK:60 )

在线值:

3

0

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再来找她

活跃值: 活跃值 (2735)

能力值:

( LV5,RANK:60 )

在线值:

4

0

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即可

最后于 3天前 被ericyudatou编辑 ,原因:

活跃值: 活跃值 (1073)

能力值:

( LV2,RANK:10 )

在线值:

5

0

高手,明白一下大神


文章来源: https://bbs.pediy.com/thread-275690.htm
如有侵权请联系:admin#unsafe.sh