1.
Analysis
Hi guys!
Well, it’s time to keygen a game, isn’t it? Today we’re going to defeat Waga Invader by Waganono. I’ve to say Waganono had an original idea, this game is simple but funny and the crypto protection scheme is really good even if in the last few years it isn't too much used.
Before we start I’ve to said THANKS to Shub-Niggurath and the whole ARTeam, they allowed me to use their template, Waganono for this challenge and for his hints and to jB who helped, supported and lost his free time (and I think also a good part of his patience!) for me.
This game was coded in C with OpenGL (for the game) and Miracl (for the algorithm) so I used a little bit IDA, its signature windows and the miracl.sig (you can find it here: http://beatrix2004.free.fr/pamplemousse/miracl.sig). I wrote “a little bit” in fact it doesn’t recognize many functions but no problem, playing a lot with Miracl I know exactly which are the other unknown functions. Always in IDA you can export the .map file (as I did) and with Olly’s plugins like LoadMap or GODUP you can import this .map file and see the name of all known calls directly in Olly. I’ve modified my .map file with all interested function’s names.
Then… Open it with Olly and search for all referenced text strings until you see something like:
Figure n.1 – Text strings
Hum…
What do you think? Are you thinking RSA, right? Let’s check it out, double left click over the long number and we are exactly in the middle of the algorithm! Bingo! Now load my .map file, in this way you can better understand the algorithm.
>DISASSEMBLY
004036C0 push 10 ; 0x10 = 16 004036C2 push 12C ; 0x12c = 300 004036C7 mov byte ptr ss:[esp+1F],1
004036CC call <wagainva._mirsys> ; init miracl with 300 hexadecimal digits for each big 004036D1 push 0 ; mirvar’s parameter, describes the initial value of big 004036D3 mov dword ptr ds:[eax+230],10 ; hexadecimal base
004036DD call <wagainva._mirvar> ; initialises a big variable
004036E2 push 0 ; mirvar’s parameter, describes the initial value of big 004036E4 mov dword ptr ss:[esp+30],eax ; save the pointer to the reserved memory after mirvar 004036E8 call <wagainva._mirvar> ; initialises a big variable
004036ED push 0 ; mirvar’s parameter, describes the initial value of big 004036EF mov dword ptr ss:[esp+30],eax ; save the pointer to the reserved memory after mirvar 004036F3 call <wagainva._mirvar> ; initialises a big variable
004036F8 push 0 ; mirvar’s parameter, describes the initial value of big 004036FA mov dword ptr ss:[esp+3C],eax ; save the pointer to the reserved memory after mirvar 004036FE call <wagainva._mirvar> ; initialises a big variable
00403703 push 0 ; mirvar’s parameter, describes the initial value of big 00403705 mov ebx, eax ; copy the pointer of a new big into ebx
00403707 call <wagainva._mirvar> ; initialises a big variable
0040370C push 0 ; mirvar’s parameter, describes the initial value of big 0040370E mov dword ptr ss:[esp+38],eax ; save the pointer to the reserved memory after mirvar 00403712 call <wagainva._mirvar> ; initialises a big variable
Diasm n.1
W
W
a
a
g
g
a
a
I
I
n
n
v
v
a
a
d
d
e
e
r
r
b
b
y
y
W
W
a
a
g
g
a
a
n
n
o
o
n
n
o
o
O
OXX8877KK 2
PAGE 2 WAGA INVADER BY WAGANONO
As we can see in this piece of code there is a mirsys call and after many mirvar. The algorithm is already started, it initialises the MIRACL system and after that it allocates memory for six big numbers initialized to zero.
The C code is something like this:
>C CODE
#include "lib\miracl.h"
void Serial_Check(char *serial) {
miracl *mip; // Init MIRACL
mip = mirsys(300, 16); // 300 hexadecimal digits for each bignum mip->IOBASE = 16; // Hexadecimal base for all operations
big s = mirvar(0); // Allocate memory for a bignum initialized to zero big n = mirvar(0); // Allocate memory for a bignum initialized to zero big k = mirvar(0); // Allocate memory for a bignum initialized to zero big h = mirvar(0); // Allocate memory for a bignum initialized to zero big e = mirvar(0); // Allocate memory for a bignum initialized to zero big x = mirvar(0); // Allocate memory for a bignum initialized to zero
Code n.1
Until now it’s not too hard to understand, yep? Continue with the analysis of the algorithm.
>DISASSEMBLY
0040371A push wagainva.0042F284 ; /string = "GAGANONO" 0040371F mov ebp,eax ; |
00403721 call dword ptr ds:[<&kernel32.lstrlena>] ; \lstrlena -> GAGANONO, how long is it? lol! 00403727 mov edi,eax ; len(GAGANONO) = 8 chars and save it in edi 00403729 lea eax,dword ptr ss:[esp+28] ; eax points to an instance of sha256 struct 0040372D push eax ; push this structure as parameter
0040372E call <wagainva._shs256_init> ; initialises an instance of SHA-256 00403733 add esp,4
00403736 xor esi,esi ; esi = 0 (reset the counter) 00403738 test edi,edi ; len(GAGANONO) != 0
0040373A je short <wagainva.loc_40375C> ; yes so don’t jump away and continue 0040373C lea esp,dword ptr ss:[esp]
00403740 movsx eax,byte ptr ds:[esi+42F284] ; al = string_GAGANONO[esi] 00403747 push eax ; push the char
00403748 lea ecx,dword ptr ss:[esp+2C] ; ecx points to own instance of sha256 0040374C push ecx ; push this struct
0040374D call <wagainva._shs256_process> ; processes a single byte 00403752 add esi,1 ; esi += 1 (counter++) 00403755 add esp,8
00403758 cmp esi,edi ; counter == len(GAGANONO) ? if yes
0040375A jnz short <wagainva.loc_403740> ; go out else jump to process another char 0040375C lea edx,dword ptr ss:[esp+28] ; edx points to own struct processed before 00403760 push wagainva.00430514 ; push a string at least 32 chars
00403765 push edx ; push own struct processed
00403766 call <wagainva._shs256_hash> ; generates a 32 byte hash into this string 0040376B add esp,8
0040376E xor esi,esi ; esi = 0 (reset the counter) 00403770 mov edi,wagainva.004304D0 ; edi points to another empty string 00403775 jmp short <wagainva.loc_403780>
00403777 lea esp,dword ptr ss:[esp]
0040377E mov edi,edi ; EHI, LOOK AT THAT SHIT! <- =) 00403780 movzx eax,byte ptr ds:[esi+430514] ; al = sha256_string_result[esi] 00403787 push eax ; /<%02x>
00403788 push wagainva.00428DCC ; |format = "%02x" 0040378D push edi ; |s
0040378E call dword ptr ds:[<&user32.wsprintfa>] ; \wsprintfa
00403794 add esi,1 ; esi += 1 (counter++) 00403797 add esp,0C
PAGE 3 WAGA INVADER BY WAGANONO
Nothing hard just gets the string “GAGANONO” and generates its SHA-256 hash. After that it converts each byte for 28 times (and not 32) so the result is:
Input String GAGANONO
Length 8
Waga Invader’s SHA-256 9D762D2018E010104148CCB6ED4B2A23B826CA97C3DB374B564CF901
Original SHA-256 9D762D2018E010104148CCB6ED4B2A23B826CA97C3DB374B564CF9018D456B88
So the code would be something like:
>C CODE
…… previous code ……
#define waga_str "GAGANONO"
char hash[100]; // String for SHA-256 final output BYTE digest[32]; // Array of 32 bytes
sha256 psh; // Instance of sha256 structure
shs256_init(&psh) ; // Initialises this instance // Processes each single byte
for(i=0;i<strlen(waga_str);i++) {
shs256_process(&psh, waga_str[i]); }
// Generates the 32 byte hash shs256_hash(&psh, digest);
// Print out the SHA-256 (28 chars) into the string called “hash”
for(i=0;i<28;i++) {
wsprintf(hash+(i*2), "%02X", digest[i]); }
Code n.2
Good, another piece of code..
>DISASSEMBLY
004037A2 mov ecx,dword ptr ss:[esp+18] ; ecx points to a bignum 004037A6 push wagainva.00430538 ; ascii "123456789012345678901234567890" <- own serial 004037AB push ecx ; push the address of a bignum
004037AC call <wagainva._cinstr> ; inputs a bignum from a character string 004037B1 mov edi,dword ptr ss:[esp+28] ; edi points to another bignum
004037B5 push wagainva.0042F208 ; ascii "26A1768C0DAB078115BCA03C5ECFC39EBE7FEC84478D7B3CFC0D6E3A6A3555AC08DB" 004037BA push edi ; push the address of a bignum
004037BB call <wagainva._cinstr> ; inputs a bignum from a character string 004037C0 mov edx,dword ptr ss:[esp+2C] ; edx points to another bignum 004037C4 push wagainva.0042F250 ; ascii "29A"
004037C9 push edx ; push the address of a bignum
004037CA call <wagainva._cinstr> ; inputs a bignum from a character string 004037CF push wagainva.004304D0 ; ascii "9D762D2018E010104148CCB6ED4B2A23B826CA97C3DB374B564CF901" 004037D4 push ebx ; push the address of a bignum
004037D5 call <wagainva._cinstr> ; inputs a bignum from a character string 004037DA push wagainva.0042F254 ; ascii "1000000000000000000000000000000000000000000000"
004037DF push ebp ; push the address of a bignum
004037E0 call <wagainva._cinstr> ; inputs a bignum from a character string
Diasm n.3
PAGE 4 WAGA INVADER BY WAGANONO
>C CODE
…… previous code ……
cinstr(s, serial); // Converts into a bignum own serial cinstr(k, "29A");
cinstr(n, "26A1768C0DAB078115BCA03C5ECFC39EBE7FEC84478D7B3CFC0D6E3A6A3555AC08DB"); cinstr(h, hash); // Converts into a bignum the hash of GAGANONO cinstr(e, "1000000000000000000000000000000000000000000000");
Code n.3
It’s time to understand something!
>DISASSEMBLY
004037E5 mov esi,dword ptr ss:[esp+4C] ; esi points to an empty bignum (x) 004037E9 mov eax,dword ptr ss:[esp+44] ; eax points to bignum k
004037ED mov ecx,dword ptr ss:[esp+40] ; ecx points to bignum s (own serial) 004037F1 push esi ; push bignum x
004037F2 push edi ; push bignum n (edi points to n) 004037F3 push eax ; push bignum k
004037F4 push ecx ; push bignum s (own serial) 004037F5 call <wagainva._powmod> ; x = (s**k) % n
004037FA push ebp ; push bignum e (ebp points to e) 004037FB push ebx ; push bignum h (the SHA-256 hash) 004037FC push ebp ; push bignum e (another time) 004037FD call <wagainva._add> ; e = e + h
00403802 add esp,44
00403805 push esi ; push the bignum x (result of powmod) 00403806 push ebx ; push the bignum h (the SHA-256 hash) 00403807 call <wagainva._compare> ; compare h and x
0040380C add esp,8
0040380F cmp eax,1 ; if the result of compare isn’t 1 then jump 00403812 jnz short <wagainva.loc_403819> ; to the next compare else it doesn’t jump and 00403814 mov byte ptr ss:[esp+17],0 ; set this byte to zero
00403819 push ebp ; push the bignum e
0040381A push esi ; push the bignum x (result of powmod) 0040381B call <wagainva._compare> ; compare x and e
00403820 add esp,8
00403823 cmp eax,1 ; if the result of compare isn’t 1 then jump 00403826 jnz short <wagainva.loc_40382D> ; out else it doesn’t jump
00403828 mov byte ptr ss:[esp+17],0 ; and set the same byte as before to zero
Diasm n.4
Well, the core of the algorithm is all here. We have a powmod, an add and two compare. With the powmod it raise a big number to a big power modulus another big number. In this case we have:
x = sk mod n
|
= (serial**0x29A) % 26A1768C0DAB078115BCA03C5ECFC39EBE7FEC84478D7B3CFC0D6E3A6A3555AC08DB
Next step, the add. It adds a bignum (represented in this case from ‘e’) to another bignum (‘h’, the hash). As serial I tried “123456789012345678901234567890” and now my situation is this:
x (result of powmod) 1615A79B496E78184D33547E749ADCD640115BC2C5CC59276ACAC1F4C129A02B56D0 e 1000000000000000000000000000000000000000000000
h 9D762D2018E010104148CCB6ED4B2A23B826CA97C3DB374B564CF901 e after the add 9D762D2018F010104148CCB6ED4B2A23B826CA97C3DB374B564CF901
Result’s table n.1
PAGE 5 WAGA INVADER BY WAGANONO
The compare(x, y) function returns 1 if x>y, returns 0 if x==y and returns -1if x<y. In the two cases the returned value must be NOT equal to 1 because if one of two compare returns back 1 then own tested byte will be zero while at the beginning own byte isn’t zero and we need this variable not equal to zero.
At the first compare we have compare(h, x) so the parameter ‘x’ will be own bignum ‘h’ while the parameter ‘y’ will be own bignum ‘x’. As I told you before, we need a returned value not equal to 1 and it can be possible only if parameter x is <= than parameter y. In this case it’s true because bignum x is greater than bignum h (look at table called Result’s Table n.1).
At the second compare we have compare(x, e) so the parameter ‘x’ will be own bignum ‘x’ while the parameter ‘y’ will be own bignum ‘e’. As I told you before, we need a returned value not equal to 1 and it can be possible only if parameter x is <= than parameter y. In this case it’s false because this time bignum x (after the add) is greater than bignum e (look at table called Result’s Table n.1) while we need it less big than e (always after the add).
Finally we need a result of powmod like this:
9D762D2018E010104148CCB6ED4B2A23B826CA97C3DB374B564CF901 <= x <= 9D762D2018F010104148CCB6ED4B2A23B826CA97C3DB374B564CF901
The final code with the last functions is the following
>C CODE
…… previous code ……
powmod(s,k,n,x); // x = (s**k) % n
add(e,h,e); // e = e + h
if(compare(h,x)!=1 && compare(x,e)!=1) // if x>=h AND x<=e
isRight=TRUE; // WELL DONE! We've found a valid serial
mirkill(x); // Kills off a bignum by zeroising it and freeing its memory mirkill(n); // Kills off a bignum by zeroising it and freeing its memory mirkill(k); // Kills off a bignum by zeroising it and freeing its memory mirkill(s); // Kills off a bignum by zeroising it and freeing its memory mirkill(h); // Kills off a bignum by zeroising it and freeing its memory mirkill(e); // Kills off a bignum by zeroising it and freeing its memory
mirexit(); // Cleans up and frees all internal variables
Code n.4
Too easy until now.. In fact try to factorize N with RSA Tool or Msieve and look!!! Own N isn’t composed by two prime numbers or better yes but the formula isn’t the classic N = p*q but another.
N (270 bits) 26A1768C0DAB078115BCA03C5ECFC39EBE7FEC84478D7B3CFC0D6E3A6A3555AC08DB
p1 3755CA6CDD6B49826A669A3
p2 3755CA6CDD6B49826A669A3
q 33AD1EAA125E7B119B6F383
So the classic formula becomes N = p*p*q or better N = (p**2) * q.
Never heard before now of this kind of algorithm and then Waganono gave me an hint. All doubts can be defeat by this link: http://www.codeproject.com/useritems/ESIGN.asp
It’s ESIGN!
Easy to understand the following. The three steps to Key generation are outlined below. The Public Key is (n, k) and the Private Key is (p, q).
1) Select primes p and q of roughly the same bitlength with p >= q 2) Compute N = p2q
PAGE 6 WAGA INVADER BY WAGANONO
All is confirmed from the fact that all we saw is just the ESIGN’s Signature Algorithm.
1) Compute v = sk mod n (in this case we have x = sk mod n)
2) Compute z = h(m) (in this case we have h = SHA-256(“GAGANONO”))
If z <= v <= z + 2CEIL( (2/3) lg n ), the signature is valid. Otherwise, reject the signature.
So in order to code a valid keygen we need to follow this scheme:
1) d = H(m) (in this case for us d will be own h (the hash))
2) Select a random integer x such that 0 < x < p 3) Compute w = CEIL( (( d - xk) mod n ) / (p*q) )
4) Compute y = w * (k * xk-1)-1 mod p
5) Compute s = x + y*p*q mod n
In step three, w is simply the least integer that is larger than or equal to ( H(m) - xk mod n ) / p*q.
For other information look the source (it’s commented) of my keygen! Sorry for the bad code but it was the first time for me that i saw ESIGN.
For today is all, game over! Sorry for my bad English, hope you could understand me. Ciao! ;-)
2.
References
Ø Applied Crypto++: Using the ESIGN Digital Signature System by Jeffrey Walton
(http://www.codeproject.com/useritems/ESIGN.asp/)
Ø Self-Evaluation of ESIGN Signatures
(http://mack.ittc.ku.edu/cache/papers/cs/17310/http:zSzzSzwww.crypto.nkfu.edu.twzSzCRYPTOzSzSelf_ESIGN.pdf/self-evaluation.pdf/)
Ø M.I.R.A.C.L
(http://www.shamus.ie/)
Ø Handbook of Applied Cryptography by A. Menezes, P. van Oorschot, S. Vanstone
(http://www.cacr.math.uwaterloo.ca/hac/)
Ø ARTeam
(http://arteam.accessroot.com/)
Ø jB’s web space
(http://jardinezchezjb.free.fr/)
3.
Greetings
Thanks Waganono for this original keygenme! Thanks all TEAM ICU, ARTeam, REVENGE, SnD
Thanks Shub-Nigurrath, Zairon, black-eye, CuTedEvil ;)
Thanks Ank83, Guetta, HMX0101, deroko, haggar, ++Meat, x15or, all crackmes.de community Thanks UIC (università italiana cracking - www.quequero.org)
Thanks ThunderPwr, SatUrN, Quequero, AndreaGeddon, active85k, Zero_G, Pn[L]uck, Death-Reaver, theANGEL, n00b, and etc... :D