Got a hold of this reverse me from the new crackme site that was released. It’s called “xordemo”, it was an interesting little crackme I reversed up and wanted to give a write up on.

Upon opening up xordemo inside of IDA you’ll noticed its an executable using the ELF64 format (AKA it runs on UNIX). I personally do not use Linux, and I didn’t want to go through the hassle of installing Linux, as I mostly just reverse to learn and toy around, so I just reversed it, documenting how it worked, and also writing my own program that handles decrypting.

Anyways, you’ll notice the main function is accessing program launch flags

.text:0000000000000825 push rbp .text:0000000000000826 mov rbp, rsp .text:0000000000000829 sub rsp, 20h .text:000000000000082D mov [rbp+var_14], edi .text:0000000000000830 mov [rbp+var_20], rsi .text:0000000000000834 cmp [rbp+var_14], 2 ; launch flags get accessed here and are compared to 2 .text:0000000000000838 jz short loc_84D .text:000000000000083A lea rdi, s ; "Need exactly one argument." .text:0000000000000841 call _puts .text:0000000000000846 mov eax, 0FFFFFFFFh .text:000000000000084B jmp short locret_88D

Program flags get stored in var_14 which is accessed using the frame pointer, not surprising as the frame pointer has access to local variables and function parameters (in this main function).

Further down the start function you’ll see references to a subroutine

.text:000000000000084D loc_84D: ; CODE XREF: main+13j .text:000000000000084D mov rax, [rbp+var_20] .text:0000000000000851 add rax, 8 .text:0000000000000855 mov rax, [rax] .text:0000000000000858 mov rdi, rax .text:000000000000085B call key_encryptor .text:0000000000000860 mov [rbp+var_1], al .text:0000000000000863 cmp [rbp+var_1], 0 .text:0000000000000867 jz short loc_87C .text:0000000000000869 lea rdi, aJackpot ; "Jackpot"

I renamed the subroutine to key_encryptor, but you notice the string "jackpot" is referenced right after the call to this certain subroutine. Using basic logic and the process of elimination, its obvious that that this function has some sort of play with the key itself. Going further down into it key_encryptor , you’ll notice there is a string, "1feebdab" , and an array of byte code is being stored in the rax register:

.text:0000000000000756 mov rax, '1feebdab' ; the string that gets encrypted .text:0000000000000760 mov qword ptr [rbp+s], rax .text:0000000000000764 mov [rbp+var_2E], 32h .text:000000000000076A mov rax, 10 14 0A 05 0C 17 0A 02 ; byte code that is store

"1feebdab" gets reversed later on due to endianness on x86, the littlest endian gets put up front, while the most significant gets put in the back, so I’ll just refer to it as "badbeef1" , which is what it truly is, anyways.

Later on in the program there is this big chunk of disassembly:

.text:0000000000000787 loc_787: ; CODE XREF: key_encryptor+8Dj .text:0000000000000787 mov eax, [rbp+var_3C] .text:000000000000078A cdqe .text:000000000000078C movzx ecx, [rbp+rax+s] .text:0000000000000791 mov eax, [rbp+var_3C] .text:0000000000000794 movsxd rdx, eax .text:0000000000000797 mov rax, [rbp+var_48] .text:000000000000079B add rax, rdx .text:000000000000079E movzx eax, byte ptr [rax] .text:00000000000007A1 xor ecx, eax .text:00000000000007A3 mov edx, ecx .text:00000000000007A5 mov eax, [rbp+var_3C] .text:00000000000007A8 cdqe .text:00000000000007AA mov [rbp+rax+s1], dl .text:00000000000007AE add [rbp+var_3C], 1 .text:00000000000007B2 .text:00000000000007B2 loc_7B2: ; CODE XREF: key_encryptor+4Bj .text:00000000000007B2 mov eax, [rbp+var_3C] .text:00000000000007B5 movsxd rbx, eax .text:00000000000007B8 lea rax, [rbp+s] .text:00000000000007BC mov rdi, rax ; s .text:00000000000007BF call _strlen .text:00000000000007C4 cmp rbx, rax .text:00000000000007C7 jbe short loc_787

This whole chunk loops over string, and xor’s each character at X by bytecode at X which is iterated over. For better understanding:

b gets XOR’d by 0x10 a XOR 0x14 d XOR 0x0A b XOR 0x05 e XOR 0x0C e XOR 0x17 f XOR 0x0A 1 XOR 0x02

You’ll notice on this line

movzx eax, byte ptr [rax]

the current indexed byte code gets stored in eax, which then gets XOR’d by ecx , which is the current character in the string,

movzx ecx, [rbp+rax+s] ; brackets signify a dereference

Where the XOR’ing is happening:

.text:000000000000079E movzx eax, byte ptr [rax] .text:00000000000007A1 xor ecx, eax .text:00000000000007A3 mov edx, ecx

Anyways, despite me not being able to run the program on Windows, I decided to write my own little program which does all the work for you:

std::vector do_xor() { // const char* base_key = "1feebdab"; // the string gets reversed based on the assembly shown, // so it actually is: const char* base_key = "badbeef1"; std::uint8_t byte_code[] = { 0x10, 0x14, 0x0A, 0x05, 0x0C, 0x17, 0x0A, 0x02 }; const std::int32_t size = std::strlen(base_key); std::vector data; for (std::int32_t i = 0; i < size; i++) { std::uint8_t xor_value = base_key[i] ^ byte_code[i]; data.push_back(xor_value); } return data; }

This (very bad) code will return the data back into an std::vector, which you can use to then print the key