death's "electric-camel"

0 comments
[difficulty: 6][protection: tiger,sha1,blowfish,el-gamal]


It does some sha/tiger/blowfish which has to produce the right el-gamal private key to decrypt the goodboy message. See solution and keygen for details. The hardest thing about this crackme is the message of c++ code that goes along with crypto++ library. There's just so much code. I downloaded an old compiler and service pack in order to build the two versions of crypto++ that straddle the 2001 time that this crackme was made, and produced IDA signatures from these.

One of the coolest thing to be learned from this crackme is just how fast MAGMA can solve the DLP for this crackme:

P: cc7346a8b4ffb3f2393b
G: 00000000000000000003
Y: 3e2cb006ad3961beda9d


I left alpertron on overnight and it had not found anything... About 17 hours of computation was continuing when Dcoder from EFNET #cryptography helped me his script:

p := 965489229592273293031739;
K := GF(p);
g := K ! 3;
y := K ! 293611062693023723739805;
x := Log(g, y);
x;


It finds x=0792A1952223 in about .3 seconds!

upb's "Keygenme Sheg"

0 comments
[difficulty: 2][protection: digital logic]

Your serial number encodes instructions for a stack-based machine that has instructions for AND, XOR, and PUSH.

The VM is called 16 times and the output must match some values calculated from your username.

This is extremely similar to Malfunction's "Digital Arithmetic" (which came later). In DA, your serial encoded a circuit of NAND gates, and it was shown that NAND gates can be used to make any circuit. In sheg, you have a stack machine, so it is a little harder to produce a functioning "circuit" (the binary operations can ONLY use the two values at the top of the stack, and can ONLY put a result at the top of the stack). Since NOT(x)=XOR(x,1), we get can combine with AND to get NAND, and thus we get anything.

EDIT: upb has informed me that his machine is evaluating a Zhegalkin polynomial, which is neat view of a logic circuit as a polynomial over GF(2).

Amenesia's "Howl"

0 comments
[difficulty: 6][protection: custom finite field cipher activity]


Contains a block cipher utilizing finite field arithmetic. You can invert it by reversing the operations and inverting certain field operations. Your serial must encrypt to some values generated from the name and Allen Ginsberg's poem "Howl" (what is an angelheaded hipster anyways!?).

Check out the code for some real calculation of multiplicative inverses in the field using the Euclidean algorithm (no bruteforcing this time!!)

so61pi's "Keygenme#1"

0 comments
[difficulty: 2][protection: miniature RSA and subset sum]


A fun little keygenme! See if you can make serials containing only printable characters!

Numernia's "Keygenme Tvaa"

0 comments
[difficulty: 3][protection: crc32, GF(2^19) arithmetic]


I won't post too much about this yet (it is less than 2 days old on crackmes.de right now) and I am curious what other people submit. It is a very nicely, tightly coded crackme that I'm sure you will enjoy.

"SDDecoder Junior"

0 comments
SDDecoder keygenme by gbe32241 is a truly amazing work, worth anybody's examination.

In this knock-off, the strengthening factors are removed, namely:

- the input and output transforms are separated from the core function
- the key size is reduced from 128 bits to 64 bits
- some other minor simplifications

Here is a list of example codes.

0B9406A6A5CF8D68
7C2E3DC7A9357094
04FEB466BEDCDB92
21E55E6A6B309F70
A8FAC8E4DF56C998
D597329D34D0ED3A
33D0A0EA1092124B
28AC66DD28697C52
1E380939AF1BF545
4FC1636FB7EEAFB0
93B2C52E2B77BD8F

Can you keygen the rest?

UPDATE: (Oct05_2009) a mysterious visitor "z1hye" registered October 5th, 2009 and immediately posted the serial "F365E2149BEA87F1" with ID 0xDEAD!!

UPDATE: (Nov25_2009): Numernia has keygenned it!! check his site or my file archive for sources...going to increase the difficulty towards the real sddecoder now by applying just an input transformation and just an output transformation (but not both simultaneously) ...watch crackmes.de soon

Encrypto's "Aurora"

0 comments
[difficulty: 5][protection: crc32, custom block cipher, Nyberg Rueppel]


Numernia, Cyclops and I teamed up on this flashy crackme from Encrypto. You have to reverse both symmetric (amazing work Numernia!!) and public key routines. Solution will be posted soon!

"Capriccio"

3 comments
[difficulty: 4] [protection: crc64 control]


I don't want to give away too much yet. The routine is very small. So hopefully very little time reversing and more time keygenning.

EDIT: so it wasn't as hard as I thought! Villani killed it without even knowing the underlying scheme (which, to me, is even more impressive). Artif owned it up also with ease :) Both solutions posted, thanks guys.

ZeroCoder's "CrackMe v10.0"

0 comments
[difficulty: 6][protection: int1, hash]

I'm 14 again ruining any chance of being awake tomorrow at school due to a VX zine that I'm nowhere near comprehending. The 16-bit world lives on with ZeroCoder's 425 bytes of frustration that he calls his v10.0 crackme.

It sets up an int1 handler and uses pushf/popf to set the TF. A simple NOP invokes the handler which retrieves "instructions" from an area in the crackme and executes them. Yea it's like a VM, but there are only four instructions. Pretty damn cool so far.

Enter the pain. A simple hash calculation involving add/xor is used with inputs and outputs all over the place (from the crackme, from a supplied password, from the calculations of previous hashes, to the goodboy message, to temporary areas). You don't know what the goodboy is, what characters are in the goodboy or the password, or even what the length of the password is (except that it's capped at 10).

Anyways, it is solvable, although with a little brute-force and guesswork. Like many before it, I could never admit how much time was spent on this thing :P

SIDE NOTES: It was pretty cool using debug.exe and recognizing some of its influence in WinDBG. For a cool description of why interleaving add/xor is so powerful, check out the paper "Block Ciphers and Cryptanalysis" by Fauzan Mirza.

WinDBG Extension to Read BigLib Numbers

0 comments
In filedump, file "WinDBG_Ext_Roy_BigLib.zip" is a WinDBG extension for reading arbitrary precision number structures from the BigLib library by Roy | Fleur. It comes with source. To use it, copy it to the "winext" subdirectory in your WinDBG installation directory.An example:

.text:004016B0 push dword_4047A1 <-- arg3
.text:004016B6 push dword_404789 <-- arg2
.text:004016BC push dword_404799 <-- arg1
.text:004016C2 push dword_40478D <-- arg0
.text:004016C8 mov eax, 1
.text:004016CD call sub_4017F3

Even without knowing what sub_4017F3 is, the input/output can be monitored. Load up the extension so that we can view the numbers:

0:000> !load bn
extension: initializing...
0:000> !bn poi(esp)
bignum @00AC0000
00003652B36A37B0C7ECE042
0:000> !bn poi(esp+4)
bignum @00AF0000
00000002
0:000> !bn poi(esp+8)
bignum @009B0000
0000CBEC5F1F97FB14C803CB
0:000> !bn poi(esp+c)
bignum @00B10000
0

Proceed over the call, and check the numbers again:

0:000> p
0:000> !bn 00AC0000
bignum @00AC0000
00003652B36A37B0C7ECE042
0:000> !bn 00AF0000
bignum @00AF0000
00000002
0:000> !bn 009B0000
bignum @009B0000
0000CBEC5F1F97FB14C803CB
0:000> !bn 00B10000
bignum @00B10000
00006F18441B1396928838B5

All the arguments except arg3 remained the same. With some intuition and a verifying test in BigCalto, we see that arg3 = arg0 ^ arg1 (mod arg2) and identify this function as _BigPowMod().

I purposely did some things to encourage rapid re-purposement of the source code: all the code is in one file, there is minimal bloat (just some placeholders with prints where extended functionality can exist), and it compiles with a simple batch file which invokes the Visual Studio command line tools. No project or solution bullshit.

dihux's Keygenme nr. 2

0 comments
[difficulty: 4][protection: math and tricks]


I've had the privilege to beta-test dihux's second keygenme... it's tough but not too hard. Grab it quick when it hits crackmes.de!

upb's Push-The-Pusher

2 comments
[difficulty: 3][protection: CRC32 manipulation]

Your name is used to generate an input buffer. Your serial decides which bits of the input buffer are complemented. Finally, the CRC32 of the input buffer must equal a predefined value (0xFAF3CCCE).

Though CRC is so old and there are numerous resources online about its many variations, this turned out to be much much harder than I expected. Here is the final technique:

  1. find what 32-bit input has CRC32 of 0xFAF3CCCE (brute all 32-bit values or try to work backwards with the long division)
  2. calculate CRC32 of the input buffer less 32-bits
  3. since CRC32(input,4,X) == CRC32(input^X,4,0) we can concatenate the four bytes discovered from step 1 to the buffer after having xor'd them by the result from step 2
  4. the CRC32 of the full input buffer now is 0xFAF3CCCE

If you're facing a similar task, the solution text includes a hand-worked example of CRC32. Oh! And a challenge to you: given a CRC lookup table, how can you quickly find which polynomial was used to generate it?

Waganono's WagaTemplate

0 comments
[difficulty: 2][protection: mmx]


Hehe yet another strange crackme by this guy. This one comes with the about-box template (which has cool fractal animation worth checking out).

The serial verification makes use of just 5 mmx/sse/simd/whatever instructions (I always ignore these strange things!) After some time on google, it is very easy to solve.

What is NOT so easy is getting the floating point crap right. The easiest path is to use your debugger to write the values into the xmmX register and then read the register as raw bytes, but not so fast. WinDBG is shown to have MANY bugs when it comes to this.

First of all, the register command and register window will show two different values:

3.60134e-043 7.9881e-041 6.16406e-039 1.71607e-038 <-- from "r xmm1"

3.601337e-043: 7.988102e-041: 6.164064e-039: 1.716068e-038 <-- from register window

And evidently, it's not just a rounding issue. Second, if you double click in the register window on this xmm1 value, when you click away, the first float will be set to 0! This is the same for any xmmX register. If you do it to the same register twice in a row, all the floats will be set to 0! WTF?

Third, the input into the xmmX register "r xmm0= " truncates the precision also. You can list all registers and WinDBG will show them identical (register window or r command), but then the "r xmm0:ub" will show small difference, as will "cmpps" from crackme.

Ultimately I had to give up and use the compiler to generate code to do the work for me. See in \crackmes_solutions.

Maybe I have something wrong here. Storing real numbers inside discrete registers is just outright confusing. It's been on my to-do list for years to read "What Every Computer Scientist Should Know About Floating-Pointe Arithmetic". Maybe some other day! :)

HappyTown's CrackME_0009

0 comments
[difficulty: 4][protection: CRC/blowfish/distractions]


Ok, so I did this one because it looked kinda cool and there are a few remaining unsolved HappyTown's still out there. Decoys aside, your serial number must decrypt to a value calculated from the CRC's of your username and Windows product ID.

HappyTown's CrackME_0030

0 comments
[difficulty: 4][protection: 2nd order polynomial over Zn]

This crackme made me read the "quadratic residue" Wikipedia page AGAIN. One day I might have it memorized :)

You end up having to solve:

C*X^2 + Y^2 = U (mod N)

Where C is constant, U is a number derived from your username, and N is the product of two large primes P and Q. The X and Y are the components of the serial.

First you can randomly produce values for either X^2 or Y^2 and solve for the other. Whatever values you use, you must make sure that they're truly squares (mod N) [thus squares both (mod P) and (mod Q)] using Euler's criterion. Luckily, both P and Q are congruent 3 (mod 4) so we get the easy path in deriving X and Y.

But not so fast! Like "D-Racinez Moi" from decades past, solving for either X or Y must be done in pieces, as the modulus is composite, (one piece for P and one piece for Q). You'll have two answers:

X = <something> (mod P)
X = <something> (mod Q)

As primes, GCD(P,Q) = 1, and that means the Chinese remainder theorem will be making a guest appearance as the final step in generating keys.

Also, author notes that "Public Key algorithm INSIDE." - does anybody recognize this algorithm?

HappyTown's CrackME_0026

0 comments
[difficulty: 3][protection: ECDSA]


The moderator comments revealed that it did some type of ECC, so I had to try it in continuation of my elliptic journey. Fortunately, it implements ECDSA just like WiteG #10. But in this crackme, you have to actually forge a signature for a message generated from your user name (actually it is bignum(sha1(user)) just like the previous crackme!).

Of course ECDSA would be useless if forgery were possible without knowing the private key. I enlisted Mr. Haandi's ECDLP Solver v0.2a to find the discrete log (just the scalar k so that k * G = public key). The crackme uses extremely small parameters for its curve, so the solver finished in about 1/5th of a second.

From there it's just calculation...

WiteG #10: What do you think you know about signatures?

0 comments
[difficulty: 4][protection: ECDSA]

The idea of this crackme is a challenge: to generate a single ECDSA signature for two different messages (computed from the username). The cracker gets free choice of the public key QA and the randomized parameter k.


Two messages, one signature? It's impossible right?

Well after many lonely mornings in front of the glow, it really is possible!

The spoiler is that in the ECDSA signature verification computations, only the x-coordinate of the computed point is checked. For each x-coordinate on a curve over Fp, there are two y-coordinates (two distinct points total, inverses of one another for point arithmetic on the curve). Here is a good example from Certicom's ECC Java applet:


Given some point k*G, where k is an integer and point G is the generator, you don't have to do anything difficult like the ECDLP to find -(k*G). One of the six generators is G=(17,10). Calculating 1*G, 2*G, ... this generator produces:

(17,10), (16,15), (15,03), (09,18), (21,17), (01,18), (11,10), (18,13),
(20,04), (13,05), (19,22), (00,00), (19,01), (13,18), (20,19), (18,10),
(11,13), (01,05), (21,06), (09,05), (15,20), (16,08), (17,13), (inf)

The inverse points are mirrored around the (0,0) point in the middle of the sequence. We can put this symmetry into a simple formula:

k * G = -((N-k) * G)

Where N is the order of the generator. Like in the above picture, P and Q are inverses. But suppose we didn't know this beforehand, and only knew that P=14*G. We could discover Q by calculating (24-14)*G = 10*G = Q.

Ok so we can find the two inverse points easily in terms of a coefficient and a generator point. Now just try to get the two separate messages to have these two separate points as signatures. See my solution for more detail.

It is mentioned in WiteG's comment that it has been keygenned by jB before, a known keygenning master. If I find the time I will his solution and maybe add some more comments.

GiveMeMoney by ksc91u

4 comments
[difficulty: 6][protection: ECC]

According to Google, ksc91u is a Taiwanese student, and his main study lately is keeping me from getting any sleep trying to keygen his little creation:


His crackme uses Crypto++ library, which is a royal pain to disassemble. I used default settings in the Crypto++ Visual Studio project file to compile the lib and attempt to make an IDA sig, but it only recognized 16 functions in the crackme, and none of them were important names in the serial verification routine. An OpenRCE post here seems to mirror my frustration. Look at this example code for simply calculating a hash here:

#include "sha.h"
#include "base64.h"

std::string digest;

CryptoPP::SHA256 hash; // don't use MD5 anymore. It is considered insecure

// Thank you, Wei Dai, for making this possible:
CryptoPP::StringSource foo("CryptoPP is cool", true,
new CryptoPP::HashFilter(hash,
new CryptoPP::Base64Encoder (
new CryptoPP::StringSink(digest))));

std::cout << digest << std::endl;

"Thank you, Wei Dai, for making this possible"? This is crazy! I want my init(), update(), final() calls, not this object-within-an-object source-pipe-sink paradigm garbage!

There is some help from openrce user igorsk who published his "Microsoft VC++ Reversing Helpers" IDA scripts. Other than that, it's the classic poking around, trying different inputs and watching the output (and verifying against your calculator), and tracing deep :) If you can get through this, the hard part is over.

Your serial encodes a point on an elliptic curve over Fp which is multiplied (point multiplication on the curve) by a constant within the crackme. The resulting point's X coordinate acts as a modulus for which the second part of your serial has its inverse found. From this inverse is derived a decryption key for a DLL which exports the goodboy function.

Fortunately, some valid keys come with this crackme, and the magical inverse value is revealed to be constant simply by tracing through the crackme with a valid key. It is prime, so it will have an inverse mod any number (specifically any X coordinate from any random point on the curve).

Though not having anything to do with the crackme, it is interesting to see the curve used by the crackme over the real numbers and imagining the the group addition law:

gp > E=ellinit([0,0,0,589755319233708863313827,435926913486323829977567])
gp > ploth(X=-.7393,-.7390,real(ellordinate(E,X)))


If you look near Y=0, it is hard to image a tangent line at a point intersecting the curve in another spot. But then zoom out:

ploth(X=-1000000000000000000,1000000000000000000,real(ellordinate(E,X)))


The way that the curve is concave is somewhat convincing that the tangent line would eventually hit it, although it would be lightyears above or below the X axis. I would really like to learn how to graph the points of the curve over Fp.

Locating the code that executes upon a button press

2 comments
It can be hard to find the code that executes when a button is pushed. For a dialog, you can break on creation of the dialog and find the callback parameter. The callback logic can be followed on WM_COMMAND (0x111). But development environments that do a lot of the GUI work for you can be a different situation. Often a message will pass through several handlers before arriving at the user mode code.

SetWindowsHookEx() lets a callback be notified of different events. When supplied WH_CALLWNDPROC it can be notified of messages before they go to the windows procedure. Moreover, the lParam you are sent is a CWPSTRUCT* which has a handle to the destination window. This can be given to GetWindowLongPtr() with GWL_WNDPROC to find what code gets called.

I know spy++ type tools do this type of thing already, but it was a learning experience to make a tool. It does exactly what is described above. Just give it a PID and it will enumerate all threads and install the monitoring hook. It actually injects a DLL which opens a pipe back to the control program to send it the caught messages:


Instead of complicating it with filters and stuff, just grep the output to capture what message you are looking for. For instance: msgspy.exe 924 | grep 111 to get your WM_COMMAND. Download this tool msgspy_v1.rar in filedump.

Many times the PROC given for the command will be some default function in user32 or comctl32 or something (eg: COMCTL32!Button_WndProc). In this case you can trace through until the code in your target is hit or use your debuggers facility to do this, if it exists (go to user code in Olly).

One difficulty in making this tool was that it would often return values like 0xFFFFF052 for the WndProc. On the MSDN page for CallWindowProc the strangeness is explained, "If this value is obtained by calling the GetWindowLong function with the nIndex parameter set to GWL_WNDPROC or DWL_DLGPROC, it is actually either the address of a window or dialog box procedure, or a special internal value meaningful only to CallWindowProc."

That sucks - this internal magic token is useless to us trying to find the code. I traced into user32!CallWindowProc to see what it did with these funny values. My CallWindowProc is actually typedef'd to CallWindowProcA. This calls CallWindowProcAorW which performs this test:

.text:7E429FD3 mov eax, 0FFFF0000h
.text:7E429FD8 mov ecx, esi <-- WndProc
.text:7E429FDA and ecx, eax
.text:7E429FDC cmp ecx, eax
.text:7E429FDE push edi
.text:7E429FDF mov edi, [ebp+arg_8]
.text:7E429FE2 jz special_value

So what makes the specialty of these WndProc's is that their top 16 bits are set. Immediately into special_value, we get:

.text:7E42A99C mov dl, 7
.text:7E42A99E mov ecx, esi <-- WndProc
.text:7E42A9A0 call HMValidateHandleNoRip

This HMValidateHandleNoRip function is way beyond me: it does some system call crap to get a struct in ebx and blah blah who cares. But what does with the return value is treats it like some type of structure and extracts the member 24 bytes inwards:

.text:7E42A9C6 mov esi, [eax+18h]

And this is the real function pointer. The only way I could think of to get this feature in my code was to scan user32 for the function HMValidateHandleNoRip, having made a signature that consists of just the opcode (the operands are like wildcards). This blows because if user32 ever looks different (different service pack, different windows version?), the tool will need to fall back and only display the special tokens, unable to resolve them. Have a better idea? Please comment.

Anyways so if it is found (which it does on my XP SP3 machine), it can be used to resolve the special tokens just fine.

typedef struct HMVALIDATESTRUCT
{
// filler to get to offset 0x18
UINT a, b, c, d, e, f;
UINT fptr;
} HMVALIDATESTRUCT, *PHMVALIDATESTRUCT;

typedef PHMVALIDATESTRUCT (__fastcall *PFN_HMVALIDATEHANDLENORIP)(WNDPROC, BYTE);

PFN_HMVALIDATEHANDLENORIP ValidateHandle = ScanForFunction(hUser32);

WndProc proc = GetWindowLongPtr(pcwp->hwnd, GWL_WNDPROC);

PHMVALIDATESTRUCT foo = ValidateHandle((WNDPROC) proc, 7);

spymsg.wndProc = foo->fptr;

Ugly but it works!

You may find this tool useful for delphi apps or other programs made with workshop type development environments. A recent example I used it on is obnoxious's AutoIt crackme.

May 11th, 2009 EDIT: zip now includes source!

Generate a vector basis over GF(2)

0 comments
To me it is interesting that you can have a set of N vectors, a basis, and compose any target vector of N dimensions using some subset of the basis.

It's like the basis holds within it the essential ingredients for any other vector.

Think about N=64, a quad word, and think of its bits as coefficients. Only 64 values are required to construct, via addition (xor), any of the 2^64 == 18,446,744,073,709,551,616 possible values.

It is surprisingly easy to generate a basis. You can simply generate the vectors at random, and apply Gauss-Jordan reduction to determine linear independence. When linear DEPENDENCE is discovered (by a vector going to zero during the reduction process), the original vector can be toggled and the process repeated.

See it implemented in generate_basis_gf2.cpp in filedump.

Usually [0..2] adjustments/repeats are needed. Once in a long while (about 20 runs) you'll have a pivot position in which no equation has its coefficient set. Simply re-run the prog :)

Here is one generated basis:

0x36ECDF01878AD175, 0xB6BA1594C6395658, 0x7A65F212F6442565, 0x804459B685D7497B,
0xBD20B9A87076F189, 0xF3314C16CEABA85E, 0x91204C6C87EB44A9, 0x54CD26E01D809779,
0x0046219A01D3F545, 0xC7A88B5B44F9772D, 0x39CE8CFD783A4455, 0x617D27249FC26846,
0xCA200643740AD6AD, 0x9CB8931DD2DEE83E, 0x66A8B2460D96649D, 0x3166F92B1750F745,
0xE1FFFC799B243671, 0x28017690FA8E1419, 0xC4E4123E68EB4571, 0x3CFFBCEF1D052B76,
0x039B58CCEE8D369D, 0x6C9A2DC670D0CB0A, 0x0F02298D9F4045AD, 0x484A65A08327D421,
0x9CF562B59F70161D, 0x594B2800F2531439, 0xA461CFB5E6ED250D, 0x5B5142A403710B22,
0x7617C60F68DC37E1, 0x0172F309FA01EA36, 0x5A2FD746933507F3, 0xAFC81B6708239591,
0xFD519DBE0517772C, 0x14E2B6CF64257505, 0xD9DB716A5498063F, 0xAB78DD2B82AE6A86,
0x883C3BD4522257D4, 0x70696ECAEE77AA46, 0xB1FD89D129FD26D7, 0xF67184BC1AFCB5A5,
0x221A0365A78A7618, 0xE3704904EE205571, 0x3E93AFA05A47460B, 0xC6AA01F03CDE6BB6,
0xE9DCA51BD67776F4, 0x9FC5104164A28B62, 0x84D4141AAFEAC7E7, 0x933B5A16B68994C9,
0x43B05DA2B9FC560C, 0xCB5CD74FF8F89061, 0x473030EBCA33E76F, 0x3782BF133ED98FCA,
0x94C67F904D113D80, 0xCE468A9EF2DD6F56, 0xEC5EEAD1B49FC793, 0x6C92A4D92C5F53F9,
0x3E21E069382D3D6C, 0xBEF70AD8308FB345, 0x02184E6C41F4C6DF, 0x5809E09CA20CACFE,
0x756F805A4F841D3C, 0xA36E5554B9D96C26, 0x19F6741E3049A62F, 0x0DB0311A2A3673CD

Eggi's Arteam Crackme #1

5 comments
[difficulty: 2][protection: code protected with xor]


This was recently posted to crackmes.de. It is another crackme where your serial is used to decrypt some code that will eventually execute.

Specifically here, a word is made by summing your serial characters with strlen(serial). This word then is xor'd sequentially (no feedback) across 10 words.

Before execution reached the decrypted code, a one byte comparison is made for validity, revealing half of the xor key.

To get the other half, I used z0mbie's XDE engine on trial decryptions (incrementing trial keys) to see which key produced the most normal disassembly; normal meaning it consisted of typical instructions: push, pop, call, etc.

UPDATE: crackme was removed from crackmes.de since it is preferred that the real author submit - a copy is in this site's crackmes folder

Paltalk Password Storage Algorithm

0 comments

There are a ton of password recovery tools for this program, so how hard can the storage algorithm be?

Homemade schemes can be very interesting. Here, in the registry is stored one dword for each of the password characters.

The username and is interleaved with the volume serial number. Like "myuser" and volume serial DEADBEEF come to: "mDyEuAsDeBrEEF". That is then trippled: "mDyEuAsDeBrEEFmDyEuAsDeBrEEFmDyEuAsDeBrEEF".

The registry value is then used to subtract values from certain characters, resulting in the password. It is easier to convey in code: see paltalk_pw_recover.cpp in filedump.

Cyclop's Angler

1 comments

A fellow mod from crackmes.de has posted an interesting crackme that will exercise your number theory knowledge. It's short and not too hard, have fun!

Funny hint: the color of the crackme may be some clue!

EDIT: freesoul freaking solved it in record time! (I admittedly had a head start as it rested in the moderator queue). So the spoiler is that it's Goldbach's conjecture. The serial has four pairs of numbers that must be prime and sum to four CRC's generated from the username (which are incremented if odd).

Cyclops said that not all usernames have serials, but I think they do. The CRC's are 32-bits, and the conjecture has been tested way up above 2^32. Since the addition check happens in the confines of a 32-bit register, you can even generate serials for when a CRC is 0 or 2 (aim for 2^32 and 2^32+2).

Internal binary representation of FGInt

0 comments
From FGInt.pas:

// TFGInt = Record
// Sign : TSign;
// Number : Array Of LongWord;

In a crackme, the MD5 of "myname" == ABB45C192F0818FF22B7DDDA8566DAC3 is made into an FGInt via Base256StringToFGInt(). Looking at the memory where this FGInt is stored:

0014fc04 00000001 00862354 0014fc34 0001d4c7

Where 000000001 is the Sign member and 00862354 is the Number member.

00862354 00000009 44414333 706a6c6c 11111104
00862364 119211ba 13846463 48c60706 50cc4e4c
00862374 21211a1a 00000041 00860a3c 0001f604

Where 00000009 is the amount of dwords in the array, and the 44414333 706a6c6c 11111104... are the members of the array.

Strange, but the only quantities contributing to the FGInt are the 31 lower bits of the 9 dwords.

Here is some C where a 64-bit value is used as kind of a bit buffer to shift in the 31-bit chunks. When more than 8 are collected, a byte can be consumed. If you know a better way, please tell me.

typedef struct {
ULONG nLongWords;
ULONG longWords[1];
} DELPHI6ARRAYLONGWORD, *PDELPHI6ARRAYLONGWORD;

// assume DELPHI6ARRAYLONGWORD variable darray and byte buffer bytes[]

for(int i=0; i<darray.nLongWords; ++i)
OutputRaw("digit %d: %08X\n", i, (ULONG)digits[i]);

ULONGLONG ullBuff=0;
UINT bytei=0;

UINT nBitsInBuff=0;

for(int i=0; i<darray.nLongWords; ++i)
{
ullBuff |= (digits[i] << nBitsInBuff);

nBitsInBuff += 31;

while(nBitsInBuff >= 8)
{
bytes[bytei++] = ullBuff & 0xFF;
ullBuff >>= 8;
nBitsInBuff -= 8;
}
}

if(nBitsInBuff)
bytes[bytei++] = ullBuff & 0xFF;

for(int i=bytei-1; i>=0; i--)
OutputRaw("%02X", (ULONG)bytes[i]);

The output is good:

digit 0: 44414333
digit 1: 706A6C6C
digit 2: 11111104
digit 3: 119211BA
digit 4: 13846463
digit 5: 48C60706
digit 6: 50CC4E4C
digit 7: 21211A1A
digit 8: 00000041
0000004142423434433139304630383038464630324237404444410035363644414333

The bytes of the FGInt (within the 31-bit chunks) are stored little-endian. The 414242... start the "ABB..." from the hash.

Wondering about the bit operation groups

0 comments
Bored... see bitops_16_html_table.pl in filedump for source.

add table:
+ 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
0000 0000000100100011010001010110011110001001101010111100110111101111
0001 0001001000110100010101100111100010011010101111001101111011110000
0010 0010001101000101011001111000100110101011110011011110111100000001
0011 0011010001010110011110001001101010111100110111101111000000010010
0100 0100010101100111100010011010101111001101111011110000000100100011
0101 0101011001111000100110101011110011011110111100000001001000110100
0110 0110011110001001101010111100110111101111000000010010001101000101
0111 0111100010011010101111001101111011110000000100100011010001010110
1000 1000100110101011110011011110111100000001001000110100010101100111
1001 1001101010111100110111101111000000010010001101000101011001111000
1010 1010101111001101111011110000000100100011010001010110011110001001
1011 1011110011011110111100000001001000110100010101100111100010011010
1100 1100110111101111000000010010001101000101011001111000100110101011
1101 1101111011110000000100100011010001010110011110001001101010111100
1110 1110111100000001001000110100010101100111100010011010101111001101
1111 1111000000010010001101000101011001111000100110101011110011011110

Associativity by normal addition (mod 16). 0 is the identity. Every row has the identity in it, so there is an inverse for every element.

xor table:
+ 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
0000 0000000100100011010001010110011110001001101010111100110111101111
0001 0001000000110010010101000111011010011000101110101101110011111110
0010 0010001100000001011001110100010110101011100010011110111111001101
0011 0011001000010000011101100101010010111010100110001111111011011100
0100 0100010101100111000000010010001111001101111011111000100110101011
0101 0101010001110110000100000011001011011100111111101001100010111010
0110 0110011101000101001000110000000111101111110011011010101110001001
0111 0111011001010100001100100001000011111110110111001011101010011000
1000 1000100110101011110011011110111100000001001000110100010101100111
1001 1001100010111010110111001111111000010000001100100101010001110110
1010 1010101110001001111011111100110100100011000000010110011101000101
1011 1011101010011000111111101101110000110010000100000111011001010100
1100 1100110111101111100010011010101101000101011001110000000100100011
1101 1101110011111110100110001011101001010100011101100001000000110010
1110 1110111111001101101010111000100101100111010001010010001100000001
1111 1111111011011100101110101001100001110110010101000011001000010000

Associativity by nature of XOR, ie xor(a,xor(b,c)) == xor(xor(a,b),c). 0 is the identity again. And every element has another which returns to 0 after xor.

mult table:
* 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
0000 0000000000000000000000000000000000000000000000000000000000000000
0001 0000000100100011010001010110011110001001101010111100110111101111
0010 0000001001000110100010101100111000000010010001101000101011001110
0011 0000001101101001110011110010010110001011111000010100011110101101
0100 0000010010001100000001001000110000000100100011000000010010001100
0101 0000010110101111010010011110001110001101001001111100000101101011
0110 0000011011000010100011100100101000000110110000101000111001001010
0111 0000011111100101110000111010000110001111011011010100101100101001
1000 0000100000001000000010000000100000001000000010000000100000001000
1001 0000100100101011010011010110111110000001101000111100010111100111
1010 0000101001001110100000101100011000001010010011101000001011000110
1011 0000101101100001110001110010110110000011111010010100111110100101
1100 0000110010000100000011001000010000001100100001000000110010000100
1101 0000110110100111010000011110101110000101001011111100100101100011
1110 0000111011001010100001100100001000001110110010101000011001000010
1111 0000111111101101110010111010100110000111011001010100001100100001

Associativity by normal multiplication rules (mod 16). 1 serves as the identity for all elements except 0. Not a group.

If 0 removed, only 1,3,5,...,15 have inverses. It is because these numbers are coprime with 16. There is some symbol for this, Z*

and table:
& 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
0000 0000000000000000000000000000000000000000000000000000000000000000
0001 0000000100000001000000010000000100000001000000010000000100000001
0010 0000000000100010000000000010001000000000001000100000000000100010
0011 0000000100100011000000010010001100000001001000110000000100100011
0100 0000000000000000010001000100010000000000000000000100010001000100
0101 0000000100000001010001010100010100000001000000010100010101000101
0110 0000000000100010010001000110011000000000001000100100010001100110
0111 0000000100100011010001010110011100000001001000110100010101100111
1000 0000000000000000000000000000000010001000100010001000100010001000
1001 0000000100000001000000010000000110001001100010011000100110001001
1010 0000000000100010000000000010001010001000101010101000100010101010
1011 0000000100100011000000010010001110001001101010111000100110101011
1100 0000000000000000010001000100010010001000100010001100110011001100
1101 0000000100000001010001010100010110001001100010011100110111001101
1110 0000000000100010010001000110011010001000101010101100110011101110
1111 0000000100100011010001010110011110001001101010111100110111101111

I think AND is associative - three bits and'd in any order is true when they are all true, and false otherwise. 1111 looks like the identity. But nobody has an inverse except 1111. Not a group?

or table:
| 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
0000 0000000100100011010001010110011110001001101010111100110111101111
0001 0001000100110011010101010111011110011001101110111101110111111111
0010 0010001100100011011001110110011110101011101010111110111111101111
0011 0011001100110011011101110111011110111011101110111111111111111111
0100 0100010101100111010001010110011111001101111011111100110111101111
0101 0101010101110111010101010111011111011101111111111101110111111111
0110 0110011101100111011001110110011111101111111011111110111111101111
0111 0111011101110111011101110111011111111111111111111111111111111111
1000 1000100110101011110011011110111110001001101010111100110111101111
1001 1001100110111011110111011111111110011001101110111101110111111111
1010 1010101110101011111011111110111110101011101010111110111111101111
1011 1011101110111011111111111111111110111011101110111111111111111111
1100 1100110111101111110011011110111111001101111011111100110111101111
1101 1101110111111111110111011111111111011101111111111101110111111111
1110 1110111111101111111011111110111111101111111011111110111111101111
1111 1111111111111111111111111111111111111111111111111111111111111111

Associativity by the same line of reasoning as AND. 0 is the identity. But nobody except 0 has an inverse. Not a group?

Xspider's xCryptokGnMe #1

0 comments
[difficulty: 2][protection: modified blowfish, distractions]

This crackme does a few things to distract you, like getting the dialog text of fields that don't exist, computing a SHA1 hash and then not using it, and other calculations. Ultimately 16 calls to blowfish's F() function (that does not use the tables within the blowfish context struct) work on the first 64-bits of your entered username. A little custom math after that computers your 64-bit key. Crackme is fishable.

Trevil's Keygenme #1

0 comments
[difficulty: 2] [protection: 1/2 of RSA]

Check out Trevil's (a cool guy from #c4n) keygenme. It's old, and does some basic RSA stuff. Just encrypt(user) == serial. But the fact that its in VB made it a little difficult. I probably would have been lost without Googling for some of the strings. See if you can do it without searching!

Malfunction's "Digital Arithmetic"

0 comments
[difficulty: 5] [protection: SEH, NAND Circuit Simulation]

This crackme has encrypted functions and to decrypt them, you must trace through some exceptions. Luckily they follow a standard form and a static deprotector can be made (included in the solution.)

The functions eventually reads in your key file, which must contain a description of a NAND gate logic circuit whose simulation verifies some calculations made in the crackme from your username. The bits of some generated inputs are the hi/lo inputs of the nand gates, and the output hi/lo is the output bits.

The major weakness is that the crackme only generates ten input/output pairs to verify that your circuit works. For instance the username "malfunction" generates:

input output
0x103473A2E909556A 0xD5C5F13B5D19C47B
0xD58409F7ACE0B636 0xCC00155EACD0443C

0xB8E23CEC3499154F 0xDA1F03994BAD8545

0x7D0D5F4A4B7EE494 0x3700BB945FE19291

0x3C8249E1942D1311 0xB21A80661AF8410E

0x6450E69AC82EF542 0x149E7890D8C07AFC

0xFA754B5BE6241519 0x4D0FF60D1535ED08

0xD2AC47F7A3DDC7F2 0xD8002D85A3D9E8D5

0x67BF14070BAAE972 0x8F000EC07480C934

0x7D173C2B4336EE20 0xE1F3F4387250BC3F


Here again (like in BUBlic's Xor2Zero) the strength of understanding GF(2) and vectors of GF(2) is shown (even if it is basic as hell, like mine :)). The bit columns of the input and outputs can be thought of as vectors. This yields 64 vectors, each with 10 dimensions.

Since the smallest basis of ten dimensions is a set of 10 vectors, having 64 nearly guarantees that we have a basis here. So we must be able to find some vectors that, summed (xor) over GF(2) give us any output vector we want. In reality, it is difficult to find some output vector that needs more than 3 input vectors.

Thus we could solve Digital Arithmetic if only it was a simulation of XOR gates. But it's a simulation of NAND gates. What to do?

XOR(A,B) = NAND(NAND(A,(NAND(A,B)),NAND(B,NAND(A,B)))

BUBLic's Security^-1

0 comments
[difficulty: 4] [protection: MD5, NTRU crypto]

MD5 is used on the username to generate what's called a truncated polynomial within the NTRU cryptosystem. It represents the plaintext. The entered serial number encodes a polynomial that is the ciphertext, and the crackme decrypts it to compare it to the plaintext. Keygenning is just enciphering the the poly produced from the username, but a small shortcut can be taken.

BUBLic's Xor2Zero

0 comments
[difficulty: 3] [protection: GF(2) math]

This one is really neat! MD5 is used to generate a system of linear equations over GF(2). Your serial encodes a solution to the equations. Only XOR, and AND can be used. A reduced Gauss-Jordan elimination algorithm was employed to make a keygen.

Waganono's D-Vinaigrez Moi

0 comments
[difficulty: 2] [protection: HMAC, XOR]

Another one for my Waganono collection :) Your serial number "decrypts" (xor) a ciphered message in the crackme. The crackme verifies proper decryption by using a keyed hash message authentication code. You get a French message and a song as a reward! You can see patterns in the ciphertext that clue you to the length of the key. Next, individual bytes of the key can be toggled to maximize the amount of "normal" text in trial decryptions.

Bruteforceme#1_astigmata

1 comments
[difficulty: 2] [protection: the RCR instruction :)]

A machine has been dedicated to searching for the key to Bruteforceme#1_astigmata, a crackme which has stood unsolved for quite a while now. (LINK: Bruteforceme#1_astigmata). What's so neat about the crackme is that there are really only four instructions that make it so difficult. Those instructions are iterated 1E8 times. RCR has no 1:1 inverse, but it is invertible. The branching factor, moving backwards, is so large that any attempt to go back more than 100 iterations becomes infeasible. It is also not difficult to find (eax,ebx) pairs that lead to a solution by just allowing the loop to run unbounded, and stopping when the xor result is 0xD5446474 (keep a 1E8 history). So back to brute force. My biggest fear is that no answer exists. I'm confident that the search is correct because I compared some outputs with outputs of the crackme. Some inputs were also chosen, ran them through the crackme to get the xor result, and then the search was tested to see if it could really find the chosen input.



UPDATE!
After nearly 9 days of searching, on Christmas eve morning (2008), the serial was found :) See the solution in the crackme solutions download link or on crackmes.de.

"UPSKiRT"

0 comments
[difficulty: 3] [protection: Modular Arithmetic]

"UPSKiRT" CrackME

- serial verification uses elementary number theory

- uses just four or five different calls to custom big number lib

- values of big numbers in memory are EXTREMELY obvious

The serial verification is based on a single call, no decoys. The only anti-debug is distracting multimedia :) I apologise in advance if any bugs are found. Kindly report them immediately. Crackme and a working keygen were tested on 32-bit XP SP2.

Keygen only! Enjoy!


UPDATE! Numernia has solved my crackme in an impressive manner! See his solution in my solutions link or on crackmes.de. I've decided also to release the keygen I made during testing for another angle at the problem.

TMG official keygenme #3

0 comments
[difficulty: 4] [protection: El-Gamal Signature Scheme]

The username is converted into a message using MD5 and a powmod op. The serial number you supply is the signature which is verified using a public key hardcoded in the crackme. In other words, keygenning is forging the signature for the username.

JE's "jE!_CRC_DRx"

0 comments
[difficulty: 7] [protection: SEH, TF, HW BP's]

At only 11k, this crackme is a real pain. It's a puzzle from you must supply an input file that coerces the crackme into displaying a message box (the usual user32!MessageBoxA). There is some anti-debug. The calculation is based on the bytes of the executabe memory image, so it will change if BPX's are present. The code itself has int3 and a handler for int3, making you wonder how much the cme behavior will intersect with your debugger. The DRx registers are used as the area for calculations, an obstacle for BPM. It also depends on the trap flag being set in certain places, so single-stepping is not an easy option. Though I lost alot of time on it, it forced me to learn alot about my debugger (making it pass and handle certain exceptions, tracing SEH, etc.) For some reason, I find jE!'s comments very entertaining:
U must buid KEY-file, which forces cr0ckme to show msgbox:

WOW!
CONGRATULATIONZ!

main cr0ckme idea is playing with SEHs..
(look at comment in code below)

after i imagined some fun-way to call MessageBox..
fun is fun, but what for U!?
btw, maybe i will call it 'READY-STACK' & write another cr0ckme on this idea!

so i mostly removed my-fun! U must discover that fun-way first!
(info leaved should be enough.. U need fUntaziE+LogiQ, have U!?)
then you need discover main ck0ckme idea.. o-o-o!

c00lw0lf's KeygenMe1

0 comments
[difficulty: 1] [protection: RSA]

It has an easily factorable modulus. Crackme serial must encipher/decipher to a number derived from the "C:\" volume serial number.

Crosys's Keygenme 2008

0 comments
[difficulty: 4] [protection: DSA]

I was stuck on this crackme for some time, not able to recognise or figure out the math. A clue came from fuss on IRC who revealed that it was digital signature algorithm :) The serials consist of three parts: a message, and the two components of the signature, using the public key parameters stored in the crackme. I can imagine such a scheme being used in a real protection: the message could encode enabled/disabled features or a license expiration time, and the signature (only producible by the company having the private key) prevents forgeries.

DSA is interesting in its similarity to the El-Gamal scheme present in the last TMG crackme. In El-Gamal, there was only one prime, and the random K you chose had to be ensured relatively prime to P-1. DSA is more complicated. The public parameters consist of two primes, one which divides the totient of the other, and the K can be chosen freely within the number line of the smaller prime. Bruce Schneier's "Applied Cryptography" has an interesting discussion of the similarities between signature schemes based on the discrete logarithm problem at chapter 20.4 where he unifies them with generalized equations and attempts to count all possible variations.

"ScrewME #2" by Dynasty

0 comments
[difficulty: 4] [protection: obfuscation, custom serial routine]

This has some anti-debugging tricks obscured among junk/obfuscation. The parts that verify the serial, however, are plaintext and viewable in IDA, so you can breakpoint there without stepping through the nonsense. I included a single-stepping program to attempt a more general counter to the problem of "finding the algorithm among the hay stack". An experienced reverser on crackmes.de, HMX0101 rates the difficulty at just 1 or 2.

"JUNo" by _J_

0 comments
[difficulty: 3] [protection: modified MD5, unknown math, 3way block cipher]

JUNo contains floating point math utilizing the normal instructions and a math library called MAPM (Mike's Arbitrary Precision Math). However, the path between the serial number and the comparison requiring satisfaction is separated only by the 3way code. Ultimately the serial needs to be the ciphertext that, when decrypted with a key generated by the crackme, yields a block of plaintext derived from the name and company fields.

"Genaytyk's VM KeygenMe"

0 comments
[difficulty: 5] [protection: Virtual Machine, packed with MEW]

This is a well-structured VM with instructions analogous to x86 architecture, but with a larger set of registers. There are about 30 opcodes. The program executed by the VM is just under 400 instructions long and is some kind of monstrous crypto algorithm of the author's design (Genaytyk calls it a "high cipher" in his crackmes.de description).

UPDATE: artif has solved this crackme too! see his keygen written in pure ASM in the crackme solutions download link

"Ra$cal crackme N3 with VM" by Ra$cal

1 comments
[difficulty: 4] [protection: Virtual Machine]

The VM supports 25 instruction, some of which contain obfuscated opcodes. The VM program is nearly 1000 instructions long. I needed to write a program that drew VM execution using text to get an idea what was going on, and then resigned to making a full disassembler, which isn't perfect, but sufficient. The final algorithm held in the VM consists of xor's and rol's on the serial and output of GetComputerName().



March 25th, 2009 EDIT:
Posted crackme source rascal_n3_vm_src.rar in filedump!

May 11th, 2009 EDIT:
Source moved to ra$cal_vm_n3_src.rar in crackmes!

"miniVM Crackme v1" by craig@neo

0 comments
[difficulty: 2] [protection: Virtual Machine]

The machine is of decent size, but you only have to trace a small subset of the instructions to follow the logic. This was just the right difficulty, I feel, for a first VM crackme - and now I want to do more of them :). The author says that the source code for the crackme will be posted at http://labs.neohapsis.com after RECON.

"MiSHKA'S RETRiBUTiON" by Zart

0 comments
[difficulty: 4] [protection: custom serial routine]

Not much of the stuff in this crackme makes sense. To produce keys that work, some logic and some brute forcing was required.

"Russian Dolls"

0 comments
[difficulty: 3] [protection: Telescoping Code :)]

"Russian Dolls" CrackME

You will quickly find "good boy" and "bad boy" message.

The decision is based on one call. No tricks.

The call is to the "Russian Dolls" verification function. (Russian dolls are those dolls where there's like one inside another, and one inside that, and so on...)

You'll quickly see the similarity of how this verificationi function executes and the dolls that the crackme name refersi to.

Write a keygen and a tutorial!


UPDATE! red477 made short work of this crackme using his IDA script writing skills! See his solution on crackmes.de or in my solutions link!

"D-Racinez Moi" by Waganono

0 comments
[difficulty: 3] [protection: ripemd160, quadratic residue formula]

There's a collection of anti-debug tricks in a TLS routine. A big number (using OpenSSL) quadratic residue equation has to be solved - but it's of special form, allowing the solution to be found in pieces and combined using the Chinese remainder theorem.

"Demeter" by _J_

0 comments
[difficulty: 4] [protection: RSA, custom HAVAL]

A big number library called BigDigits is used in this crackme, and making sense of its code is the hardest task. Correct serials are the RSA ciphertexts whose decryption yields a plaintext derived from the username and volume serial number.

"192 Bits" by NeoN

0 comments
[difficulty: 3] [protection: RSA]

My solution is very poor because I did not know much about big number structures at the time.

"D-Montez Moi" by Waganono (in crackme solutions)

0 comments
[difficulty: 2] [protection: custom math]

Anti-debug tricks in TLS routine and a math problem (mod 2^64) have to be beaten.

"Keygenme I v1.0" by luxor

0 comments
[difficulty: 3] [protection: polynomial modulus equation]

Some lookup tables and transforms set up a cubic polynomial (mod 2^64) equation from the name and serial. Schoup's NTL makes short work of it.

GoDaddy's Terrible Customer Care Policy

0 comments
I get an email telling me it's time to renew my .us domain name registration. The price is $19.99 for one year. That's pretty expensive for even a .com.


So I call them up and ask why it's so expensive, considering registering the thing was something like $3 at the time. He offers me a "discount" to $7.99. You know that the email price is just to prey on the push-overs if they're willing to drop 60% just for questioning it.

But look at the front page of GoDaddy (in my browser at the time of the phone call), and you can click to this offering: (screenshot taken July 6th, 2009).


You can get a BRAND NEW .us domain name for only $4.99 a year. Yet my registration is already in their system and they want $7.99 a year. I tell him "You know what this tells me? That you value new people that come across your website more than you value your existing customers." He responds that this is the price. I ask "Well explain to me how it is that an existing customer who just want RENEWAL can be charged more than a non-customer who wants a NEW registration... surely a renewal can't cost more than a registration, can it? How does this make sense?". He responds again that this is a price, and refuses my offer to renew for three years if HE WOULD JUST GIVE ME THE SAME PRICE AS NEW CUSTOMERS.

So just out of principle now, (even though it will cost me more time and money), I must transfer this name elsewhere.

Good thinking, GoDaddy!