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;
}
md5校验(关注生成的xxxx_xlsm_32.dll)
内置md5
该内嵌md5 为vbaclr4e.exe 在compile时传递,可以在生成过程在temp目录中找到相关文件
可在vbaclr4e.exe DeleteFileW 设断点,便可获取中间编译文件
签名校验(关注释放到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校验
regkey 中key校验
相关信息可参考后面py脚本中的verify_rkey
key3 校验
#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代码(弹窗),