VbaCompiler 2.5.2 版本 分析

VbaCompiler 2.5.2 版本

关联 VbaCompiler 1.6.4 注册分析1 - DirWangK - 博客园

2.5.2 版本在patch公钥后发现编译的xlsm文件打不开,考虑问题发现在:

  • 许可的公钥还出现在内嵌资源中(cbinrtl.dll),在patch时一并进行了替换导致校验出错

  • key3 是错误的,跟踪发现在compile进行了访问,猜测是dsa签名,在xlsm打开时进行验证

excel调试(关注生成的xxxx_xlsm_32.dll)

在导出函数设断停在SetThisWorkbook

发现是一个包装的导出函数,经验证后后加载cbinrtl.dll(cbinrtl.dll释放在temp随机目录中),调用其SetThisWorkbook 导出函数,关键点一便是对cbinrtl.dll 的校验处理

SetThisWorkbook

int __stdcall SetThisWorkbook(int a1)
{
  FARPROC SetThisWorkbook; // [esp+18h] [ebp-10h]
  int v3; // [esp+1Ch] [ebp-Ch]

  v3 = 0;
  if ( !sub_67B82BF1(dword_67B88104, a1) || !hLibModule )
    return v3;
  SetThisWorkbook = GetProcAddress(hLibModule, "SetThisWorkbook");
  v3 = ((int (__cdecl *)(int))SetThisWorkbook)(a1);
  if ( a1 )
  {
    if ( !v3 )
    {
      sub_67B815C4();
      return 1;
    }
  }
  else
  {
    sub_67B817F0();
  }
  return v3;
}

sub_67B82BF1

int __cdecl sub_67B82BF1(int a1, int a2)
{
  __time32_t v2; // ebx
  clock_t v3; // eax
  FARPROC DummyFunc05; // [esp+18h] [ebp-10h]
  int v6; // [esp+1Ch] [ebp-Ch]

  v6 = 0;
  v2 = time(0);
  v3 = clock();
  srand(v2 ^ v3);
  if ( Destination[0] && check_md5_67B82421(Destination) )
    hLibModule = LoadLibraryW(Destination);
  if ( !hLibModule )
  {
    hLibModule = (HMODULE)sub_67B82581(a1);
    if ( !hLibModule )
      hLibModule = (HMODULE)sub_67B82923(a1);
  }
  if ( !hLibModule )
    return v6;
  if ( dword_67B860C0 )
  {
    return 1;
  }
  else
  {
    DummyFunc05 = GetProcAddress(hLibModule, "DummyFunc05");
    if ( DummyFunc05 && ((int (__stdcall *)(int, int *, int))DummyFunc05)(a1, &dword_67B860C0, a2) )
    {
      sub_67B81450(dword_67B860C0);
      return 1;
    }
  }
  return v6;
}

check_md5_67B82421

int __cdecl check_md5_67B82421(wchar_t *FileName)
{
  const void *embedded_md5_67B815A3; // eax
  int Buf2; // [esp+14h] [ebp-24h] BYREF
  int v4; // [esp+18h] [ebp-20h]
  int v5; // [esp+1Ch] [ebp-1Ch]
  int v6; // [esp+20h] [ebp-18h]
  size_t Size; // [esp+24h] [ebp-14h] BYREF
  void *Block; // [esp+28h] [ebp-10h] BYREF
  int v9; // [esp+2Ch] [ebp-Ch]

  v9 = sub_67B821B1(FileName);
  if ( !v9 )
    return v9;
  v9 = 0;
  Block = 0;
  Size = 0;
  if ( f_read_all_67B82363(FileName, (int)&Block, (int)&Size) )
  {
    if ( Block )
    {
      if ( Size )
      {
        Buf2 = 0;
        v4 = 0;
        v5 = 0;
        v6 = 0;
        md5_67B81CD6(Block, Size, (int)&Buf2);
        if ( Buf2 )
        {
          if ( v4 )
          {
            embedded_md5_67B815A3 = (const void *)get_embedded_md5_67B815A3();
            if ( !memcmp(embedded_md5_67B815A3, &Buf2, 0x10u) )
              v9 = 1;
          }
        }
      }
    }
  }
  if ( Block )
    free(Block);
  return v9;
}

get_embedded_md5_67B815A3

_DWORD *get_embedded_md5_67B815A3()
{
  _DWORD *result; // eax

  result = dword_67B860A4;
  dword_67B860A4[0] = 0x219194C0;
  dword_67B860A4[1] = 0xFC19E933;
  dword_67B860A4[2] = 0x6B841424;
  dword_67B860A4[3] = 0x6AB5B127;
  return result;
}

image-20241229103445798

md5校验(关注生成的xxxx_xlsm_32.dll)

image-20241229103907963

内置md5

image-20241229103550606

该内嵌md5 为vbaclr4e.exe 在compile时传递,可以在生成过程在temp目录中找到相关文件

可在vbaclr4e.exe DeleteFileW 设断点,便可获取中间编译文件

image-20241229121827889

签名校验(关注释放到temp目录的cbinrtl.dll)

在处理了md5校验之后发现程序还是异常退出,打不开xlsm,跟踪发现是对一全局对象(cbinrtl.dll rva 14FE84)访问发生0地址访问,对全局对象设置硬件访问断点跟踪,发现其在最初被正常初始化,但后来被释放掉,(但后续有一些逻辑并未对该对象进行有效性判断,便发生了异常),通过回溯最后发现CryptoPP::PK_Verifier::VerifyMessage 函数的调用

CryptoPP sig 可以从github下载到:

HongThatCong/IDA_Signatures: Các IDA Flirt signatures HTC tạo

.rdata:100F927C ; class CryptoPP::DL_VerifierImpl<CryptoPP::DL_SignatureSchemeOptions<CryptoPP::DSA2<CryptoPP::SHA1>,CryptoPP::DL_Keys_DSA,CryptoPP::DL_Algorithm_GDSA<CryptoPP::Integer>,CryptoPP::DL_SignatureMessageEncodingMethod_DSA,CryptoPP::SHA1> >: CryptoPP::DL_ObjectImpl<CryptoPP::DL_VerifierBase<CryptoPP::Integer>,CryptoPP::DL_SignatureSchemeOptions<CryptoPP::DSA2<CryptoPP::SHA1>,CryptoPP::DL_Keys_DSA,CryptoPP::DL_Algorithm_GDSA<CryptoPP::Integer>,CryptoPP::DL_SignatureMessageEncodingMethod_DSA,CryptoPP::SHA1>,CryptoPP::DL_PublicKey_GFP<CryptoPP::DL_GroupParameters_DSA> >, CryptoPP::DL_ObjectImplBase<CryptoPP::DL_VerifierBase<CryptoPP::Integer>,CryptoPP::DL_SignatureSchemeOptions<CryptoPP::DSA2<CryptoPP::SHA1>,CryptoPP::DL_Keys_DSA,CryptoPP::DL_Algorithm_GDSA<CryptoPP::Integer>,CryptoPP::DL_SignatureMessageEncodingMethod_DSA,CryptoPP::SHA1>,CryptoPP::DL_PublicKey_GFP<CryptoPP::DL_GroupParameters_DSA> >, CryptoPP::AlgorithmImpl<CryptoPP::DL_VerifierBase<CryptoPP::Integer>,CryptoPP::DSA2<CryptoPP::SHA1> >, CryptoPP::DL_V
.rdata:100F927C                 dd offset const CryptoPP::DL_VerifierImpl<CryptoPP::DL_SignatureSchemeOptions<CryptoPP::DSA2<CryptoPP::SHA1>,CryptoPP::DL_Keys_DSA,CryptoPP::DL_Algorithm_GDSA<CryptoPP::Integer>,CryptoPP::DL_SignatureMessageEncodingMethod_DSA,CryptoPP::SHA1>>::`RTTI Complete Object Locator'
.rdata:100F9280 const CryptoPP::DL_VerifierImpl<struct CryptoPP::DL_SignatureSchemeOptions<class CryptoPP::DSA2<class CryptoPP::SHA1>, struct CryptoPP::DL_Keys_DSA, class CryptoPP::DL_Algorithm_GDSA<class CryptoPP::Integer>, class CryptoPP::DL_SignatureMessageEncodingMethod_DSA, class CryptoPP::SHA1>>::`vftable' dd offset sub_10047C10
.rdata:100F9280                                         ; DATA XREF: sub_100397A0+3C5↑o
.rdata:100F9280                                         ; sub_10039EB0+1A5↑o
.rdata:100F9284                 dd offset sub_10046250
.rdata:100F9288                 dd offset CryptoPP::PK_SignatureScheme::MaxSignatureLength(uint)
.rdata:100F928C                 dd offset sub_100462D0
.rdata:100F9290                 dd offset sub_10046320
.rdata:100F9294                 dd offset sub_10042F90
.rdata:100F9298                 dd offset sub_10046390
.rdata:100F929C                 dd offset sub_10042EC0
.rdata:100F92A0                 dd offset sub_100463A0
.rdata:100F92A4                 dd offset sub_100468D0
.rdata:100F92A8                 dd offset sub_100469F0
.rdata:100F92AC                 dd offset CryptoPP::PK_Verifier::Verify(CryptoPP::PK_MessageAccumulator *)
.rdata:100F92B0                 dd offset sub_10046AC0
.rdata:100F92B4                 dd offset CryptoPP::PK_Verifier::VerifyMessage(uchar const *,uint,uchar const *,uint)
.rdata:100F92B8                 dd offset CryptoPP::PK_Verifier::Recover(uchar *,CryptoPP::PK_MessageAccumulator *)
.rdata:100F92BC                 dd offset sub_10046F80
.rdata:100F92C0                 dd offset CryptoPP::PK_Verifier::VerifyMessage(uchar const *,uint,uchar const *,uint)
.rdata:100F92C4                 dd offset sub_10047A60
.rdata:100F92C8                 dd offset sub_10047BE0
.rdata:100F92CC                 dd offset sub_10047BF0
.rdata:100F92D0                 dd offset sub_10046650
bool __fastcall sub_10039EB0(wchar_t *a1, CHAR *a2, int a3)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v66 = v33;
  v40 = a2;
  v39 = a3;
  v41 = 0;
  v67 = 0;
  sub_10076BD1(v33[0], v33[1]);
  MultiByteStr = a2 + 0x24;
  LOBYTE(v67) = 1;
  if ( *((_DWORD *)a2 + 0xE) > 0xFu )
    MultiByteStr = *(CHAR **)MultiByteStr;
  if ( !MultiByteStr || !*MultiByteStr )
    MultiByteStr = (CHAR *)MultiByteStr;
  sub_1004B9B0(v53, MultiByteStr, strlen(MultiByteStr));
  if ( *((_DWORD *)a1 + 5) > 7u )
    a1 = *(wchar_t **)a1;
  sub_10076F7F(a1);
  v59 = 0;
  *(_OWORD *)v58 = 0i64;
  n0xF = 0xF;
  LOBYTE(v58[0]) = 0;
  LOBYTE(v67) = 2;
  v6 = (_DWORD *)sub_1007B350(v45, 0x26);
  LOBYTE(v67) = 3;
  if ( v6[5] > 0xFu )
    v6 = (_DWORD *)*v6;
  sub_10077651((int)MultiByteStr, (int)v6, v58);
  LOBYTE(v67) = 2;
  if ( n0xF_1 > 0xF )
  {
    v7 = v45[0];
    if ( n0xF_1 + 1 >= 0x1000 )
    {
      v7 = (void *)*((_DWORD *)v45[0] + 0xFFFFFFFF);
      if ( (unsigned int)(v45[0] - v7 - 4) > 0x1F )
        _invalid_parameter_noinfo_noreturn();
    }
    free_(v7);
  }
  v8 = operator new(0x3Cu);
  v38 = v8;
  LOBYTE(v67) = 4;
  v9 = sub_1008B630();
  sub_10049290(v9, 4, 0);
  LOBYTE(v67) = 2;
  v10 = (char *)v40 + 0x6C;
  *v8 = &CryptoPP::HexDecoder::`vftable';
  v8[1] = &CryptoPP::HexDecoder::`vftable';
  sub_1004B0C0(v10, v11, v8);
  LOBYTE(v67) = 5;
  sub_10088C00(1);
  sub_10046070(1);
  v34[0] = (int)&CryptoPP::DL_VerifierImpl<CryptoPP::DL_SignatureSchemeOptions<CryptoPP::DSA2<CryptoPP::SHA1>,CryptoPP::DL_Keys_DSA,CryptoPP::DL_Algorithm_GDSA<CryptoPP::Integer>,CryptoPP::DL_SignatureMessageEncodingMethod_DSA,CryptoPP::SHA1>>::`vftable';
  v34[1] = (int)&CryptoPP::DL_VerifierImpl<CryptoPP::DL_SignatureSchemeOptions<CryptoPP::DSA2<CryptoPP::SHA1>,CryptoPP::DL_Keys_DSA,CryptoPP::DL_Algorithm_GDSA<CryptoPP::Integer>,CryptoPP::DL_SignatureMessageEncodingMethod_DSA,CryptoPP::SHA1>>::`vftable';
  v34[2] = (int)&CryptoPP::DL_VerifierImpl<CryptoPP::DL_SignatureSchemeOptions<CryptoPP::DSA2<CryptoPP::SHA1>,CryptoPP::DL_Keys_DSA,CryptoPP::DL_Algorithm_GDSA<CryptoPP::Integer>,CryptoPP::DL_SignatureMessageEncodingMethod_DSA,CryptoPP::SHA1>>::`vftable';
  LOBYTE(v67) = 6;
  (*(void (__thiscall **)(int *, char *))(v35 + 4))(&v35, v43);
  v62 = 0;
  *(_OWORD *)v61 = 0i64;
  n0xF_2 = 0xF;
  LOBYTE(v61[0]) = 0;
  LOBYTE(v67) = 8;
  v12 = operator new(0x3Cu);
  v38 = v12;
  LOBYTE(v67) = 9;
  v13 = operator new(0x10u);
  v40 = v13;
  LOBYTE(v67) = 0xA;
  sub_10088C00(0);
  *v13 = &CryptoPP::StringSinkTemplate<std::string>::`vftable';
  v13[1] = &CryptoPP::StringSinkTemplate<std::string>::`vftable';
  v13[3] = v61;
  LOBYTE(v67) = 9;
  v14 = sub_1008B630();
  sub_10049290(v14, 4, v13);
  *v12 = &CryptoPP::HexDecoder::`vftable';
  v15 = v58;
  v12[1] = &CryptoPP::HexDecoder::`vftable';
  LOBYTE(v67) = 8;
  if ( n0xF > 0xF )
    v15 = (void **)v58[0];
  sub_10048750(v15, 1, v12);
  if ( v42 )
    (**v42)(v42, 1);
  sub_10038900(v64, (int)&v47, 3);
  LOBYTE(v67) = 0xB;
  v16 = v61;
  if ( n0xF_2 > 0xF )
    v16 = (void **)v61[0];
  v17 = v64;
  if ( HIDWORD(v65) > 7 )
    v17 = (void **)v64[0];
  v18 = CryptoPP::PK_Verifier::VerifyMessage(
          (CryptoPP::PK_Verifier *)v34,
          (const unsigned __int8 *)v17,
          2 * v65,
          (const unsigned __int8 *)v16,
          0);
  v19 = v18;
  v41 = v18;
  if ( !v18 )
  {
    v20 = sub_10038900(v45, (int)&v47, 4);
    if ( v64 != (void **)v20 )
    {
      if ( HIDWORD(v65) > 7 )
      {
        v21 = v64[0];
        if ( (unsigned int)(2 * HIDWORD(v65) + 2) >= 0x1000 )
        {
          v21 = (void *)*((_DWORD *)v64[0] + 0xFFFFFFFF);
          if ( (unsigned int)(v64[0] - v21 - 4) > 0x1F )
            _invalid_parameter_noinfo_noreturn();
        }
        free_(v21);
      }
      v65 = 0x700000000i64;
      LOWORD(v64[0]) = 0;
      *(_OWORD *)v64 = *(_OWORD *)v20;
      v65 = *(_QWORD *)(v20 + 0x10);
      *(_DWORD *)(v20 + 0x10) = 0;
      *(_DWORD *)(v20 + 0x14) = 7;
      *(_WORD *)v20 = 0;
    }
    sub_10002910(v45);
    v22 = v61;
    if ( n0xF_2 > 0xF )
      v22 = (void **)v61[0];
    v23 = v64;
    if ( HIDWORD(v65) > 7 )
      v23 = (void **)v64[0];
    v19 = CryptoPP::PK_Verifier::VerifyMessage(
            (CryptoPP::PK_Verifier *)v34,
            (const unsigned __int8 *)v23,
            2 * v65,
            (const unsigned __int8 *)v22,
            0);
  }
  LOBYTE(v67) = 8;
  if ( HIDWORD(v65) > 7 )
  {
    v24 = v64[0];
    if ( (unsigned int)(2 * HIDWORD(v65) + 2) >= 0x1000 )
    {
      v24 = (void *)*((_DWORD *)v64[0] + 0xFFFFFFFF);
      if ( (unsigned int)(v64[0] - v24 - 4) > 0x1F )
        goto LABEL_73;
    }
    free_(v24);
  }
  LOBYTE(v67) = 7;
  v65 = 0x700000000i64;
  LOWORD(v64[0]) = 0;
  if ( n0xF_2 > 0xF )
  {
    v25 = v61[0];
    if ( n0xF_2 + 1 >= 0x1000 )
    {
      v25 = (void *)*((_DWORD *)v61[0] + 0xFFFFFFFF);
      if ( (unsigned int)(v61[0] - v25 - 4) > 0x1F )
        goto LABEL_73;
    }
    free_(v25);
  }
  v62 = 0;
  n0xF_2 = 0xF;
  LOBYTE(v61[0]) = 0;
  sub_10048D40(v37);
  sub_10048AA0(v36);
  if ( v44 )
    (**v44)(v44, 1);
  LOBYTE(v67) = 1;
  if ( n0xF > 0xF )
  {
    v26 = v58[0];
    if ( n0xF + 1 >= 0x1000 )
    {
      v26 = (void *)*((_DWORD *)v58[0] + 0xFFFFFFFF);
      if ( (unsigned int)(v58[0] - v26 - 4) > 0x1F )
        goto LABEL_73;
    }
    free_(v26);
  }
  v59 = 0;
  n0xF = 0xF;
  LOBYTE(v58[0]) = 0;
  LOBYTE(v67) = 0xC;
  v27 = (char *)v55;
  v47 = (int)&off_1010EBE8;
  if ( v55 != v56 )
  {
    do
    {
      (*(void (__thiscall **)(_DWORD))(**(_DWORD **)v27 + 0x1C))(*(_DWORD *)v27);
      if ( *(_DWORD *)v27 )
        (***(void (__thiscall ****)(_DWORD, int))v27)(*(_DWORD *)v27, 1);
      v27 += 4;
    }
    while ( v27 != (char *)v56 );
    v27 = (char *)v55;
    if ( v55 != v56 )
      v56 = v55;
  }
  if ( v27 )
  {
    v28 = v27;
    if ( ((v57 - (_DWORD)v27) & 0xFFFFFFFC) >= 0x1000 )
    {
      v27 = (char *)*((_DWORD *)v27 + 0xFFFFFFFF);
      if ( (unsigned int)(v28 - v27 - 4) > 0x1F )
        goto LABEL_73;
    }
    free_(v27);
    v55 = 0;
    v56 = 0;
    v57 = 0;
  }
  if ( n0xF_3 > 0xF )
  {
    v29 = v53[0];
    if ( n0xF_3 + 1 >= 0x1000 )
    {
      v29 = (void *)*((_DWORD *)v53[0] + 0xFFFFFFFF);
      if ( (unsigned int)(v53[0] - v29 - 4) > 0x1F )
        goto LABEL_73;
    }
    free_(v29);
  }
  v53[4] = 0;
  n0xF_3 = 0xF;
  LOBYTE(v53[0]) = 0;
  if ( n7 > 7 )
  {
    v30 = v50;
    if ( 2 * n7 + 2 >= 0x1000 )
    {
      v30 = (_BYTE *)*((_DWORD *)v50 + 0xFFFFFFFF);
      if ( (unsigned int)((_BYTE *)v50 - v30 - 4) > 0x1F )
        goto LABEL_73;
    }
    free_(v30);
  }
  v51 = 0;
  n7 = 7;
  LOWORD(v50) = 0;
  if ( n0xF_4 <= 0xF )
    return v19;
  v31 = v48;
  if ( n0xF_4 + 1 < 0x1000
    || (v31 = (void *)*((_DWORD *)v48 + 0xFFFFFFFF), (unsigned int)((_BYTE *)v48 - (_BYTE *)v31 - 4) <= 0x1F) )
  {
    free_(v31);
    return v19;
  }
LABEL_73:
  _invalid_parameter_noinfo_noreturn();
  return v19;
}

rkey校验

image-20241229104435236

regkey 中key校验
相关信息可参考后面py脚本中的verify_rkey

key3 校验

image-20241229104608793

    #mid3  为computer id 解码后解析而来
    msg3=f'{mail}|{mid3}|{dtf}|{dtt}'.encode()
    print('[-]msg3:',msg3)
    key3=dsa_sign_data(private_key,msg3).hex().upper()

py

from datetime import datetime
import zlib
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA1

#vbaclr4e.exe 1.6.4
def char_map(currentChar: int):
    charValue = 0
    if (currentChar - ord('0')) > 9:
        if (currentChar - ord('A')) > 0x1A:
            if (currentChar - ord('a')) <= 0x1A:
                # currentChar>9+ord('0') currentChar>0x1A+ord('A') currentChar<=0x1A+ord('a') 时
                #   charValue>9+ord('0')-0x3c
                #   charValue>0x1A+ord('A')-0x3c
                #   charValue<=0x1A+ord('a') -0x3c
                charValue = currentChar - 0x3C
            else:
                add = False
        else:
            # currentChar>9+ord('0') currentChar<=0x1A+ord('A') 时
            #  9+ord('0')- ord('7')< charValue<=0x1A+ord('A')- ord('7')
            charValue = currentChar - ord('7')
    else:
        '''
        currentChar<=9+ord('0')时 charValue<=9 
        '''
        charValue = currentChar - ord('0')
    return charValue


def re_char_map(charValue: int):
    currentChar = 0

    if charValue <= 9:
        currentChar = charValue+ord('0')
    elif 9+ord('0') - ord('7') < charValue <= 0x1A+ord('A') - ord('7'):
        currentChar = charValue+ord('7')
    elif charValue > 9+ord('0')-0x3c:
        if 0x1A+ord('A')-0x3c < charValue <= 0x1A+ord('a') - 0x3c:
            currentChar = charValue+0x3C

    return currentChar


def re_decode_5332A4(binary_data: bytes) -> bytes:
    pad_sz = 3-len(binary_data) % 3
    if pad_sz != 3:
        binary_data += b'\x00'*pad_sz
    out = []
    accumulator = 0
    for i, x in enumerate(binary_data):
        value = 0
        idx = i % 3
        if idx == 0:
            value = x
            pass
        elif idx == 1:
            value = x << 8
            pass
        elif idx == 2:
            value = x << 16
            pass

        accumulator |= value
        if idx == 2:
            a = re_char_map(accumulator & 0x3f)
            b = re_char_map((accumulator >> 6) & 0x3f)
            c = re_char_map((accumulator >> 12) & 0x3f)
            d = re_char_map((accumulator >> 18) & 0x3f)

            out.append(chr(a))
            out.append(chr(b))
            out.append(chr(c))
            out.append(chr(d))
            accumulator = 0

    return ''.join(out[:-pad_sz]).encode()


def decode_5332A4(Str: str) -> bytes:
    out = []  # 用于存储输出结果
    length = len(Str)
    index = 0
    accumulator = 0  # 用于累积解析出来的各部分值

    while index < length:
        currentChar = ord(Str[index])
        charValue = char_map(currentChar)
        count = index % 4

        # 根据计数情况左移位处理值
        if count == 1:
            charValue <<= 6
        elif count == 2:
            charValue <<= 4
        elif count == 3:
            charValue <<= 2

        accumulator |= charValue

        # 每次拼接一个完整字节(count != 0 时)
        if count != 0:
            out.append(accumulator & 0xFF)
            accumulator >>= 8

        index += 1
    if accumulator:
        out.append(accumulator & 0xFF)
    return bytes(out)


def rol(value, bits, size=8):
    """
    循环左移(ROL)操作
    :param value: 要处理的值
    :param bits: 要左移的位数
    :param size: 数据宽度(默认为8位)
    :return: 处理后的值
    """
    return ((value << bits) & ((1 << size) - 1)) | (value >> (size - bits))


def ror(value, bits, size=8):
    """
    循环右移(ROR)操作
    :param value: 要处理的值
    :param bits: 要右移的位数
    :param size: 数据宽度(默认为8位)
    :return: 处理后的值
    """
    return (value >> bits) | ((value << (size - bits)) & ((1 << size) - 1))


def inverse_sub_532C99(key: str, encrypted_data: bytes) -> bytes:
    """
    sub_532C99 的逆操作函数,用于解密数据。
    :param key: 密钥字符串
    :param encrypted_data: 加密/混淆后的数据 (bytes)
    :return: 解密后的原始数据 (bytes)
    """
    # Step 1: 处理密钥 (与加密函数相同)
    def rol(val, r_bits, max_bits=8):
        """循环左移"""
        return ((val << r_bits) & (2**max_bits - 1)) | (val >> (max_bits - r_bits))

    def ror(val, r_bits, max_bits=8):
        """循环右移"""
        return (val >> r_bits) | ((val << (max_bits - r_bits)) & (2**max_bits - 1))

    block = bytearray()
    for i, char in enumerate(key):
        shift = (i + 1) & 7  # 位移数 = (索引 + 1) % 8
        if i % 2 == 0:  # 偶数索引:循环左移
            block.append(rol(ord(char), shift))
        else:  # 奇数索引:循环右移
            block.append(ror(ord(char), shift))

    # Step 2: 解密数据
    result = bytearray()
    block_size = len(block)
    d_index = 1
    for i, byte in enumerate(encrypted_data):
        k_index = d_index % block_size
        shift = k_index + i  # 计算位移数
        key_byte = block[k_index]  # 从处理后的密钥中取字符

        # 异或还原
        xor_byte = byte ^ key_byte

        # 逆操作:还原位移
        if i % 2 == 0:  # 偶数索引:加密时进行了右移,因此解密时需要左移
            original_byte = rol(xor_byte, shift & 7)
        else:  # 奇数索引:加密时进行了左移,因此解密时需要右移
            original_byte = ror(xor_byte, shift & 7)

        result.append(original_byte)
        d_index = k_index + 1

    return bytes(result)


def sub_532C99(key: str, data: bytes) -> bytes:
    """
    Python 实现的加密/混淆函数,模仿原始 C 函数逻辑
    :param key: 密钥字符串
    :param data: 输入数据 (bytes)
    :return: 混淆/加密后的结果 (bytes)
    """
    # Step 1: 处理密钥
    block = bytearray()
    for i, char in enumerate(key):
        shift = (i + 1) & 7  # 位移数 = (索引 + 1) % 8
        if i % 2 == 0:  # 偶数索引:循环左移
            block.append(rol(ord(char), shift))
        else:  # 奇数索引:循环右移
            block.append(ror(ord(char), shift))
    # Step 2: 混淆输入数据
    result = bytearray()
    block_size = len(block)
    d_index = 1
    for i, byte in enumerate(data):
        k_index = d_index % block_size
        shift = k_index + i  # 计算位移数
        key_byte = block[k_index]  # 从处理后的密钥中取字符

        if i % 2 == 0:  # 偶数索引:循环右移
            shifted_byte = ror(byte, shift & 7)
        else:  # 奇数索引:循环左移
            shifted_byte = rol(byte, shift & 7)

        # 异或混淆
        result.append(shifted_byte ^ key_byte)
        d_index = k_index+1

    return bytes(result)

# 生成 DSA 密钥对
def generate_dsa_key(key_size=1024):
    """
    生成 DSA 密钥对。
    参数:
        key_size: DSA 密钥的长度(如 1024、2048)
    返回:
        private_key: 私钥对象
        public_key: 公钥对象
    """
    key = DSA.generate(key_size)
    private_key = key
    public_key = key.publickey()
    return private_key, public_key

# 对数据进行签名
def dsa_sign_data_sha1(private_key, data):
    """
    使用 DSA 私钥对数据进行签名。
    参数:
        private_key: 私钥对象
        data: 要签名的字节数据
    返回:
        signature: 生成的签名
    """
    # 计算数据的哈希值
    hash_obj = SHA1.new(data)
    # 使用私钥签名数据
    signer = DSS.new(private_key, 'fips-186-3')
    signature = signer.sign(hash_obj)
    return signature

# 验证签名
def dsa_sha1_verify_signature(public_key, data, signature):
    """
    使用 DSA 公钥验证签名。
    参数:
        public_key: 公钥对象
        data: 原始字节数据
        signature: 签名
    返回:
        True: 验证通过
        False: 验证失败
    """
    # 计算数据的哈希值
    hash_obj = SHA1.new(data)
    # 验证签名
    verifier = DSS.new(public_key, 'fips-186-3')
    try:
        verifier.verify(hash_obj, signature)
        return True
    except ValueError:
        return False


def load_dsa_public_key_from_der(der_data):
    key = None
    # 尝试解析为 DSA 公钥
    try:
        key = DSA.import_key(der_data)
        print("=== DSA 公钥信息 ===")
        print("参数 p:", key.p)
        print("参数 q:", key.q)
        print("参数 g:", key.g)
        print("公钥 y:", key.y)
    except ValueError as e:
        print("解析失败,错误信息:", e)
    return key


def load_dsa_private_key_from_der(der_data):
    key = None
    # 尝试解析为 DSA 私钥
    try:
        key = DSA.import_key(der_data)
        print("=== DSA 私钥信息 ===")
        print("p (素数参数):", key.p)  # 素数 p
        print("q (安全常数):", key.q)  # 安全常数 q
        print("g (生成元):", key.g)  # 生成元 g
        print("私钥值 (x):", key.x)  # 私钥值 (x)
    except ValueError as e:
        print("解析失败,错误信息:", e)
    return key

# generate_dsa_key生成,可自定义
private_key_der = bytes.fromhex('3082014a0201003082012b06072a8648ce3804013082011e02818100a95e21bc8cf2665be64d81d1ce07cb13dfb1eb3e05048646510abc65184b7ef6b3ac61b94549362b6fe7984f5a9397785a91f9d32c4f1ee1d485c4a1bfaeae071d7db5553c6570a0c28040eb8de2b859858c031507ccdf6c5764061145aa3746bc004fe3cca3e111ce41676aaaa0a198d58f3f3a431d8313e66ecd46dad36e13021500cece499ef3d02d845e6adc8f2969890a8376e2af0281800cefc78792d0bbf24df61ccf2cb89882c95cb73f1ece59001701299f60053dc270918561f39815325418a48682912a0d94788d313ae5780aedf837a730d5cf1d8dcac77aef6cc8066de8eb19e24697591d7e592f23a00266b8e1b878a33cb4b2de591042a18a5ae5778d2e2cb0799ee98124a2392b196af9dca6f56c0464971b041602140f7958c36064cc8b75db89ac9a664cd0061bcc00')
public_key_der = bytes.fromhex('308201b63082012b06072a8648ce3804013082011e02818100a95e21bc8cf2665be64d81d1ce07cb13dfb1eb3e05048646510abc65184b7ef6b3ac61b94549362b6fe7984f5a9397785a91f9d32c4f1ee1d485c4a1bfaeae071d7db5553c6570a0c28040eb8de2b859858c031507ccdf6c5764061145aa3746bc004fe3cca3e111ce41676aaaa0a198d58f3f3a431d8313e66ecd46dad36e13021500cece499ef3d02d845e6adc8f2969890a8376e2af0281800cefc78792d0bbf24df61ccf2cb89882c95cb73f1ece59001701299f60053dc270918561f39815325418a48682912a0d94788d313ae5780aedf837a730d5cf1d8dcac77aef6cc8066de8eb19e24697591d7e592f23a00266b8e1b878a33cb4b2de591042a18a5ae5778d2e2cb0799ee98124a2392b196af9dca6f56c0464971b0381840002818022fe96661b2a2958d3a89f4aa232195430aa0c392584411e8fc3843985fd8b6c841ccf1482734bb882a011d2a5aa9648317adcce5f0b2645a52ecc7f3217459c19b1e2c33133b3f71dc44a3954cc7a8825a8cf2bd4aa061dd3917ae325c33dd8924e3d6c4b0e67dad568d0b283ae25e8c93a8f4ded18fd9c0a7a9df7cb5a8cee')

# 生成dsa 公私密码,后续patch 公钥
# private_key,public_key =generate_dsa_key()
private_key = DSA.import_key(private_key_der)
public_key = DSA.import_key(public_key_der)


header = bytes([
    0x44, 0x31, 0x45, 0x58, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00
])


def date_to_systemtime(date_str: str, input_format: str = r"%Y/%m/%d") -> bytes:
    """
    将日期字符串转换为 SYSTEMTIME 结构体的字节数组。

    参数:
        date_str (str): 输入的日期字符串 (如 "2022/09/01")。
        input_format (str): 日期字符串的格式,默认为 "%Y/%m/%d"。

    返回:
        bytes: 转换后的 SYSTEMTIME 小端序字节数组,格式为 [Day, Month, Year_Low, Year_High]。
    """
    # 解析日期字符串为 datetime 对象
    date_obj = datetime.strptime(date_str, input_format)

    # 提取日期部分
    year = date_obj.year  # 年份
    month = date_obj.month  # 月份
    day = date_obj.day  # 日期

    # 转换为小端序字节
    year_low = year & 0xFF  # 年低位
    year_high = (year >> 8) & 0xFF  # 年高位

    # 构建字节数组
    systemtime_bytes = bytearray([day, month, year_low, year_high])

    return bytes(systemtime_bytes)



# 2.5.2
def sub_1403479E0(a1: bytes) -> bytes:
    # 初始化变量
    size = len(a1)  # 字符串长度
    if size == 0:
        return ""  # 空字符串直接返回

    out = []  # 输出字符串,使用列表存储(模拟 std::string)
    c = 0  # 拼接的中间值
    n7 = 0  # 状态机变量
    # 主循环,逐字符处理
    for index,v10 in enumerate(a1):
        n7 = index % 8
        c_1 = 0
        # 映射字符到数值
        if ord('A')+0x19 >= v10 > ord('4')+5:
            c_1 = v10 - 0x3B
        else:
            c_1 = v10 - ord('4')

        # 根据状态 n7 拼接值到 c
        if n7 == 0:
            c = c_1
        elif n7 == 1:
            # <<5
            c |= 0x20 * c_1
        elif n7 == 2:
            # <<2
            c |= 4 * c_1
        elif n7 == 3:
            # <<7
            c |= (c_1 << 7)
        elif n7 == 4:
            # <<4
            c |= 0x10 * c_1
        elif n7 == 5:
            # <<1
            c |= 2 * c_1
        elif n7 == 6:
            # <<6
            c |= (c_1 << 6)
        elif n7 == 7:
            # <<3
            c |= 8 * c_1

        # print('[%d]'%index,hex(c))
        # 写入输出字符串
        if n7 == 0 or n7 == 2 or n7 == 5:  # 假设这些状态下需要写入
            continue
        out.append(c & 0xFF)  # 取 c 的低 8 位作为字符写入
        c >>= 8  # 右移8位,准备下一次拼接
    # 返回最终结果
    return bytes(out)  # 将列表转换为字符串
def shift_data(input_string:bytes, vector_size=4)->bytes:
    vector = [0] * vector_size
    
    n = 0  # 当前字符的左移位数
    # 遍历字符串的每个字符
    for i,char in enumerate(input_string) :
        # 计算向量索引
        index = i % vector_size
        
        # 将当前字符的 ASCII 值(低 8 位)左移 v11 位后,与向量中对应元素按位或
        vector[index] |= (char& 0xFF) << n
        
        # 如果当前索引是向量的最后一个元素,则增加左移量
        if index == vector_size - 1:
            n += 8
        
    out=b''
    for x in vector:
        out+=x.to_bytes(4,'little')
    return out

# 2.5.2  computer id 解码
def xxcode_140343B60(data:bytes):
    out=sub_1403479E0(data)
    out=shift_data(out)
    return out


original_pubkey_str = '308201B63082012B06072A8648CE3804013082011E02818100AB5F120721601637086D623B6FC4055AF2BA3C8E0F857FBCACD10847B2955D3D5D5C7981F501AD433EFC8C490BA1BD012ABD80B367F6E7AFEC2FCA6194B53EA556333C46A9C860EAC4EA4933E0F4DCD0DFC105D0E335C35B1172963C1B204EA06EA06EE16D66659E4C100625404E669838EA4BBCA897B0FFF324BAAD68B4FDEB021500DE2089D8D203BCD0858D724B7685F22EDD9121E5028180023866E3B1B0142072BB464E1C4DC0A614F45B0F59D3D9F1D1E97D322331AA0D997EE31A7BD22C3661CDE5B2C3176CF89A9CA8FE93DDCAF856B18156A44D02834AF4A6B4735C0CF3DA2B2B4D2A861D883AD4D662927080ACB5DB9E668AA0E973769EBBA2EB07E1A641CA8B3F579553196D0047829F646172BD6FB3BB187653F503818400028180575B8A6B48A405FB605752B9992711E92D943447F34F872495010733D08E859E68D7B54BECFEEDC52761ADDC7D51764D4AD36BD31EEB34BC00BD06FEF856DFA57DF01D4F905D3ACD40569077653290AD4FF2C48C96DC3DA37D81DC007BA2C35010C9DACF21F26311EAFF765DB73E463FF45A6316039CA3020E067784E20AF33E'
original_encode_pubkey_str = re_decode_5332A4(original_pubkey_str.encode())

def dec_dat(fpath='vbc4ekey.dat',k = 'VbaCompiler for Excel') -> str:
    d = b''
    with open(fpath, 'rb') as f:
        d = f.read()[0x14:]
    x = sub_532C99(k, d)
    # print(x.decode())
    return x.decode()


# generate_dsa_key生成,可自定义
dsa_private2 = '3082014B0201003082012B06072A8648CE3804013082011E02818100857C48814FD5189563978EA492289E59254F3B9C58E36DB18F862919384CEAFCAEE9132BE3FDD4E2E5A3FBB4503C3FA0359B3E690C9BD1F66E13F3710D9A3D40F32208DEDD3ED5B2CB8659A6268034B109DCA80B3A80AEE248C9FD1CF9A00B3762678ABE83C0033FD7C6E7295693D44694743ABBC57A81F15A63FD2975C7EA95021500B71D6B870091B2174CFBD4FCDB6386012D7C1B0B028180642D1171AB3DF46794426769B739EC63A42ECE53ADDDA53050DB5D252BA033D5FD42CED572734D4C120A1E57BE6B630B80B1234C5B0220839BF282A9DECD6D892AA8DE75728051B5CA8BC8B85971DCFD50A0C9AF5041162783CA9D3201E5F0558C2CCFE2DBF9B9825EEE8BB763770D1462316A1C524867C30FED1D23B6141BD20417021500AC09340B8A9AC4EAAEE4179DD2E5CC8872F731D1'
dsa_public2 = '308201B63082012B06072A8648CE3804013082011E02818100857C48814FD5189563978EA492289E59254F3B9C58E36DB18F862919384CEAFCAEE9132BE3FDD4E2E5A3FBB4503C3FA0359B3E690C9BD1F66E13F3710D9A3D40F32208DEDD3ED5B2CB8659A6268034B109DCA80B3A80AEE248C9FD1CF9A00B3762678ABE83C0033FD7C6E7295693D44694743ABBC57A81F15A63FD2975C7EA95021500B71D6B870091B2174CFBD4FCDB6386012D7C1B0B028180642D1171AB3DF46794426769B739EC63A42ECE53ADDDA53050DB5D252BA033D5FD42CED572734D4C120A1E57BE6B630B80B1234C5B0220839BF282A9DECD6D892AA8DE75728051B5CA8BC8B85971DCFD50A0C9AF5041162783CA9D3201E5F0558C2CCFE2DBF9B9825EEE8BB763770D1462316A1C524867C30FED1D23B6141BD20381840002818011411AB9146289D4876181306F1F102CE6EC363108D352132C4B2A2A52BA78D90B6607B850FAC705C2B293D6CEA62C51DE2C11F33B8E327E3811A7EA01181470BAC957C45A41D7604BAD50893A16A5C8C3E74EBA39AA94884CA82D6DC51449C0AF457A5C7D746FADFD7F293099ED1A39826A0C2219EB958DD08EB96780C3BF98'

# license_type :{standard,professional}
def build_key_version1(private_key: DSA.DsaKey, mid: str,
                       out_path='test_vbc4ekey.dat',
                       user='ikun_v1',
                       mail='ikun_v1@ikun.com',
                       product='VbaCompiler for Excel',
                       version=1,
                       license_type='professional',
                       dtf=int.from_bytes(
                           date_to_systemtime('2024/12/12'), 'little'),
                       dtt=int.from_bytes(
                           date_to_systemtime('2099/12/12'), 'little')
                       ):
    mid=mid.replace('-','')
    # user|email|VbaCompiler for Excel|version|professional_type|mid|dtf|dtt| (opts|mainto)
    # can null==> opts   %d|
    # can null==> mainto %d|
    text = f'{user}|{mail}|{product}|{version}|{license_type}|{mid}|{dtf}|{dtt}|'
    key = dsa_sign_data_sha1(private_key, text.encode()).hex().upper()
    # cidcrc = zlib.crc32(mid.encode()).to_bytes(4, 'little').hex()
    temp =xxcode_140343B60(mid.encode())
    cid_data=temp[:-4]
    cidcrc=temp[-4:]
    # print('[-]check crc:',zlib.crc32(cid_data).to_bytes(4, 'little')==cidcrc)
    msg2=f'{user}|{mail}|{product}|{dsa_private2}|{dsa_public2}|'.encode()
    key2=dsa_sign_data_sha1(private_key,msg2).hex().upper()
    ftext = [
        f'user={user}',
        f'mail={mail}',
        f'product={product}',
        f'version={version}.6',
        f'license_type={license_type}',
        f'dtf={dtf}',
        f'dtt={dtt}',
        f'cidcrc={cidcrc.hex()}',
        f'key={key}',
        f'int={dsa_private2}',
        f'ext={dsa_public2}',
        f'key2={key2}',
    ]
    # f'activation=on',
    k = 'VbaCompiler for Excel'
    data = '\n'.join(ftext).encode()
    enc = inverse_sub_532C99(k, data)
    with open(out_path, 'wb') as f:
        f.write(header)
        f.write(enc)
    print('[-]enc finished!,out:', out_path)
    print('[#]you need patch pub_key!')
    print('[+]original_encode_pubkey:\n%s\n\n' %
          original_encode_pubkey_str.decode())
    enc_pub_key = re_decode_5332A4(public_key_der.hex().upper().encode())
    print('[+]replace to ==>your_encode_pubkey:\n%s\n\n' %
          enc_pub_key.decode())
    # dec_pub_key=decode_5332A4(enc_pub_key.decode())
    # print('[#]dec_pub_key:',dec_pub_key.decode())
    # print('check pub key:',dec_pub_key==public_key_der.hex().upper().encode())
    dec = dec_dat(out_path)
    print(f'[-]test dec {out_path}:')
    print(dec)
    print()


def build_key_version2(private_key: DSA.DsaKey, mid: str, 
                       out_path='test2_vbc4ekey.dat',
                       user='ikun_v2',
                       mail='ikun_v2@ikun.com',
                       product='VbaCompiler for Excel',
                       version=2,
                       license_type='professional',
                       dtf=int.from_bytes(
                           date_to_systemtime('2024/12/12'), 'little'),
                       dtt=int.from_bytes(
                           date_to_systemtime('2099/12/12'), 'little')
                       ):
    mid=mid.replace('-','')
    # user|mail|VbaCompiler for Excel|version|professional_type|mid|dtf|dtt|
    text = f'{user}|{mail}|{product}|{version}|{license_type}|{mid}|{dtf}|{dtt}|'
    key = dsa_sign_data_sha1(private_key, text.encode()).hex().upper()
    # cidcrc = zlib.crc32(mid.encode()).to_bytes(4, 'little').hex()
    # temp =xxcode_140343B60(mid.encode())
    # cid_data=temp[:-4]
    # cidcrc=temp[-4:]
    # print('[-]check crc:',zlib.crc32(cid_data).to_bytes(4, 'little')==cidcrc)
    xx_mid=xxcode_140343B60(mid.encode())
    a=xx_mid[:4]
    b=xx_mid[4:4*2]
    c=xx_mid[4*2:4*3]
    d=xx_mid[4*3:]
    cidcrc=d
    msg2=f'{user}|{mail}|{product}|{dsa_private2}|{dsa_public2}|'.encode()
    print('[-]msg2:',msg2)
    key2=dsa_sign_data_sha1(private_key,msg2).hex().upper()
    mid3=(c[::-1]+a[::-1]+b[::-1]).hex()
    msg3=f'{mail}|{mid3}|{dtf}|{dtt}'.encode()
    print('[-]msg3:',msg3)
    key3=dsa_sign_data_sha1(private_key,msg3).hex().upper()
    ftext = [
        f'user={user}',
        f'mail={mail}',
        # f'product={product}',
        f'version={version}.5',
        f'license_type={license_type}',
        f'dtf={dtf}',
        f'dtt={dtt}',
        f'cidcrc={cidcrc.hex()}',
        f'key={key}',
        f'int={dsa_private2}',
        f'ext={dsa_public2}',
        f'key2={key2}',
        f'key3={key3}',
    ]
    # f'activation=on',
    k = 'VbaCompiler for Excel'
    data = '\n'.join(ftext).encode()
    enc = inverse_sub_532C99(k, data)
    with open(out_path, 'wb') as f:
        f.write(header)
        f.write(enc)
    print('[-]enc finished!,out:', out_path)
    print('[#]you need patch pub_key!')
    print('[+]original_encode_pubkey:\n%s\n\n' %
          original_encode_pubkey_str.decode())
    enc_pub_key = re_decode_5332A4(public_key_der.hex().upper().encode())
    print('[+]replace to ==>your_encode_pubkey:\n%s\n\n' %
          enc_pub_key.decode())
    # dec_pub_key=decode_5332A4(enc_pub_key.decode())
    # print('[#]dec_pub_key:',dec_pub_key.decode())
    # print('check pub key:',dec_pub_key==public_key_der.hex().upper().encode())
    dec = dec_dat(out_path)
    print(f'[-]test dec {out_path}:')
    print(dec)
    print()





def dec_rkey(fpath,k='ikun_v2@ikun.com'):
    info=dec_dat(fpath,k)
    print(f'[-]{fpath} text:\n{info}')
    l=info.splitlines()
    s=[]
    for x in l:
        a=x.split('=')[1]    
        s.append(a)
    k=s[-1]
    ss='|'.join(s[:-1])+'|'
    return s,ss

def verify_rkey(mid:str,fpath,k,dsa_public_hexstr=dsa_public2):
    mid=mid.replace('-','')
    info_l,info_msg=dec_rkey(fpath,k)
    
    info_msg+=mid+'|'
    
    info_msg=info_msg.encode('utf-16-le')
    signature=bytes.fromhex(info_l[-1])
    print(f'[-]rkey msg:{info_msg}')
    print(f'[-]rkey signature:{signature}')
    dsa_public2_key=DSA.import_key(bytes.fromhex(dsa_public_hexstr))
    ret=dsa_sha1_verify_signature(dsa_public2_key,info_msg,signature)
    print('[-]verify:',ret)
    pass

if __name__ == "__main__":

    # dat_info=dec_dat('vbc4ekey.dat')
    # print(dat_info)

    mid='copy your computer id'

    # rkey 文件解密的key为许可的email
    # verify_rkey(mid,'ssss.rkey','ikun_v2@ikun.com')
    print('--------------------------------------------------------------------------------\n\n\n\n')

    # build_key_version1(private_key,mid,'v1_vbc4ekey.dat')
    # print('--------------------------------------------------------------------------------\n\n\n\n')
    build_key_version2(private_key,mid,'v2_vbc4ekey.dat')
    # print('--------------------------------------------------------------------------------\n\n\n\n')


    pass





测试

在处理MD5验证后xlsm打开正常,并执行了自定义vba代码(弹窗),

image-20241229122855454

posted @ 2024-12-29 12:47  DirWangK  阅读(24)  评论(3编辑  收藏  举报