查看原文
其他

Wibu Codemeter 7.3学习笔记——Codemeter服务端

ericyudatou 看雪学苑 2023-04-07


本文为看雪论坛优秀文章

看雪论坛作者ID:ericyudatou





准备


  1. CodeMeter.exe Win32 v7.30

  2. IDA Pro






初见——反调试


之前我说Codemeter的反调试很猛,我收回。Scylla Hide调至VMP档,运行后将爆出的几个错误全部Pass to the Application && Do not suspend or log即可。





初见——从何入手(通信协议部分)


有了前面分析的经验,感觉还是从通信协议入手比较好,顺便看看咱跟人家写的有什么差距。

根据前面的分析,我们知道了Codemeter私有协议中最终要的两个函数就是encrypt_telegram和decrypt_telegram。服务器接受到客户端的请求那就必定调用decrypt_telegram解密,向客户端回复数据必定通过encrypt_telegram解密。

因此我们第一步就需要确定这两个函数。


用Bindiff就可以轻而易举的解决这个问题。

先对decrypt_telegram下断,跑起来看看谁调用她。然后我们就轻而易举的找的了这个函数。
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;}

对encrypt_telegram下断,断在这里:
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;}

查看decrypt_package和encrypt_package的交叉引用,有一个函数同时引用这两个函数。
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);}
这不由得让我们联想到send_cm_socket_req(https://bbs.pediy.com/thread-275395.htm)函数,以为轻松秒杀。但事情真的由这么简单吗?

在这个cm_client_encrypt_req中是先调用encrypt_package加密数据包再发送、接收,最后在decrypt_package。这显然与我们对Codemeter服务器加解密逻辑相违背,服务器应当先接受客户端得请求、解密、进行处理之后再加密数据包发送给客户端。

因为cm_client_encrypt_req在运行中不会被调用,我怀疑这只是未被移除的测试代码,但cm_client_encrypt_req却是一块敲门砖,观察她得交叉引用。


引用不少,凭借直觉以及一眼丁真的分析,大概所有得codemeter api都得跟她有一腿。

看看它是如何被调用的(api_cm_access_2):


这调用方式不由得让我们联想起她同父同母的亲兄妹send_cm_socket_req,*buf+36便是API code(此处 100对应CmAccess2)。但不必花太长时间重命名一下函数,这只是一块敲门砖,用完就扔,我们就重点分析一下api_cm_access_2。

看一下对api_cm_access_2的引用,因为这里只是阐述分析Codemeter API在服务器上的实现,所以就不深入探讨这些函数的具体意义,今后有需要再细说:


这堆函数就是CmAccess2在服务器上的实现,假设存在一个API switch,通过客户端加密请求数据包中的API code来调用API。那么这个API swicth一定会调用这个树状图最上头的节点来传递参数。

逐一排查,发现一个有意思的函数。

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));}

这一眼就看得出这个函数不一般,因为C++答辩一样的虚函数,所以只能下断运行。


发现*this+0x24便是已经解密了的客户端请求包,这个函数并非CmAccess2的Entry,而是CmAccess的Entry,这个函数将CmAccess转发到CmAccess2。

返回看看是哪个小可爱调用的它。
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;}

在此处下断,看看调用其他CodeMeter API的反应:
(*(void (__thiscall **)(int))(*(_DWORD *)api_class + 16))(api_class);

也能断下,*this+0x24也与监听到的数据包相符。

简单的标注一下常用的API





初见——服务端的主要加密算法


1、CRC32
主要用于通信协议中的校验,和函数签名的校验
2、SHA1
仅用于getRandomkey,其余没有调用

3、SHA256
用于签名校验,配合ECDSA

4、AES
用于数据包的加解密,以及license文件的加解密

5、ECDSA
secp224r1{ p = 0xffffffffffffffffffffffffffffffff000000000000000000000001 a = 0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D Gx = 0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21 Gy = 0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34}
ECDSA 是研究的重点,涉及License文件的验证。





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

*本文由看雪论坛 ericyudatou 原创,转载请注明来自看雪社区


# 往期推荐

1、CVE-2016-7255提权漏洞学习笔记

2、从新生赛入门PWN

3、Chrome v8 Issue 1307610漏洞及其利用分析

4、CVE-2015-2546提权漏洞学习笔记

5、EXP编写学习之绕过GS

6、EXP编写学习之网络上的EXP



球分享

球点赞

球在看


点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存