0
期待完整版
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再来找她
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编辑 ,原因:
0
高手,明白一下大神