本文为看雪论坛优秀文章
看雪论坛作者ID:ericyudatou
一
准备
CodeMeter.exe Win32 v7.30
IDA Pro
二
初见——反调试
三
初见——从何入手(通信协议部分)
char __thiscall decrypt_package(void *this, int a2, unsigned __int8 a3, int pbuf, _BYTE *plen, int flag)
{
char v6; // dl
char result; // al
v6 = 1;
if ( a3 == 160 )
{
if ( (*plen & 0xF) != 0 ) // len 应当是16的倍数
return 0;
return (*(int (__fastcall **)(void *, char, int, _BYTE *, int))(*(_DWORD *)this + 16))(this, 1, pbuf, plen, flag);// decrypt_telegram
}
else if ( a3 == 161 || a3 == 163 )
{
if ( (*plen & 0xF) == 0 )
{
result = (*(int (__fastcall **)(int, char, int, _BYTE *, _DWORD))(*(_DWORD *)a2 + 16))(a2, 1, pbuf, plen, 0);
*(_DWORD *)plen = *(_DWORD *)(*(_DWORD *)plen + pbuf - 4);
return result;
}
return 0;
}
return v6;
}
char __thiscall encrypt_package(struct_communication *this, int a2, unsigned __int8 a3, int buf, int *length, int flag)
{
char result; // al
int v8; // ecx
int v9; // eax
char v10; // bl
int v11; // [esp+10h] [ebp-18h] BYREF
int v12[5]; // [esp+14h] [ebp-14h] BYREF
v12[0] = a2;
result = 1;
if ( a3 == 160 )
{
v12[0] = 0;
sub_219A20(dword_800FC4 + 268);
v9 = *(_DWORD *)this->gap0;
v12[4] = 0;
v10 = (*(int (__thiscall **)(struct_communication *, int, int *, int))(v9 + 12))(this, buf, length, flag);// encrypt_telegram
sub_219A90(v12);
return v10;
}
else if ( a3 == 161 || a3 == 163 )
{
v11 = *length;
sub_3578E0(buf, length, 0);
v8 = *length;
*(_DWORD *)(buf + v8 - 8) = v11;
v11 = v8 - 4;
return (*(int (__thiscall **)(int, int, int *, _DWORD))(*(_DWORD *)v12[0] + 12))(v12[0], buf, &v11, 0);
}
return result;
}
void __thiscall cm_client_encrypt_req(
_DWORD *this,
_DWORD *message_buf,
unsigned __int8 a3,
unsigned int final_len,
unsigned int a5)
{
int v6; // ecx
unsigned int v7; // eax
size_t v8; // esi
void *v9; // eax
size_t v10; // ecx
size_t v11; // eax
size_t v12; // esi
_BYTE *buf_1; // esi
int v14; // ecx
int v15; // ecx
size_t v16; // edx
int v17; // eax
int v18; // ecx
char v19; // al
int v20; // ecx
int v21; // ecx
_DWORD *v22; // ecx
unsigned int v23; // edx
_BYTE *v24; // eax
void *p_Block; // edx
_DWORD *v26; // eax
_DWORD *v27; // esi
void *v28; // eax
int v29; // eax
int v30; // eax
int v31; // eax
int v32; // eax
int v33; // eax
int v34; // eax
int v35; // [esp-10h] [ebp-298h]
int v36; // [esp-10h] [ebp-298h]
int v37; // [esp-Ch] [ebp-294h]
int v38; // [esp-Ch] [ebp-294h]
int v39; // [esp-8h] [ebp-290h]
int v40; // [esp-8h] [ebp-290h]
int v41; // [esp-4h] [ebp-28Ch]
int v42; // [esp-4h] [ebp-28Ch]
int v43; // [esp-4h] [ebp-28Ch]
int v44; // [esp-4h] [ebp-28Ch]
int v45; // [esp+0h] [ebp-288h]
char pExceptionObject[140]; // [esp+Ch] [ebp-27Ch] BYREF
char *v47; // [esp+98h] [ebp-1F0h]
int buf; // [esp+9Ch] [ebp-1ECh]
unsigned int v49; // [esp+A4h] [ebp-1E4h]
int v50; // [esp+A8h] [ebp-1E0h]
size_t v51; // [esp+ACh] [ebp-1DCh]
_DWORD *v52; // [esp+B0h] [ebp-1D8h]
char v53; // [esp+B7h] [ebp-1D1h]
char v54[160]; // [esp+B8h] [ebp-1D0h] BYREF
char v55[160]; // [esp+158h] [ebp-130h] BYREF
void **v56; // [esp+1F8h] [ebp-90h] BYREF
__int128 v57; // [esp+1FCh] [ebp-8Ch]
__int128 v58; // [esp+20Ch] [ebp-7Ch]
int v59; // [esp+21Ch] [ebp-6Ch]
int v60; // [esp+220h] [ebp-68h] BYREF
void **v61; // [esp+224h] [ebp-64h] BYREF
void *Block; // [esp+228h] [ebp-60h] BYREF
int v63; // [esp+238h] [ebp-50h]
unsigned int v64; // [esp+23Ch] [ebp-4Ch]
size_t Size[4]; // [esp+240h] [ebp-48h] BYREF
int v66; // [esp+250h] [ebp-38h]
__int128 v67; // [esp+254h] [ebp-34h]
int v68; // [esp+264h] [ebp-24h] BYREF
int v69; // [esp+268h] [ebp-20h] BYREF
unsigned int len_1; // [esp+26Ch] [ebp-1Ch] BYREF
int len; // [esp+270h] [ebp-18h] BYREF
char v72; // [esp+276h] [ebp-12h] BYREF
char v73; // [esp+277h] [ebp-11h] BYREF
int v74; // [esp+284h] [ebp-4h]
v52 = message_buf;
v57 = 0i64;
v56 = &YS0076::YS0306::`vftable';
v58 = 0i64;
v59 = 0;
v74 = 0;
sub_EB4620(v55);
v6 = this[63];
LOBYTE(v74) = 1;
(*(void (__thiscall **)(int, char *))(*(_DWORD *)v6 + 64))(v6, v55);
sub_ECA0E0(v55);
sub_ECA3F0(v41);
v47 = v55;
v7 = a5;
HIDWORD(v67) = 0;
if ( a5 < 0x1000 )
v7 = 4096;
*(_OWORD *)Size = 0i64;
if ( final_len > v7 )
v7 = final_len;
Size[0] = (size_t)&YS0073::YS0080<unsigned char>::`vftable';
memset(&Size[1], 0, 12);
v66 = 1;
v49 = ((v7 + 39) & 0xFFFFFFF0) + 1;
v8 = ((v7 + 39) & 0xFFFFFFF0) + 17;
v51 = v8;
v67 = xmmword_126CA30;
LOBYTE(v74) = 3;
v9 = (void *)unknown_libname_56(v8);
Size[3] = v8;
v10 = (size_t)v9;
Size[1] = (size_t)v9;
Size[2] = v8;
if ( (_DWORD)v67 == 1 )
{
memset(v9, 0, v8);
v11 = Size[2];
v10 = Size[1];
}
else
{
v11 = v51;
}
v12 = 0;
LOBYTE(v74) = 4;
if ( v11 )
v12 = v10;
buf_1 = (_BYTE *)(v12 + 15);
if ( a3 == 0xA2 )
(*(void (__thiscall **)(_DWORD))(*(_DWORD *)this[63] + 20))(this[63]);
v51 = 0;
v53 = 1;
while ( 1 )
{
if ( !(*(unsigned __int8 (__thiscall **)(_DWORD))(*(_DWORD *)this[63] + 84))(this[63]) )
{
sub_EB4620(v54);
v14 = this[63];
LOBYTE(v74) = 5;
(*(void (__thiscall **)(int, char *))(*(_DWORD *)v14 + 64))(v14, v54);
v15 = this[63];
LOBYTE(v50) = a3 != 0xA2;
(*(void (__thiscall **)(int))(*(_DWORD *)v15 + 100))(v15);
sub_DB90E0(this, v54, 3500, v50, 1);
if ( !(*(unsigned __int8 (__thiscall **)(_DWORD))(*(_DWORD *)this[63] + 84))(this[63]) )
{
v32 = sub_D6E030(v45);
v33 = sub_D6E030(v32);
v34 = sub_D6E030(v33);
v36 = sub_D6E030(v34);
sub_D70090(100, v36, v38, v40, v44);
goto LABEL_70;
}
LOBYTE(v74) = 4;
sub_EB4790(v54);
}
v69 = 0;
sub_D79A20(this + 1);
v16 = 0;
len = final_len;
if ( Size[2] )
v16 = Size[1];
len_1 = v49;
LOBYTE(v74) = 6;
v17 = *v52;
buf = v16 + 16;
if ( !(*(unsigned __int8 (__stdcall **)(size_t, int *))(v17 + 4))(v16 + 16, &len) || len != final_len )
{
v52[2] = 100;
LABEL_67:
LOBYTE(v74) = 4;
sub_D79A90(&v69);
LABEL_68:
v29 = sub_D6E030(v45);
v30 = sub_D6E030(v29);
v31 = sub_D6E030(v30);
v35 = sub_D6E030(v31);
sub_D70090(v52[2], v35, v37, v39, v43);
LABEL_70:
_CxxThrowException(pExceptionObject, (_ThrowInfo *)&_TI2_AVException_wbs__);
}
if ( !encrypt_package((int *)&v56, (int)(this + 2), a3, buf, &len, 0) )
{
v52[2] = 302;
goto LABEL_67;
}
++len;
*buf_1 = a3;
v68 = 0;
v63 = 0;
v64 = 15;
LOBYTE(Block) = 0;
v61 = &wbs::StringBase<char>::`vftable';
v18 = this[63];
LOBYTE(v74) = 7;
v19 = (*(int (__thiscall **)(int, _BYTE *, int, _DWORD, int *, void ***))(*(_DWORD *)v18 + 28))(
v18,
buf_1,
len,
0,
&v68,
&v61);
if ( v19 == 1 )
{
if ( len )
{
*(_QWORD *)(dword_1360FC4 + 520) += (unsigned int)len;
++*(_DWORD *)(dword_1360FC4 + 528);
}
}
else if ( !v19 )
{
if ( v68 )
{
p_Block = &Block;
if ( v64 >= 0x10 )
p_Block = Block;
(*(void (**)(int, const char *, ...))(*(_DWORD *)dword_13610C4 + 4))(
dword_13610C4,
"HTTP ERROR %i: %s\n",
v68,
p_Block);
}
v52[2] = 102;
if ( (*(unsigned __int8 (__thiscall **)(_DWORD))(*(_DWORD *)this[63] + 84))(this[63]) )
(*(void (__thiscall **)(_DWORD, int))(*(_DWORD *)this[63] + 16))(this[63], 1);
v61 = &wbs::StringBase<char>::`vftable';
if ( v64 < 0x10 )
goto LABEL_38;
v22 = Block;
v23 = v64 + 1;
v24 = Block;
LOBYTE(v74) = 8;
goto LABEL_35;
}
sub_EB82A0(v52);
v20 = this[63];
v73 = 0;
v72 = 0;
if ( (*(unsigned __int8 (__thiscall **)(int, size_t *, unsigned int *, char *, char *))(*(_DWORD *)v20 + 40))(
v20,
Size,
&len_1,
&v73,
&v72) )
{
break;
}
LABEL_31:
v52[2] = 103;
if ( (*(unsigned __int8 (__thiscall **)(_DWORD))(*(_DWORD *)this[63] + 84))(this[63]) )
(*(void (__thiscall **)(_DWORD, int))(*(_DWORD *)this[63] + 16))(this[63], 1);
v61 = &wbs::StringBase<char>::`vftable';
if ( v64 < 0x10 )
goto LABEL_38;
v22 = Block;
v23 = v64 + 1;
v24 = Block;
LOBYTE(v74) = 9;
LABEL_35:
if ( v23 >= 0x1000 )
{
v22 = (_DWORD *)*(v22 - 1);
if ( (unsigned int)(v24 - (_BYTE *)v22 - 4) > 0x1F )
_invalid_parameter_noinfo_noreturn();
}
sub_F17EF9(v22);
LABEL_38:
LOBYTE(v74) = 4;
LOBYTE(Block) = 0;
v64 = 15;
v63 = 0;
sub_D79A90(&v69);
if ( (int)++v51 >= 2 )
goto LABEL_68;
}
while ( 1 )
{
if ( (int)len_1 <= 0 )
goto LABEL_31;
buf_1 = 0;
if ( Size[2] )
buf_1 = (_BYTE *)Size[1];
*(_QWORD *)(dword_1360FC4 + 504) += len_1;
++*(_DWORD *)(dword_1360FC4 + 512);
if ( !decrypt_package(&v56, (int)(this + 2), a3, (int)buf_1, &len_1, (v73 & 0xF0) == 112) )
goto LABEL_64;
v60 = 0;
if ( !(unsigned __int8)sub_EB8AC0(buf_1, len_1, &v60) )
break;
v21 = this[63];
v73 = 0;
v72 = 0;
if ( !(*(unsigned __int8 (__thiscall **)(int, size_t *, unsigned int *, char *, char *))(*(_DWORD *)v21 + 40))(
v21,
Size,
&len_1,
&v73,
&v72) )
goto LABEL_31;
}
if ( len_1 > a5 )
{
LABEL_64:
v52[2] = 302;
sub_D63570(&v61);
goto LABEL_67;
}
if ( !(*(unsigned __int8 (__thiscall **)(_DWORD *, _BYTE *, unsigned int))(*v52 + 8))(v52, buf_1, len_1) )
{
v53 = 0;
v52[2] = 100;
}
sub_D63570(&v61);
LOBYTE(v74) = 4;
sub_D79A90(&v69);
if ( v53 != 1 )
goto LABEL_68;
v26 = (_DWORD *)DWORD2(v67);
v27 = (_DWORD *)DWORD1(v67);
LOBYTE(v74) = 10;
for ( Size[0] = (size_t)&YS0073::YS0080<unsigned char>::`vftable'; v27 != v26; ++v27 )
{
if ( *v27 )
{
(*(void (__thiscall **)(_DWORD, _DWORD))(*(_DWORD *)*v27 + 4))(*v27, 0);
v26 = (_DWORD *)DWORD2(v67);
}
}
if ( (_BYTE)v66 )
{
v28 = (void *)Size[1];
if ( Size[1] )
{
if ( (_DWORD)v67 == 1 )
{
memset((void *)Size[1], 0, Size[2]);
v28 = (void *)Size[1];
}
j_j__free(v28);
}
memset(&Size[1], 0, 12);
LOBYTE(v66) = 1;
}
sub_D6CCA0();
LOBYTE(v74) = 11;
sub_ECA0E0(v55);
sub_ECA6B0(v42);
sub_EB4790(v55);
}
void __thiscall api_cm_access_2_entry(int this)
{
CMTIME *v2; // eax
char v3; // cl
CMTIME v4; // xmm0
char v5[16]; // [esp+4h] [ebp-2D4h] BYREF
CMACCESS2 cmacc; // [esp+14h] [ebp-2C4h] BYREF
cmacc.mulReserved1 = 0;
cmacc.mulReserved2 = 0;
memset(&cmacc.mulLicenseQuantity, 0, 0x88u);
memset(cmacc.mabCmActId, 0, 0x110u);
memset(&cmacc.mcmCredential.mulCreationTime, 0, 0xE0u);
cmacc.mflCtrl = *(_DWORD *)(this + 44);
cmacc.mulFirmCode = *(_DWORD *)(this + 48);
cmacc.mulProductCode = *(_DWORD *)(this + 52);
cmacc.mulFeatureCode = *(_DWORD *)(this + 56);
v2 = (CMTIME *)getCMTime(v5, 0);
v3 = *(_BYTE *)(this + 72);
v4 = *v2;
cmacc.mulUsedRuntimeVersion = *(_DWORD *)(this + 60);
cmacc.mulProductItemReference = *(unsigned __int16 *)(this + 68);
cmacc.mbMinBoxMajorVersion = *(_BYTE *)(this + 76);
cmacc.mbMinBoxMinorVersion = *(_BYTE *)(this + 77);
cmacc.musBoxMask = *(_WORD *)(this + 78);
cmacc.mulSerialNumber = *(_DWORD *)(this + 80);
cmacc.mcmCredential.mulPID = *(_DWORD *)(this + 64);
cmacc.mcmCredential.mulSession = *(unsigned __int16 *)(this + 70);
cmacc.mcmCredential.mulCleanupTime = 0;
cmacc.mcmCredential.mulMaxLifeTime = 0;
cmacc.mcmReleaseDate = v4;
if ( v3 || *(_BYTE *)(this + 73) || *(_BYTE *)(this + 74) || *(_BYTE *)(this + 75) )
sub_D9EB30(cmacc.mszServername, 0x80u, 0x80u, "%i.%i.%i.%i", v3);
if ( !*(_BYTE *)(dword_1360FC4 + 760) && (cmacc.mflCtrl & 0x200000) != 0 )
*(_DWORD *)(this + 20) = 0x80000000;
api_cm_access_2__3(
*(_DWORD *)(this + 40),
&cmacc,
(int *)(this + 220),
(_DWORD *)(this + 8),
*(void ***)(this + 16),
0,
*(_DWORD *)(this + 28));
}
int __thiscall api_handler(_DWORD *this)
{
int v2; // eax
char v3; // al
int api_class; // ecx
int v5; // ecx
char v6; // dl
int v7; // ecx
int v8; // esi
int v9; // eax
bool v10; // zf
_DWORD v12[8]; // [esp+0h] [ebp-38h] BYREF
char v13; // [esp+23h] [ebp-15h] BYREF
_DWORD *v14; // [esp+28h] [ebp-10h]
int v15; // [esp+34h] [ebp-4h]
v14 = v12;
v12[7] = this;
v13 = 0;
v12[6] = &v13;
v15 = 0;
do
{
if ( (*(unsigned __int8 (__thiscall **)(int))(*(_DWORD *)((char *)this + *(_DWORD *)(*this + 4)) + 32))((int)this + *(_DWORD *)(*this + 4)) )
{
*(_DWORD *)(this[2] + 8) = 0xD0010003;
this[1] = 0;
v15 = 1;
goto LABEL_17;
}
LOBYTE(v15) = 2;
v2 = _Mtx_trylock((_Mtx_t)(dword_1360FC4 + 220));
if ( v2 )
{
if ( v2 != 3 )
std::_Throw_C_error(v2);
v3 = 0;
}
else
{
v3 = 1;
}
LOBYTE(v15) = 0;
}
while ( !v3 );
v13 = 1;
if ( !*(_BYTE *)(dword_1360FC4 + 150) )
{
*(_DWORD *)(this[2] + 8) = 238;
this[1] = 0;
v15 = 3;
LABEL_17:
v10 = v13 == 0;
goto LABEL_18;
}
api_class = this[2];
LOBYTE(v15) = 4;
(*(void (__thiscall **)(int))(*(_DWORD *)api_class + 16))(api_class);// <==============call api
v15 = 0;
_Mtx_unlock((_Mtx_t)(dword_1360FC4 + 220));
v5 = this[2];
v6 = 0;
v13 = 0;
if ( *(int *)(v5 + 20) >= 0 )
{
v7 = *(_DWORD *)(v5 + 8);
if ( v7 )
{
if ( v7 != 112 && v7 != 209 )
{
v8 = *(_DWORD *)dword_13610C4;
v9 = sub_D8CD00(v7);
(*(void (**)(int, const char *, ...))(v8 + 4))(
dword_13610C4,
"API Error %u (%s) occurred!\n",
*(_DWORD *)(this[2] + 8),
v9);
v6 = v13;
}
}
}
this[1] = 0;
v15 = 6;
v10 = v6 == 0;
LABEL_18:
if ( !v10 )
_Mtx_unlock((_Mtx_t)(dword_1360FC4 + 220));
return 0;
}
(*(void (__thiscall **)(int))(*(_DWORD *)api_class + 16))(api_class);
简单的标注一下常用的API
三
初见——服务端的主要加密算法
secp224r1{
p = 0xffffffffffffffffffffffffffffffff000000000000000000000001
a = 0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe
b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D
Gx = 0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21
Gy = 0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34
}
四
CodeMeter所特有的ECDSA
吐槽一下,FindCrypt貌似不支持ECDSA算法。
其中我们主要关注checkECDSASignature ECDSAEncrypt ECDSASign这几个函数,因为ECDSA算法的原理及其实现论坛上一抓一大把,我这里只说说这些函数在CodeMeter中的用处以及定位方法。
checkECDSASignature
BOOL __cdecl checkECDSASignature(
char *ecdsa,
unsigned __int8 *pubKey,
unsigned __int8 *pbSignature,
unsigned __int8 *mabDigest)
{
BOOL result; // eax
void *curve_n; // [esp+10h] [ebp-A8h] BYREF
void *curve_G_y; // [esp+14h] [ebp-A4h] BYREF
void *curve_G_x; // [esp+18h] [ebp-A0h] BYREF
__int128 pSignature_2[2]; // [esp+1Ch] [ebp-9Ch] BYREF
char a2[16]; // [esp+3Ch] [ebp-7Ch] BYREF
__int128 v10; // [esp+4Ch] [ebp-6Ch]
int pSignature_1[7]; // [esp+5Ch] [ebp-5Ch] BYREF
int pubkey[4]; // [esp+78h] [ebp-40h] BYREF
__int128 v13; // [esp+88h] [ebp-30h]
char plaintext[4]; // [esp+98h] [ebp-20h] BYREF
result = sub_B1C960(ecdsa, (int)pubKey, 32);
if ( result )
{
bignum_copy((int)pSignature_1, pbSignature, 0x1Cu);
bignum_copy((int)pSignature_2, pbSignature + 32, 0x1Cu);
bignum_copy((int)plaintext, mabDigest, 0x1Cu);
get_ecdsa_curve(&curve_n, &curve_G_x, &curve_G_y);
result = bignum_compare_int((int)pSignature_1, (int)curve_n, 7);
if ( !result )
{
result = bignum_compare_int((int)pSignature_1, 0, 7);
if ( result != 3 )
{
result = bignum_compare_int((int)pSignature_2, (int)curve_n, 7);
if ( !result )
{
result = bignum_compare_int((int)pSignature_2, 0, 7);
if ( result != 3 )
{
sub_B1FBE0(pSignature_2, (int)a2, curve_n, 7);
sub_B1FE90((int)plaintext, (int)a2, curve_n, 7);
sub_B1FE90((int)a2, (int)pSignature_1, curve_n, 7);
bignum_copy((int)pubkey, pubKey, 0x1Cu);
bignum_copy((int)pSignature_2, pubKey + 32, 0x1Cu);
result = init_ecdsa_curve_G(ecdsa, a2, 7, pubkey, pSignature_2);
if ( result )
{
*(_OWORD *)a2 = *(_OWORD *)ecdsa;
v10 = *((_OWORD *)ecdsa + 1);
pSignature_2[0] = *((_OWORD *)ecdsa + 2);
pSignature_2[1] = *((_OWORD *)ecdsa + 3);
*(_OWORD *)pubkey = *((_OWORD *)ecdsa + 4);
v13 = *((_OWORD *)ecdsa + 5);
result = init_ecdsa_curve_G(ecdsa, plaintext, 7, curve_G_x, curve_G_y);
if ( result )
{
result = sub_B1BC30(ecdsa, a2, (int)pSignature_2, (int)pubkey);
if ( result )
{
result = bignum_compare_int((int)(ecdsa + 64), 0, 7);
if ( result != 3 )
{
init_ecdsa_curve_p(ecdsa);
result = bignum_compare_int((int)ecdsa, (int)pSignature_1, 7);
if ( result == 3 )
return 1;
}
}
}
}
}
}
}
}
}
return 0;
}
用处:
client CmValiateSignature验证签名
server License授权校验 Licensor Public Key检查 etc
破解方法:
函数尾xor eax,eax改成mov al,1 就能爆掉证书检查
定位方法:
1、搜索字符串 “Licensor Public Key Signature is invalid.”,串参找引用,只有一个函数。
2、这个便是checkECDSASignature。
看雪ID:ericyudatou
https://bbs.kanxue.com/homepage-786730.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!