char *LicenseHash = "LAvi4vZ1CoV9Pyfhsbk75Z3pZp4p49OTPf8ez4IlaM00QBxfz9HQyDalE6v872l643gxg6AC4864
6c5iIfjm3z8tt1leaGXUsk8z"
"KK19wyTLB23ZBobk228mS75fGjIep11NH2t433410Ii49aR20fffbfffb020100028181006e454
c3dc28fb834ea064dd64323a"
"e1cf77c83f172a8b4d02875d0e33e0b758274a6be1c2bfe4c88c3eec36e43ef2cf82a0746680
d0601cd6bd3a3ce8b93af6a7"
"a6076bc2c04d77771237f49464296c7d2cce29d903a9b86b5a83ac527b7add4c0583b2dcb220
9485a9b1f81fe831cea73d0b"
"ff74377b7eaff320d513fcb8b2902030100010281801124258f5e2cb0224d94b049d3c2faa05
71a5c7a4e6c0a3b6766b5137"
"6041afeadfa7387f690bfbdecd3333ef3417d389073f40e5da3e6e8d34fa1618c91e70d0e6ea
cd7665e760cec0098214ea1f"
"ba9b9d171e90eeb263ecf09625d20bf1cfe5fffcab1339c50117636b32cab47122cb225f677c
5968d7bdcb1eaabb6f2b1470"
"240b0ad37f9efd3286f17a2b7e8d34bff107aa60d4297022604ab4aa895a266ed72cd8d2bbaf
80146b9133b259b6eb562eec"
"6d76a94f4044bb1f8e3031f7905713702409fc78ad8d2175022f279734f8b179ac064257bb88
1e0e4baafb5593b16fe692c5"
"c818a879c78eebde92ce795c2dc00395bdddd635069014a051207c75d8c969f0241007c254b9
719b933c9543f67a17790082"
"ba93333501e52492ab0be6655b6e74264902169a01892b502698bff320809f6f9b7c938c06ff
2f0c99f0ccd6df91924e3024"
"091670dbbf2b4b48099185d0f6e0cb50f90136c5ba415fd10c705c986348ea13775978d718bb
ba1c7c182d3dd3dcee7854b4"
"cc443b4d15a12fa3a4649efa0191102409cdfda5836f6a0c171855d9c05814681d8b4456ccea
20115d04a03c4feae4bea4e0"
"e350e9cf046ebf213d67ec8eddd63a3fa491a4b0115b9afbce000b2271badfffefffe0201000
2820100aa4e25f7bf872578b"
"52d45b6dcc68b20f58d98c01cf756c4c7bfb790d611fbd14be02cb01665a7f2afe1004f9d72e
0b21f98f0525f6c5a04d8385"
"c0d4f643c976f6a6204c2bb7c546e85d6179334b35ec975d411fac2af3f4fee1c5c0e3f8b42d
0fe2cc17ed9f914be0f739ef"
"727b5207333240c8dd1cb6c93d24a47088619912cf7a1cd57a638d7d1f2b0a71fb7ee99624db
f4a3c552947211fea3dc0443"
"99049adc7d9eb62281d07b2e70af429159acecaeca3052f67155d23d7cffba16d54b14803a74
19bab2764b84ee88dbcda088"
"b8c09fd8aa8f1b168e910d219fb6d14c39354d139b586ea6412d868b34cbbbf5a3737f3a9edb
33728dad6e3d871a5a502030"
"100010282010001f2a80aaea134d6432f914a7a0eccf4be4ceae2eda1e2ec9817f303f57d84a
abc3faee5d507bf75438f8da"
"c461c7ed742b71783eb02a75542af83015af7b0f2b976971d94a8462530136df8b1e4152b751
f4378462ce25f6ac25f86d50"
"48de04b2ed10e65d7f28fa06c8c51bf39701ffecea65a52129a229e8935f4c7334373bf0b6da
992299358930a3951521ba0b"
"9078d281ae3c691a1e74359242c03925ba6a5a11816adb08680e2647242483e5ad99ad4cf565
3dcf398d7e97922355efb7b6"
"aba92e1e7564dbfc795538b9b62138e06cd8864223b65fc7de1c2c841beea40cf5f4de8e2dc2
91465901a4e4aa338de73ef2"
"400723e9b099a8c48f435ad71028180f48effd9767e05db6744898d974bc431b0474a5882cd6
c0856eacbff60cda7bd58de5"
"b7744519d8201de2d15fa546642bad35bb385eee6d641046ff6f270bf52b30b03cfcb1438cf0
e3e3ed464e13796cac62e379"
"633636ae3cc124880480b6c75d4afa1439159b95d05a14b3aa95119b3b698ab5a027a0dd7653
037bcdad0b5028180b245d72"
"525aaf1557ab7234c9db32cc53c6bb8ce5c64d9929cd016a34bff9efb6187ef2bd49766e1024
7d2116a3f7a58f95a0a0fcf3"
"6baa91be453f0411ceb8837b67fd8aa82067acd9c9b17748ee666729591d2fd61d264451f699
b18efdb94dfb3f649c44cf8f"
"ccfd8d1f48d937219f6f1021e4dba89dd1d9366ce8be7c731028180ef27bc6993e7afa09c7f3
8a8df50b79f03cce39664eba"
"fff03c87f0fd141ac0c8d907d0fc81eac120bf3925190c8e26bd53985ceac6631154ae5a4fc7
09ff369532fad5d9231c7502"
"c94906ab1f050a544ccc33b96c5d02e2270595ccf1e2515beb0ebc39f48cfde666a700e1a62a
3ada7c223d045a8706582c33"
"081014d5695028180115f00d1dbd3c6645feacc83c0b21ee0b13c85eb85d145ea256199731c0
ab2812845c8a66ab6be0d9ad"
"d48b8edb375c689b59e596005a336ab431a72c4c57ea5ed57d92951a40de1957b771958003ca
f2ae72c28750c3d260c1e3d7"
"a2179edd999dabd5afec7c9534925857fe0f3c720849183755479a2c96b025bf83065cb61028
180cc2bc4ea381710bba2232"
"ada7b6691d01276608a8e4cbc108fb02909536139f4b434d4d6abb13139a6e4b6106d1ac92ef
fd9864ddfe23b40d7c4c791c"
"b3cc001b1553d1b166795f73b5ff4d1a5079223e784e39f6b9fb46548e9400ffaf23177fea77
3a8ffb85450d7575fd8b449b"
"960c79c168d67f3f46550619f0513fe4b78";
If you look depper, you will see two very interesting values in the hash, fffefffe and fffbfffb, so now lets play with this and you got this structure
reg_hash (fffbfffb) private_key2 (fffefffe) private_key
reg_hash Can have Capitals letters as you can see
private_key and private_key2 can only hold 0..9 and a..f (non-capitals)
Pretty nice start isn't it? Later you will find the use for this.
License Structure
This the main estructure
CODE
struct t_WL_License_File
{
DWORD unk0; // 0 :: DWORDs reg_hash[8] + reg_hash[C]
BYTE unk4; // 4 :: expiration_days Value if 0 then RDTSC
BYTE unk5; // 5 :: executions Value if 0 then RDTSC
DWORD unk6; // 6 :: expire_date Value if 0 then RDTSC
DWORD unkA; // A :: global_time Value if 0 then RDTSC
DWORD unkE; // E :: country Value if 0 then RDTSC
DWORD unk12; // 12 :: run_time | expiration_days Value if 0 then RDTSC
DWORD unk16; // 16 :: DWORDs reg_hash[10] + reg_hash[14]
BYTE unk1A; // 1A :: CheckSum HWID
WORD unk1B; // 1B :: CheckSum HWID
WORD unk1D; // 1D :: CheckSum HWID
WORD unk1F; // 1F :: CheckSum HWID
WORD unk21; // 21 :: CheckSum HWID
WORD unk23; // 23 :: RDTSC
WORD unk25; // 25 :: Flags
DWORD unk27; // 27 :: RDTSC
WORD unk2B; // 2B :: CheckSum User
WORD unk2D; // 2D :: CheckSum from 0..2D
DWORD unk2F; // 2F :: RDTSC
WORD unk33; // 33 :: WORDs reg_hash[18] + (DWORDs reg_hash[18] >> 0x10);
WORD unk35; // 35 :: CheckSum User
WORD unk37; // 2D :: CheckSum from 0..37
DWORD unk39; // 39 :: Always is 0xFFFFFFFF
DWORD unk3D; // 3D :: Always is 0xFFFFFFFF
BYTE unk41[0x2000 - 0x41]; // Empty Slots for other purposes
};
RDTSC is Time Stamp Counter ( an asm instruction) return its values in eax:edx, is like a rand() function
Unk0
is the result of the next operation = *(DWORD *)((LPBYTE)hash_key + 0x08) + *(DWORD *)((LPBYTE)hash_key + 0x0C);
Hey! so this value is checked if the License belong to that program!, nice start to avoid corrupt license message!, but is not as easy as i said xd, but for example look, reg_hash has this feautres, can hold from '0' (0x30) to 'z' (0x7A), so theoperation and the result will be between 0x60 to 0xF4, for eaxmple
0x5456886D - you would bever find this value on unk0 position! why? 0x54 does not belong to the range as well as 0x56
0xF5A6E89D - the same 0xF5 does not belong to the range!
0xCCA6E89D - ... Yes you can find it
Well, nice to see, but why do i need this?, simple, with this limitations you can discard values from Virtual Machine when comapring this values.
Unk4
Will hold the expirations days from the License, ONLY if the value is below 255!
Unk5
Contains the number of executions before license expires
Unk6
Contains the expire date in this format year - month - day
day is a byte value, is on the 0..7 bit
month is a byte value, is on 8..15 bit
year is a word value, is on a 16-32 bit
For example 07D70204 says that will license will expire on 2007-March-04
UnkA
Contains the Global Time before license expires
UnkE
Contains the locked country
Unk12
Well, i think here is a bug, coz on generated license one value destroys another, maybe i reversed i little bad
CODE
if ( run_time != 0 && expiration_days > 255 )
WinLicenseFile->unk12 = expiration_days | 0x80000000;
if ( run_time == 0 && expiration_days > 255 )
WinLicenseFile->unk12 = expiration_days | 0x80000000;
else if ( run_time != 0 && expiration_days <= 255 )
WinLicenseFile->unk12 = expiration_days;
else if ( run_time == 0 && expiration_days <= 255 )
WinLicenseFile->unk12 = RDTSC();
Unk16
Same as unk0
Unk1A
Take for example this HWID
105A-DF45-3533-AACC-328D-DEDF-6789-ABCD
Conatins Checksum of HarwareID, is the result of the addition between Orange and purple (0x6A)
Unk1B
Take for example this HWID
105A-DF45-3533-AACC-328D-DEDF-6789-ABCD
Conatins Checksum of HarwareID, is the result of the addition between Orange and purple
Unk1D
Take for example this HWID
105A-DF45-3533-AACC-328D-DEDF-6789-ABCD
Conatins Checksum of HarwareID, is the result of the addition between Orange and purple
Unk1F
Take for example this HWID
105A-DF45-3533-AACC-328D-DEDF-6789-ABCD
Conatins Checksum of HarwareID, is the result of the addition between Orange and purple
Unk21
Take for example this HWID
105A-DF45-3533-AACC-328D-DEDF-6789-ABCD
Conatins Checksum of HarwareID, is the result of the next operation
CODE
unk1A ^ unk1B ^ unk1D ^ unk1F ^ *(WORD *)((LPBYTE)reg_hash + 0x64);
This is very special coz also have the a reg_ash value
Unk23
Random Value
Unk25
FLAGS!!, yea, what are you looking for, this contains which restructions are applied to every application
CODE
if ( expiration_days <= 255 )
wLicenseFlags |= 1;
if ( executions != 0 )/*25*/
wLicenseFlags |= 2;
if ( expire_date != NULL )
wLicenseFlags |= 4;
if ( hwid != NULL )
wLicenseFlags |= 8;
if ( country != 0 )
wLicenseFlags |= 0x40;
if ( run_time != 0 || expiration_days > 255 )
wLicenseFlags |= 0x20;
if ( global_time != 0 )
wLicenseFlags |= 0x10;
Unk27
Random Value
Unk2B
Contains the addition of Checksums Of User, Company, CustomData and HWID ( yeah aother check for Hardware ID )
Take this function for example
CODE
DWORD CreateCheckSumStr(const char * szString)
{
BYTE magic_high = 0;
BYTE magic_low = 0;
BYTE handler = 0;
for(int i=0;szString[i] != 0; i++)
{
if ( handler != 0 )
{
magic_low ^= szString[i];
handler--;
}
else
{
magic_high += szString[i];
handler++;
}
}
return MAKEWORD(magic_low, magic_high);
}
So unk2B is CreateCheckSumStr(user) + CreateCheckSumStr(company) + CreateCheckSumStr(CustomData) + CreateCheckSumStr(HWID)
Caution.- If in you application is enabled the Network instances you will see that CustomData Is a Little different, take for example this custom data
Without Network instances : 'This is Custom Data'
With Network instances : '*NL*0003This is Custom Data' <- in this case You got network instances ( is 3 ), *NL* is like a 'flag', that tells WL that Application have Network Instances, now, The CreateCheckSumStr in this case is only the purple data (not the *NL*)
CreateCheckSumStr(CustomData + 0x08);
Unk2D
Is the Result of a Checksum of the generated license from 0..2C
CODE
BYTE magic_h = 0;
BYTE magic_l = 0;
BYTE handler = 0;
for( i=0; i< 0x2D; i++ )
{
if ( handler != 0 )
{
magic_l += lpWLBuffer[i];
handler--;
}
else
{
magic_h ^= lpWLBuffer[i];
handler++;
}
}
WinLicenseFile->unk2D = MAKEWORD(magic_l, magic_h);
Unk2F
Random Value
Unk33
is the result of the next operation = *(WORD *)((LPBYTE)hash_key + 0x18) + *(WORD *)((LPBYTE)hash_key + 0x1A);
Unk35
Same as unk2B
Unk37
Is calculated acordind this
CODE
magic_h = 0;
magic_l = 0;
handler = 0;
for( i=0; i< 0x37; i++ )
{
if ( handler != 0 )
{
magic_l += lpWLBuffer[i];
handler--;
}
else
{
magic_h ^= lpWLBuffer[i];
handler++;
}
}
Unk39
Always is 0xFFFFFFFF
Unk3D
Always is 0xFFFFFFFF
Unk41 to the end
This part contains the user, company, customdata (including *NL* part) and HWID, remember, this are NULL terminatedStrings, adn the separation between then are the 0xFFFFFFFF. Here you got an example
CODE
77E462E0 75 73 65 72 00 FF FF FF user.ÿÿÿ
77E462E8 FF FF FF FF FF 63 6F 6D ÿÿÿÿÿcom
77E462F0 70 61 6E 79 20 6E 6F 6E pany non
77E462F8 65 00 FF FF FF FF FF FF e.ÿÿÿÿÿÿ
77E46300 FF FF 2A 4E 4C 30 30 32 ÿÿ*NL002
77E46308 33 44 65 61 69 6C 74 73 3Deailts
77E46310 20 6F 66 20 63 75 73 74 of cust
77E46318 6F 6D 65 72 00 FF FF FF omer.ÿÿÿ
77E46320 FF FF FF FF FF 31 32 33 ÿÿÿÿÿ123
77E46328 34 2D 35 36 37 38 2D 39 4-5678-9
77E46330 41 42 43 44 2D 45 46 31 ABCD-EF1
77E46338 32 2D 33 34 35 36 2D 37 2-3456-7
77E46340 38 39 41 2D 32 34 33 34 89A-2434
77E46348 2D 31 31 31 31 00 FE FF -1111.þÿ
77E46350 FF FF FE FF FF FF 23 42 ÿÿþÿÿÿ#B
77E46358 34 23 4#
note that after HWID the end is -2, not -1(0xFFFFFFFF),
But i put another Value after that, this is another checksum i mean the 0x23344223
This is calculated with this operation
CODE
dwOffset = 0;
BYTE magic_cl = 0;
BYTE magic_ch = 0;
BYTE magic_bl = 0;
BYTE magic_bh = 0;
handler = 0;
while ( *(DWORD *)(lpWLBuffer + dwOffset) != -2 && *(DWORD *)(lpWLBuffer + dwOffset + 4) != -2 )
{
if ( handler == 0 )
magic_bl += lpWLBuffer[dwOffset];
else if ( handler == 1 )
magic_bh += lpWLBuffer[dwOffset];
else if ( handler == 2 )
magic_cl ^= lpWLBuffer[dwOffset];
else if ( handler == 3 )
magic_ch ^= lpWLBuffer[dwOffset];
handler++;
if ( handler == 4 )
handler = 0;
dwOffset++;
}
*(DWORD *)(lpWLBuffer + dwOffset + 8) = MAKELONG(MAKEWORD(magic_cl,magic_ch), MAKEWORD(magic_bl,magic_bh));
Howevber you will find this is not the end, you will find 0x100 more bytes after this Checksum Value, this vlues are a rsa sign, made from LicenseUnique Hash
This is the structure, but it not all, in the process, some parts are encrypted, many times, that i will complete later
Tracing License Building
The text above is just for say what you would find on a decrypted License File, but first it is necesary to decrypt it
So Here is the values set before the first encrypt (in order )
CODE
WinLicenseFile->unk0
WinLicenseFile->unk16
WinLicenseFile->unk27
WinLicenseFile->unk33
WinLicenseFile->unk2F
WinLicenseFile->unk1A
WinLicenseFile->unk1B
WinLicenseFile->unk1D
WinLicenseFile->unk1F
WinLicenseFile->unk21
WinLicenseFile->unk2B
WinLicenseFile->unk35
WinLicenseFile->unk25
WinLicenseFile->unk4
WinLicenseFile->unk5
WinLicenseFile->unk6
WinLicenseFile->unkE
WinLicenseFile->unk12
WinLicenseFile->unkA
WinLicenseFile->unk23
Now Here is the first crypt
CODE
for( DWORD i=0; i< 0x23; i++ )
{
WinLicenseFile[i] ^= LOBYTE(WinLicenseFile->unk23);
WinLicenseFile[i] += HIBYTE(WinLicenseFile->unk23);
}
So unk23 is used to crypt the bytes from 0..22
Now, a Checksum is created
CODE
WinLicenseFile->unk2D
Now the Second Crypt begins, take note that it uses the hash, so you nknow the range
CODE
//////////////////////////////////////////////////////
// Method 01
DWORD dwMagicKey_01[4];
dwMagicKey_01[0] = *(DWORD *)((LPBYTE)hash_key + 0x1C) + *(DWORD *)((LPBYTE)hash_key + 0x2C);
dwMagicKey_01[1] = *(DWORD *)((LPBYTE)hash_key + 0x20) + *(DWORD *)((LPBYTE)hash_key + 0x30);
dwMagicKey_01[2] = *(DWORD *)((LPBYTE)hash_key + 0x24) + *(DWORD *)((LPBYTE)hash_key + 0x34);
dwMagicKey_01[3] = *(DWORD *)((LPBYTE)hash_key + 0x28) + *(DWORD *)((LPBYTE)hash_key + 0x38);
Crypt_Method_01(lpWLBuffer, 0x33, dwMagicKey_01);
void __stdcall Crypt_Method_01(LPBYTE lpBuffer, int iBufferLen, LPDWORD pMagicKey)
{
__asm
{
PUSHAD
MOV EDI,DWORD PTR SS:[EBP+0x8]
MOV ECX,DWORD PTR SS:[EBP+0xC]
SHR ECX,0x3
JMP SHORT ELoop
SLoop: PUSH 0x20
PUSH DWORD PTR SS:[EBP+0x10]
PUSH EDI
CALL Method_01
ADD EDI,0x8
DEC ECX
ELoop: OR ECX,ECX
JNZ SHORT SLoop
POPAD
}
}
void __stdcall Method_01(void * pBuffer, void * pKey, int len)
{
__asm
{
PUSHAD
MOV EDI,DWORD PTR SS:[EBP+0x8]
MOV ESI,DWORD PTR SS:[EBP+0xC]
MOV EBX,DWORD PTR DS:[EDI]
MOV ECX,DWORD PTR DS:[EDI+0x4]
XOR EAX,EAX
JMP SHORT ELoop
SLoop: ADD EAX,0x9E3779B9
MOV EDX,ECX
SHL EDX,0x4
ADD EBX,EDX
MOV EDX,DWORD PTR DS:[ESI]
XOR EDX,ECX
ADD EBX,EDX
MOV EDX,ECX
SHR EDX,0x5
XOR EDX,EAX
ADD EBX,EDX
ADD EBX,DWORD PTR DS:[ESI+0x4]
MOV EDX,EBX
SHL EDX,0x4
ADD ECX,EDX
MOV EDX,DWORD PTR DS:[ESI+0x8]
XOR EDX,EBX
ADD ECX,EDX
MOV EDX,EBX
SHR EDX,0x5
XOR EDX,EAX
ADD ECX,EDX
ADD ECX,DWORD PTR DS:[ESI+0xC]
DEC DWORD PTR SS:[EBP+0x10]
ELoop: CMP DWORD PTR SS:[EBP+0x10],0
JA SHORT SLoop
MOV DWORD PTR DS:[EDI],EBX
MOV DWORD PTR DS:[EDI+0x4],ECX
POPAD
}
}
Now, after this encryption, another is made (what i call Method 2), take a look, this time hash_key is NOT altered, so you can find the original one in the encrypted application, like an null terminate string
CODE
//////////////////////////////////////////////////////
// Method 02
DWORD dwMagicKey_02[9];
dwMagicKey_02[0] = *(DWORD *)((LPBYTE)hash_key + 0x44);
dwMagicKey_02[1] = *(DWORD *)((LPBYTE)hash_key + 0x48);
dwMagicKey_02[2] = *(DWORD *)((LPBYTE)hash_key + 0x4C);
dwMagicKey_02[3] = *(DWORD *)((LPBYTE)hash_key + 0x50);
dwMagicKey_02[4] = *(DWORD *)((LPBYTE)hash_key + 0x54);
dwMagicKey_02[5] = *(DWORD *)((LPBYTE)hash_key + 0x58);
dwMagicKey_02[6] = *(DWORD *)((LPBYTE)hash_key + 0x5C);
dwMagicKey_02[7] = *(DWORD *)((LPBYTE)hash_key + 0x60);
dwMagicKey_02[8] = 0;
Crypt_Method_02(lpWLBuffer, 0x33, dwMagicKey_02);
void __stdcall CLicense::Crypt_Method_02(LPBYTE lpBuffer, int iBufferLen, LPDWORD pMagicKey)
{
Method_02(lpBuffer, iBufferLen, pMagicKey);
}
void __stdcall Method_02(void * pBuffer, int len, void * pKey)
{
__asm
{
PUSHAD
MOV EAX,DWORD PTR SS:[EBP+0x8]
MOV ECX,DWORD PTR SS:[EBP+0xC]
MOV EDX,DWORD PTR SS:[EBP+0x10]
MOV EDI,EAX
MOV ESI,EDX
MOV EAX,0x41363233
XOR AL,BYTE PTR DS:[ESI]
CALL SubVarian_Method_02
MOV EBX,EAX
XOR AH,BYTE PTR DS:[ESI]
CALL SubVarian_Method_02
SHR ECX,0x2
MOV EDX,ECX
label1: XOR DWORD PTR DS:[EDI],EAX
MOV CL,AL
ADD EDI,0x4
ROL EBX,CL
XOR EAX,EBX
MOV CL,BH
ROR EAX,CL
ADD EBX,EAX
DEC EDX
JNZ SHORT label1
POPAD
}
}
void __declspec(naked) SubVarian_Method_02()
{
__asm
{
PUSH ECX
PUSH ESI
PUSH EDX
XOR EDX,EDX
DEC ESI
label2: INC ESI
XOR AH,BYTE PTR DS:[ESI]
label1: XOR AL,DL
ADD EAX,0x7034616B
MOV CL,AL
ROR EAX,CL
XOR EAX,0x8372A5A7
DEC DX
JNZ SHORT label1
CMP BYTE PTR DS:[ESI],0
JNZ SHORT label2
POP EDX
POP ESI
POP ECX
RETN
}
}
Now, the next values are set
CODE
WinLicenseFile->unk37 -- Checksum
WinLicenseFile->unk39
WinLicenseFile->unk3D
WinLicenseFile User, Company and Custom Data and HWID
After this, again the License File is encrypted using unk0, thi part Crypts the Usser, Company and Custom Data and HWID
CODE
DWORD dwMagic_02 = WinLicenseFile->unk0;
__asm
{
pushad
mov esi, dword ptr ss:[lpWLBuffer]
add esi, 0x39
mov eax, dword ptr ss:[dwMagic_02]
jmp Init
Start: xor byte ptr ds:[esi], al
add byte ptr ds:[esi], ah
ror eax, 1
inc esi
Init: cmp dword ptr ds:[esi], -2
jnz Start
cmp dword ptr ds:[esi+4], -2
jnz Start
popad
}
Finally after the 0xFFFFFFFE 0xFFFFFFFE (after HWID ) is created the last Checksum according to the algorithm i put above
Now, we finish with the first stage of protection, here comes the second one
WinLicense Creates a 'sign', according to the License Unique Hash, beetween the fffefffe and the end (i mean private_key), it is always 0x100 bytes
- Using LibTomCrypt lib should be
CODE
rsa_import(Private_Key, Private_Key_Size, &RSA_Key);
WLRegisterSignature((unsigned char *)(&WLLicenseFileKey), iLicenseSize, Signature, &SignatureLen, &RSA_Key);
void WLRegisterSignature(unsigned char * WLStruct, unsigned long WLStructSize, unsigned char * SignatureOut, unsigned long * SignatureLen, rsa_key * key)
{
unsigned char sign[1024];
unsigned long hash_memory_len = 0x400;
register_hash(&sha1_desc);
int hash_id = find_hash("sha1");
sign[0] = 0;
memset(sign + 1, 0, sizeof(sign)-1);
hash_memory(hash_id, WLStruct, WLStructSize, sign, &hash_memory_len);
rsa_v15_sign_hash(sign, hash_memory_len, SignatureOut, SignatureLen, hash_id, key);
}
Now it crypt using rsa algorithm
CODE
iLicenseSize += SignatureLen;
unsigned long LicOfs = 0x7F;
unsigned long NewLicenseSize = 0;
if (iLicenseSize > 0x7F )
{
while ( LicOfs < iLicenseSize )
{
output_buffer += WLRsaProtect(output_buffer, (unsigned char *)(&WLLicenseFileKey), hash_key);
LicOfs += 0x7F;
NewLicenseSize += 0x7F;
}
}
memcpy(output_buffer, (unsigned char *)(&WLLicenseFileKey) + NewLicenseSize, iLicenseSize - NewLicenseSize);
unsigned long __stdcall WLRsaProtect(unsigned char * OutputBuffer, unsigned char * WLLicenseStruct, const char * hash_key)
{
unsigned char crypt_key[0x1000];
rsa_key key;
unsigned long key_pos_1 = Find_substring(hash_key, "fffbfffb") + 8;
unsigned long key_pos_2 = Find_substring(hash_key, "fffefffe");
HexToNumber(hash_key + key_pos_1, crypt_key, (key_pos_2 - key_pos_1) / 2);
unsigned long ProtectSize = 0x7F;
if ( rsa_import(crypt_key, (key_pos_2 - key_pos_1) / 2, &key) != CRYPT_OK )
{
printf("Error importing private key");
return 0;
}
ProtectSize = 0xFE;
rsa_exptmod(WLLicenseStruct, 0x7F, OutputBuffer, &ProtectSize, PK_PRIVATE, &key);
return ProtectSize;
}
This part is more difficult, since it uses rsa, but is pretty interesting once you got it
Licenses on 2.0.5.0
Here is and update to 2.0.5.0, the changes are that HWID is Wchar null termitated strings, Net instances are no longer stored on CustomData String, and there is some new params, Like dwLocalTime (Strores year, month and day, format similar to ExpirationDate), InstallDate
CODE
// Copy HWID
if ( hwid == NULL )
*(BYTE *)(lpWLBuffer + dwOffset++) = 0;
else
dwOffset += strlen(strcpy((char *)(lpWLBuffer + dwOffset), hwid)) + 1;
*( BYTE*)(lpWLBuffer + dwOffset) = 0; // WL Change V2
dwOffset++;
*(DWORD *)(lpWLBuffer + dwOffset) = -3;
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = -3;
dwOffset += 4;
// WL Change V2
*(DWORD *)(lpWLBuffer + dwOffset) = 0x83A9B0F1; // CONSTANT
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = 0x18; // CONSTANT
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = MAKELONG(MAKEWORD(rand(),rand()),MAKEWORD(rand(),rand()));
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = InstallDate;
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = NetInstances;
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = dwLocalTime;
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = -3;
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = -3;
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = -2;
dwOffset += 4;
*(DWORD *)(lpWLBuffer + dwOffset) = -2;
dwOffset += 4;
[竞赛]2024 KCTF 大赛征题截止日期08月10日!