# Difference between revisions of "Example 2"

Assembly Language Tutorial

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, and 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

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 = { "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

This code works, and can be used. However, for this exercise, reducing it all the way helps.

Some variables are substituted where they aren't necessary:

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

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

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

Change the do loop to a for loop, rename ecx, and change the decrement of edi to the bottom of the loop:

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

int i;
int  eax,  ebx, edx;

for(i = 0xc2; i >= 7; i -= 17)
{
edx = i % 12;
eax = (char) *edi;
*edi = (char) *(edx + esi);
*(edx + esi) = (char) eax;
edi--;
}
}
```

Replace esi with key, change pointers to arrays, declare eax as a char (to remove typecasting):

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

int i;
char eax;
int  ebx, edx;

for(i = 0xc2; i >= 7; i -= 17)
{
edx = i % 12;
eax = *edi;
*edi = key[edx];
key[edx] = eax;
edi--;
}
}
```

Replacing the variable swap with a swap() function cleans things up significantly:

```void swap (char *a, char *b)
{
char temp = *a;
*a = *b;
*b = temp;
}

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

int i;
char eax;
int  ebx, edx;

for(i = 0xc2; i >= 7; i -= 17)
{
edx = i % 12;
swap(edi, key + edx);
edi--;
}
}
```

## Finished Code

Finally, rename some variables, eliminate unused variables, and combine where possible, leaving this slick little function:

```void shuffleCDKey(char *key)
{
char *position = key + 11;
int i;
for(i = 194; i >= 7; i -= 17)
swap(position--, key + (i % 12));
}
```

And that's it! Testing it with the driver function verifies that it still works.

## Questions

Feel free to edit this section and post questions, I'll do my best to answer them. But you may need to contact me to let me know that a question exists.