3

I am trying to modify a save file for a game. I think it is using an XOR cipher to encrypt it. Looking though the disassembly I think I found the function that decrypts it. I ran the assembly through a decompiler to get a better grip on what is going on.

I am a C# programmer with some knowledge of C/C++. I generally understand what is accomplished by this code, but there are some details I don't understand.

int __fastcall DecryptBuffer(unsigned __int8 *a1, int a2, int a3, unsigned int a4, int a5)
{
  unsigned __int8 *v5;
  unsigned __int8 *v6;
  int result;
  int v8;

  v5 = a1;
  v6 = &a1[a2];
  result = a5;
  v8 = a5 - (_DWORD)v5;
  while ( v5 != v6 )
  {
    result = *v5 ^ *(unsigned __int8 *)(a3 + (unsigned int)&v5[v8] % a4);
    *v5++ = result;
  }
  return result;
}

It accepts as parameters:

  • a1 - a byte array
  • a2 - the length of the array
  • a3 - 0xB19D425B
  • a4 - 0x107
  • a5 - 0x00

First, I can't figure out the value of v8. I don't know what (_DWORD)v5 means, or why it is subtracted from zero.

Second, I don't know what (unsigned int)&v5[v8] is actually doing. I take it to mean it is looking up a byte somewhere in the array, but is it retrieving a single byte and casting to an uint, or four bytes?

Here is the disassembly:

sub_301E44
PUSH.W          {R4-R8,LR}
MOV             R4, R0
ADDS            R6, R0, R1
LDR             R0, [SP,#0x18+arg_0]
MOV             R7, R2
MOV             R8, R3
SUBS            R5, R0, R4
loc_301E54
CMP             R4, R6
BEQ             locret_301E6C
ADDS            R0, R5, R4
MOV             R1, R8
BL.W            __aeabi_uidivmod
LDRB            R0, [R4]
LDRB            R3, [R7,R1]
EORS            R0, R3
STRB.W          R0, [R4],#1
B               loc_301E54
locret_301E6C
POP.W           {R4-R8,PC}

This is the function that calls the above:

PUSH            {R0-R2,LR}
MOVS            R3, #0
LDR             R2, =(dword_8749EF - 0x301E80)
STR             R3, [SP,#0x10+var_10]
MOVW            R3, #0x107
ADD             R2, PC  ; dword_8749EF
BL              sub_301E44
ADD             SP, SP, #0xC
3

(_DWORD)v5 is simply casting the __int8* pointer to _DWORD. Decompilations tend to be quite messy if you don't fix variable types, so let's ignore types for a moment.

To understand the value of v8, substitute it into the expression bellow: &v5[a5 - v5_old]. We can also understand this as v5 + a5 - v5_old. v5 is being incremented with each iteration of the loop, so the above expression is basically the current index plus a5.

The current index plus a5 modulo a4 (presumably the length of the buffer pointed to by a3) is then added to a3 and the corresponding byte is XOR'd to the byte currently pointed to by v5.

Here's my take on the algorithm:

void DecryptBuffer ( char *buffer, int buffer_len, char *key, int key_len, int key_start )
{ 
    int i;
    for ( i = 0; i < buffer_len; i++ )
        buffer [ i ] ^= key [ ( key_start + i ) % key_len ];
}

Or in other words, XOR with the key repeating over and over.

This is quite standard, so it's probably right, but it could be wrong—as I said, the decompilation is quite messy. If you want a sure answer, either clean it up by fixing variable types or post the disassembly as well.

  • Thanks so much, that works. I would never have realized that key was a pointer to an array (rather than just an integer) and that v8 was being used to determine how far into the buffer we were. I attached the disassembly for completeness. – Chet Oct 4 '18 at 0:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.