Example 2

From SkullSecurity
Revision as of 15:24, 13 March 2007 by 207.34.103.194 (Talk) (Cleaned up C Code)

Jump to: navigation, search
Assembly Language Tutorial
Please choose a tutorial page:

This example is the first step in Starcraft's CDKey Decode. This shuffles the characters in the key in a predictable way. I made this the second example because it's a little trickier, but it's not terribly difficult.

As in the previous example, try figuring this out on your own first!

   lea     edi, [esi+0Bh]
   mov     ecx, 0C2h
top:
   mov     eax, ecx
   mov     ebx, 0Ch
   cdq
   idiv    ebx
   mov     al, [edi]
   sub     ecx, 11h
   dec     edi
   cmp     ecx, 7
   mov     bl, [edx+esi]
   mov     [edi+1], bl
   mov     [edx+esi], al
   jge     top


Annotated Code

Here, comments have been added explaining each line a little bit. These comments are added in an attempt to understand what the code's doing.

   ; This is actually continued from the last example, so esi contains the verified CDKey. 
   lea     edi, [esi+0Bh]   ; edi is a pointer to the 12th character in the cdkey (0x0b = 11, and arrays start at 0).
   mov     ecx, 0C2h        ; Set ecx to 0xC2. Recall that ecx is often a loop counter. 
top:
   mov     eax, ecx         ; Move the loop counter to eax.
   mov     ebx, 0Ch         ; Set ebx to 0x0C (0x0C = 12, an arrays are indexed from 0, so the CDKey string goes from 0 to 12).
   cdq                      ; Get ready for a signed division.
   idiv    ebx              ; Divide the loop counter by 0x0C. It isn't clear yet whether this is division or modulus, but
                            ; because an accumulator is being divided by the CDKey length, it's logical to assume that 
                            ; this is modular division. edx will likely be used, and eax will likely be overwritten. 

   mov     al, [edi]        ; Move the value that edi points to (which is a character in the CDKey) to al. Recall that al is the
                            ; right-most byte in eax. This confirms two things: that edi points to a character in the CDKey
                            ; (since a character is 1 byte) and that the division above is modulus (because eax is overwritten). 
   sub     ecx, 11h         ; Subtract 0x11 from ecx. Recall that ecx is often a loop counter, and likely is in this case. 
   dec     edi              ; Decrement the pointer into the CDKey. edi started at the 12th character and is moving backwards. 
   cmp     ecx, 7           ; Compare ecx to 7, which confirms that ecx is the loop counter. The jump corresponding to this
                            ; comparison is a few lines below. 
   mov     bl, [edx+esi]    ; Recall that edx is the remainder from the above division, which is (accumulator % 12), and that 
                            ; esi points to the CDKey. So this takes the character corresponding to the accumulator and moves
                            ; it into bl, which is the right-most byte of ebx. 
   mov     [edi+1], bl      ; Overwrite the character that edi pointed to at the top of the loop. Recall that [edi] is moved 
                            ; into al, then decremented above, which is why this is +1 (to offset the decrement). 
   mov     [edx+esi], al    ; Move al into the string corresponding to the modular division. 
   jge     top              ; Jump as long as the ecx counter is greater than or equal to 7
                            ;
                            ; Note that the loop here is simply a swap. edi decrements, starting from the 12th character and
                            ; moving backwards. ecx is reduced by 0x11 each time, and the character at (ecx % 12) is swapped
                            ; with the character pointed at by edi. 

C Code

Please, try this yourself first!

This is an absolutely direct conversion from the annotated assembly to C. I added a main function that sends a bunch of test keys through the function to print out the results.

Now that a driver function can test the CDKey shuffler, the code can be reduced and condensed.

#include <stdio.h>

/* Prototype */
void shuffleCDKey(char *key);

int main(int argc, char *argv[])
{
    /* A series of test cases (I'm using fake keys here obviously, but real ones work even better) */
    char keys[3][14] = { "1212121212121", /* Valid */
                        "1234567890123", /* Valid */
                        "4962883551538" /* Valid */
                   };
    char *results[]  = { "1112222221111",
                        "7130422865193",
                        "3461239588558" };


    int i;

    for(i = 0; i < 3; i++)
    {
        printf("Original:  %s\n", keys[i]);
        shuffleCDKey(keys[i]);
        printf("Shuffled:  %s\n", keys[i]);
        printf("Should be: %s\n\n", results[i]);
    }

    return 0;
}

void shuffleCDKey(char *key)
{
    int  eax,  ebx, ecx, edx;
    char *esi;
    char *edi;

    esi = key;

        //   ; This is actually continued from the last example, so esi contains the verified CDKey.
        //   lea     edi, [esi+0Bh]   ; edi is a pointer to the 12th character in the cdkey (0x0b = 11, and arrays start at 0).
    edi = (esi + 0x0b);
        //   mov     ecx, 0C2h        ; Set ecx to 0xC2. Recall that ecx is often a loop counter.
    ecx = 0xc2;
        //top:
    do
    {
            //   mov     eax, ecx         ; Move the loop counter to eax.
        eax = ecx;
            //   mov     ebx, 0Ch         ; Set ebx to 0x0C (0x0C = 12, an arrays are indexed from 0, so the CDKey string goes from 0 to 12).
        ebx = 0x0c;
            //   cdq                      ; Get ready for a signed division.
            //   idiv    ebx              ; Divide the loop counter by 0x0C. It isn't clear yet whether this is division or modulus, but
            //                            ; because an accumulator is being divided by the CDKey length, it's logical to assume that
            //                            ; this is modular division. edx will likely be used, and eax will likely be overwritten.
        edx = eax % ebx;
            //
            //   mov     al, [edi]        ; Move the value that edi points to (which is a character in the CDKey) to al. Recall that al is the
            //                            ; right-most byte in eax. This confirms two things: that edi points to a character in the CDKey
            //                            ; (since a character is 1 byte) and that the division above is modulus (because eax is overwritten).
        eax = (char) *edi;

            //   sub     ecx, 11h         ; Subtract 0x11 from ecx. Recall that ecx is often a loop counter, and likely is in this case.
        ecx = ecx - 0x11;
            //   dec     edi              ; Decrement the pointer into the CDKey. edi started at the 12th character and is moving backwards.
        edi--;
            //   cmp     ecx, 7           ; Compare ecx to 7, which confirms that ecx is the loop counter. The jump corresponding to this
            //                            ; comparison is a few lines below.
        /* will handle this compare later */
            //   mov     bl, [edx+esi]    ; Recall that edx is the remainder from the above division, which is (accumulator % 12), and that
            //                            ; esi points to the CDKey. So this takes the character corresponding to the accumulator and moves
            //                            ; it into bl, which is the right-most byte of ebx.
        ebx = (char) *(edx + esi);
            //   mov     [edi+1], bl      ; Overwrite the character that edi pointed to at the top of the loop. Recall that [edi] is moved
            //                            ; into al, then decremented above, which is why this is +1 (to offset the decrement).
        *(edi + 1) = (char) ebx;
            //   mov     [edx+esi], al    ; Move al into the string corresponding to the modular division.
        *(edx + esi) = (char) eax;
            //   jge     top              ; Jump as long as the ecx counter is greater than or equal to 7
    }
    while(ecx >= 7);
            //                            ;
            //                            ; Note that the loop here is simply a swap. edi decrements, starting from the 12th character and
            //                            ; moving backwards. ecx is reduced by 0x11 each time, and the character at (ecx % 12) is swapped
            //                            ; with the character pointed at by edi.
            //
}

Cleaned up C Code

Here's the code after removing the assembly, fixing up the variable declarations, and changing hex values to decimal:

void shuffleCDKey(char *key)
{
    char *esi = key;
    char *edi = esi + 11;

    int ecx = 0xc2;
    int  eax,  ebx, edx;

    do
    {
        eax = ecx;
        ebx = 12;
        edx = eax % ebx;
        eax = (char) *edi;
        ecx = ecx - 17;
        edi--;
        ebx = (char) *(edx + esi);
        *(edi + 1) = (char) ebx;
        *(edx + esi) = (char) eax;
    }
    while(ecx >= 7);
}

Reduced C Code

Finished Code