Enigma’s EP_CryptDecryptBuffer internals

kao

For one of my private projects, I needed to decrypt some data. Original executable uses Enigma's EP_CryptDecryptBuffer function but I needed to implement the same thing using .NET. Much to my surprise, there was no information about what encryption algorithm is used by Enigma or how it works internally. So, I started by compiling Enigma's sample project CryptBuffer and debugging the executable.

TL;DR - IDEA cipher in CBC mode, password = MD5(user_password), IV for CBC comes from IDEA.Encrypt(8-zeroes, password).

Research

After protecting my executable with Enigma, few assembly lines

004142FA   68 B85E4100      PUSH crypto_p.00415EB8                   ; ASCII "password"
004142FF   68 00100000      PUSH 1000
00414304   53               PUSH EBX
00414305   E8 EEF9FFFF      CALL 

got changed into something horrible that looked like an Enigma's VM:

004142FA   68 B85E4100      PUSH crypto_p.00415EB8                   ; ASCII "password"
004142FF   68 00100000      PUSH 1000
00414304   53               PUSH EBX
00414305   E8 EEF9FFFF      CALL crypto_p.00413CF8
..
00413CF8   FF25 44D44100    JMP DWORD PTR DS:[41D444]                ; crypto_p.004EEC6C
..
004EEC6C  /E9 C6A60800      JMP crypto_p.00579337
..
00579337   68 6D5133A1      PUSH A133516D
0057933C   E9 BBC1FEFF      JMP crypto_p.005654FC
..
005654FC   60               PUSHAD
005654FD   9C               PUSHFD
005654FE   B2 01            MOV DL,1
00565500   BE 00000000      MOV ESI,0
00565505   8DBE CC775600    LEA EDI,DWORD PTR DS:[ESI+5677CC]
0056550B   8D3F             LEA EDI,DWORD PTR DS:[EDI]
0056550D   B9 01000000      MOV ECX,1
00565512   31C0             XOR EAX,EAX
00565514   F0:0FB10F        LOCK CMPXCHG DWORD PTR DS:[EDI],ECX      ; LOCK prefix
00565518   74 0F            JE SHORT crypto_p.00565529
0056551A   3A15 C8775600    CMP DL,BYTE PTR DS:[5677C8]
...

I didn't feel like spending my time on analyzing that. Much easier solution was to put hardware breakpoints on my data and password and wait for them to trigger. In a few seconds I landed into code that looked like MD5 (notice the constants!):

...
00454E1C   89D7             MOV EDI,EDX
00454E1E   31CF             XOR EDI,ECX
00454E20   21DF             AND EDI,EBX
00454E22   31D7             XOR EDI,EDX
00454E24   01F0             ADD EAX,ESI
00454E26   8D8407 78A46AD7  LEA EAX,DWORD PTR DS:[EDI+EAX+D76AA478]
00454E2D   C1C8 19          ROR EAX,19
00454E30   01D8             ADD EAX,EBX
00454E32   8B75 04          MOV ESI,DWORD PTR SS:[EBP+4]
00454E35   89CF             MOV EDI,ECX
00454E37   31DF             XOR EDI,EBX
00454E39   21C7             AND EDI,EAX
00454E3B   31CF             XOR EDI,ECX
00454E3D   01F2             ADD EDX,ESI
00454E3F   8D9417 56B7C7E8  LEA EDX,DWORD PTR DS:[EDI+EDX+E8C7B756]
...

and few hits later I landed into code that I didn't recognize at first:

...
004DB61C   E8 DBFDFFFF      CALL crypto_p.004DB3FC
004DB621   66:8B446E 7E     MOV AX,WORD PTR DS:[ESI+EBP*2+7E]
004DB626   66:0147 02       ADD WORD PTR DS:[EDI+2],AX
004DB62A   66:8B846E 800000>MOV AX,WORD PTR DS:[ESI+EBP*2+80]
004DB632   66:0147 04       ADD WORD PTR DS:[EDI+4],AX
004DB636   66:8B946E 820000>MOV DX,WORD PTR DS:[ESI+EBP*2+82]
004DB63E   8D47 06          LEA EAX,DWORD PTR DS:[EDI+6]
004DB641   E8 B6FDFFFF      CALL crypto_p.004DB3FC
004DB646   66:8B47 04       MOV AX,WORD PTR DS:[EDI+4]
004DB64A   66:894424 0C     MOV WORD PTR SS:[ESP+C],AX
004DB64F   66:8B07          MOV AX,WORD PTR DS:[EDI]
004DB652   66:3147 04       XOR WORD PTR DS:[EDI+4],AX
...

Delphi compiler had helpfully included RTTI information telling me that this second piece of code belongs to a class called TIdeaCipher. Now I had all the information I needed, I just had to put it all together. 🙂

Implementing decryption in C#

There aren't many IDEA implementations in C#. In fact, there are 2: by BouncyCastle and by LexBritvin. Since I'm not a big fan of huge and complex libraries like BouncyCastle, so I took the simplest possible code: from Github and modified it a bit.

First, we need change password generation algorithm to use MD5 to generate real password:

private static byte[] generateUserKeyFromCharKey(String charKey)
{
   MD5 md5 = MD5.Create();
   return md5.ComputeHash(Encoding.ASCII.GetBytes(charKey));
}

Second, we need to add support for CBC mode. It requires both calculating correct IV value and pre/post-processing each block we're encrypting or decrypting.

Calculating IV is quite simple:

// IV for CBC mode
byte[] IV;

public Idea(String charKey, bool encrypt)
{
    byte[] key = generateUserKeyFromCharKey(charKey);
    // Expands a 16-byte user key to the internal encryption sub-keys.
    int[] tempSubKey = expandUserKey(key);

    // calculate IV
    subKey = tempSubKey;
    IV = new byte[8];
    crypt(IV, 0);

    if (encrypt)
    {
        subKey = tempSubKey;
    }
    else
    {
        subKey = invertSubKey(tempSubKey);
    }
}

Processing each block is slightly harder but not a rocket science either. As explained in image borrowed from Wikipedia:
.

Before decrypting the data block, we need to store the encrypted values as IV for the next block. After decrypting the block, we need to XOR each byte of data with a corresponding byte of IV. Unfortunately, during encryption the data flow is different, so the simple crypt function has to be replaced with 2 functions: encrypt and decrypt.

/**
 * Decrypts array
 *
 * @param data
 *    Buffer containing data bytes to be decrypted
 */
public void decrypt(byte[] data)
{
    for (int q = 0; q < data.Length; q += 8)
    {
        // save original bytes for next IV
        byte[] origBytes = new byte[8];
        Array.Copy(data, q, origBytes, 0, 8);

        // decrypt data
        crypt(data, q);

        // xor data with previous IV
        for (int w = 0; w < 8; w++)
           data[q + w] ^= IV[w];

        // copy original data to next IV
        Array.Copy(origBytes, IV, 8);
    }
}

Few more cosmetic changes and that's it! We got clean and nice implementation of EP_CryptDecryptBuffer in pure C#. 🙂
Download link: https://bitbucket.org/kao/ep_cryptdecryptbuffer/

Have fun!

P.S. My implementation only supports cases where data length is divisible by 8, adding support for other lengths is left as an exercise for the reader.

3 thoughts on “Enigma’s EP_CryptDecryptBuffer internals

  1. Great job kao as always and, as always, thanks for sharing.
    I'm sorry if I focus on the least important detail but can you, please, detail a bit the following step?

    "Delphi compiler had helpfully included RTTI information telling me that this second piece of code belongs to a class called TIdeaCipher."

    My only guess (forgive me for being a newbie) is that some *code* (not just some object files or dlls) of the protector is compiled and linked with your executable.

    You could, of course, answer like 'Oh my! Grab a version of Enigma and try on your own!' ... and that would be a perfect valid answer 🙂

    Best Regards,
    Tony

    1. I intentionally skipped over that part, as it's not really relevant to the subject. 🙂 I'll do a short answer now and make note to do it in more details some other day..

      Enigma Protector is written in Delphi. It doesn't matter in what language your executable is written - when you protect your executable, protector code is added to your executable. This includes all the runtime information that Delphi compiler generated for Enigma Protector. So far, so good?

      In Delphi there is a special pattern for object creation which is quite easily recognizable. Delphi compiler also includes RTTI (run-time type information) in the compiled executable, which includes type names. The only challenge is to find it.

      So, I had landed on the address 004DB61C. Right-click code in IDA and choose "Xref graph to...". It will look like this (ignore the function names for a second):

      You can see that all calls are coming through sub_4bd9e0. Look at the disassembly (still ignoring function names):

      seg009:004DB9E0 sub_4DB9E0      proc near               ; CODE XREF: seg009:004ECA38p
      seg009:004DB9E0                                         ; sub_4F1580+90p ...
      seg009:004DB9E0
      seg009:004DB9E0 var_4_size      = dword ptr -4
      seg009:004DB9E0 arg_0           = dword ptr  8
      seg009:004DB9E0
      seg009:004DB9E0                 push    ebp
      seg009:004DB9E1                 mov     ebp, esp
      seg009:004DB9E3                 push    ecx
      seg009:004DB9E4                 push    ebx
      seg009:004DB9E5                 push    esi
      seg009:004DB9E6                 push    edi
      seg009:004DB9E7                 mov     [ebp+var_4_size], ecx
      seg009:004DB9EA                 mov     edi, edx
      seg009:004DB9EC                 mov     esi, eax
      seg009:004DB9EE                 mov     dl, 1
      seg009:004DB9F0                 mov     eax, ds:TIdeaCipherClass
      seg009:004DB9F5                 call    idea_constructor
      seg009:004DB9FA                 mov     ebx, eax
      seg009:004DB9FC                 push    0
      seg009:004DB9FE                 mov     edx, [ebp+arg_0]
      seg009:004DBA01                 mov     ecx, 80h
      seg009:004DBA06                 mov     eax, ebx
      seg009:004DBA08                 call    idea_initialize
      seg009:004DBA0D                 mov     eax, [ebp+var_4_size]
      seg009:004DBA10                 push    eax
      seg009:004DBA11                 mov     ecx, edi
      seg009:004DBA13                 mov     edx, esi
      seg009:004DBA15                 mov     eax, ebx
      seg009:004DBA17                 call    idea_decrypt_cbc
      seg009:004DBA1C                 mov     eax, ebx
      seg009:004DBA1E                 call    idea_cleanup
      seg009:004DBA23                 mov     eax, ebx
      seg009:004DBA25                 call    save_iv
      seg009:004DBA2A                 mov     eax, ebx
      seg009:004DBA2C                 call    @System@TObject@Free$qqrv_1 ; System::TObject::Free(void)
      seg009:004DBA31                 pop     edi
      seg009:004DBA32                 pop     esi
      seg009:004DBA33                 pop     ebx
      seg009:004DBA34                 pop     ecx
      seg009:004DBA35                 pop     ebp
      seg009:004DBA36                 retn    4
      seg009:004DBA36 sub_4DB9E0      endp
      

      This is the object creation pattern I was looking for:

      seg009:004DB9EE                 mov     dl, 1
      seg009:004DB9F0                 mov     eax, ds:TIdeaCipherClass
      seg009:004DB9F5                 call    idea_constructor
      seg009:004DB9FA                 mov     ebx, eax
      

      When you look at the data (TIdeaCipherClass), it a has very recognizable structure and also a name.. 🙂

      seg009:004DB030 TIdeaCipherClass dd offset TIdeaCipher  ; DATA XREF: sub_4DB984+10r
      seg009:004DB030                                         ; sub_4DB9E0+10r
      seg009:004DB030                                         ; Pointer to self
      seg009:004DB034                 dd 0                    ; Pointer to interface table
      seg009:004DB038                 dd 0                    ; Pointer to Automation initialization table
      seg009:004DB03C                 dd 0                    ; Pointer to instance initialization table
      seg009:004DB040                 dd 0                    ; Pointer to type information table
      seg009:004DB044                 dd 0                    ; Pointer to field definition table
      seg009:004DB048                 dd 0                    ; Pointer to method definition table
      seg009:004DB04C                 dd 0
      seg009:004DB050                 dd offset TIdeaCipher   ; Class name pointer
      seg009:004DB054                 dd 0E4h                 ; Instance size
      seg009:004DB058                 dd offset TObjectClass  ; Pointer to parent class
      seg009:004DB05C                 dd offset @TObject@SafeCallException ; SafeCallException  method
      seg009:004DB060                 dd offset @TObject@AfterConstruction ; AfterConstruction  method
      seg009:004DB064                 dd offset @TObject@BeforeDestruction ; BeforeDestruction method
      seg009:004DB068                 dd offset @TObject@Dispatch ; Dispatch method
      seg009:004DB06C                 dd offset @TObject@DefaultHandler ; DefaultHandler method
      seg009:004DB070                 dd offset @TObject@NewInstance ; NewInstance method
      seg009:004DB074                 dd offset @TObject@FreeInstance ; FreeInstance method
      seg009:004DB078                 dd offset @TObject@Destroy ; destructor Destroy
      seg009:004DB07C TIdeaCipher     db 11,'TIdeaCipher'     ; DATA XREF: seg009:TIdeaCipherClasso
      seg009:004DB07C                                         ; seg009:004DB050o
      seg009:004DB07C                                         ; Kind: TTypeKind;
      

      So, that's how I knew which crypto algorithm is used..

  2. WoooW! Thank you so much kao for your answer and for the time you spent to explain to me these details, making screenshots and code listings! This is in fact NOT a short reply and it answers precisely my question, thanks again!

    I'm not a good IDA user and, even if I saw the "xref graph" option, I never understood its usefulness ...
    Moreover you "disclosed" some Delphi reversing tip like the object creation pattern, and this will be of great help in future too.

    So, what can I say? You're awesome!
    I'm only afraid I'm sure I won't be able to reciprocate your continued help 🙁

    Best Regards,
    Tony

Leave a Reply

  • Be nice to me and everyone else.
  • If you are reporting a problem in my tool, please upload the file which causes the problem.
    I can`t help you without seeing the file.
  • Links in comments are visible only to me. Other visitors cannot see them.

three  ×  two  =