Anti-Petya live CD (the fastest Stage1 key decoder)

newUPDATE: 17-th July a new version of Petya has been released. At the moment, there is no way to decrypt the disk. Don’t let the infection reach the Stage 2!

Please read first Petya key decoder for more background information.

If you opened some executable downloaded from the Internet and your system crashed, it can be attack of PETYA RANSOMWARE.

Please do not let the system reboot from the hard disk! I will cause infection to progress.

Run your computer from a live CD (i.e. Kali linux)  and make a backup of the full disk. Example:

dd if=/dev/sda of=dump.bin bs=512

If you caught Petya at Stage 1 your files are still untouched and there is a chance to save them.

Petya detector in form of a bootloader:

With the help of this small tool you can easily check if Petya has been detected on your disk. In case of Petya versions 1 and 2 it can also recover the Stage 1 key.

ISO: antipetya.iso
BIN: antipetya.bin

Source code :

In order to use it,  just (from a different computer) download the antipetya.iso, burn on a CD and boot your machine from the CD.

Alternatively, instead of CD you can use a flash disk. In this case you need to dump antipetya.bin on to your disk. Example (using Linux):
1. Log in as root.
2. Check how your flash disk is represented in the system.
If the flash disk is /dev/sdb:

dd if=antipetya.bin of=/dev/sdb bs=512 count=1

The tool will give you a quick info, whether your bootloader has been substituted by the bootloader of Petya.
In case of versions 1 and 2 of Petya -it will give you the Stage 1 key automatically:

Output of Antpetya Live CD (Stage1):


Write down this key. This is the same key, that Petya uses to encrypt/decrypt your data:


If the Petya already erased the key, the bootloader will inform you about this fact. In case of the Red Petya, your disk still can be decrypted at the Stage 2 – you can read about it here: Stage2 decoder. Unfortunately, such recovery procedure does not work for the current Green version.

Posted in Malware, Malware Decryptor, Tools | 5 Comments

Petya key decoder

source code (Red Petya recovery)


[17 July 2016]new
A new (3-rd) version of Petya has been released – the current solutions no longer work. It looks exactly like the previous (green) Petya, but contains fixes in cryptography implementation and different Stage 1 keys. More info here.

[02 June 2016]
Procrash prepared a brutforcer for Green Petya key (using multi-threading and GPU for the high performance). Sourcecode available here: Cracking the key takes about 3 days.

[20 May 2016]
I found a weakness in Green Petya’s Salsa20 implementation (only 8 out of 16 characters of the key matters – read more here). I made a prototype decoder performing random attack on the key.

[ 14 May 2016 ]
I added support for the new (green) version of Petya to the antipetya live CD – again the key can be recovered from Stage 1 – unfortunately, there is no solution for Sage 2 so far.

[ 12 May 2016 ]
A new (green) version of Petya has been released – the current solutions no longer work.

[ 21 April 2016 ]
m0rb packaged my tools into antipetya_multi.iso – now recovering Stage 2 key is simpler and faster (no Kali Linux required). You can see it in action on the video.

[ 18 April 2016 ]
I made much faster way to recover the key from Stage 1. Read more:

[ 17 April 2016 ]
reimplemented leo-stone‘s idea in C++ and integrated with my code. (Thanks!) You can find new version here: petya_recovery (64 bit ELF) – I updated the tutorial to describe the new feature.

[ 8 April 2016 ]
Petya at Stage 2 has been cracked by leo-stone. Read more: and
. Congratulations to the author! I updated my decoder – now if if cannot give you the Stage1 key, it will give you the data necessary to supply to the Leo’s web application: [1] or [2]

[ 31 March 2016 ]
I made a decoder for key of Petya ransomware . It works for Stage 1 of encryption – if the system was not rebooted after the infection. Research about a possibility to decrypt Stage 2 is in progress.

My research is possible thanks to Malwarebytes.
Disclaimer: This tool is an experiment in unlocking a particular kind of Ransomware, neither Malwarebytes or Hasherezade promise this tool will help in your particular case. This tool should not be considered an official solution to the Petya problem. Any files destroyed, further encrypted or otherwise tampered with against the desire of the user are not the responsibility of the developers. Please use at your own risk.


WARNING: The Stage 2 decoder works only for the Red Petya!

You can download the decoder’s binary here (it is 64bit ELF). Sourcecode is available here.

Few tips

If you opened some executable downloaded from the Internet and your system crashed,
it can be attack of PETYA RANSOMWARE.

Best is if you don’t let the system reboot after the blue screen. However, even if you didn’t managed to catch Petya at proper time, still there is a chance to recover your data.

What to do:
1) From another computer download i.e. Kali Linux ISO 64 bit ( and record on a DVD
2) Boot the computer that crashed from this DVD, choose forensic mode.


3) Now your original hard disk should be mounted. Find it’s identificator, i.e using:

fdisk -l

Sample output:

Device     Boot Start     End Sectors  Size Id Type
/dev/sda1  *      [....]

it means your disk is sda
4) Download the decoder and make it executable (chmod +x decoder). Run it:

./decoder /dev/sda

It will tell you if known symptoms of Petya have been detected on your disk:

[+] Petya bootloader detected!
[+] Petya http address detected!
[+] Petya FOUND on the disk!

If you managed to catch Petya at Stage1, this decoder will give you a key directly:

Key: 8fb9GLT7qkQJ5hBu
[OK] Stage 1 key recovered!!

In other case, we need to recover from the Stage2. It may take up to few minutes. Wait till your key appears:

[+] Trying to decrypt... Please be patient...
ugxwErH4 89
hiSwhrau 77
ugdwErH4 74
hiSwhra4 69
ugdPErH4 67
hiSw1ra4 62
hgSPErH4 59
hiSw1raB 56
gAf31aib 51
hcfw1raB 48
hAf31aib 42
XqfJ115b 38
XPgK115b 37
Xac4115b 35
XaFF1A5e 32
AaFD1Q5B 28
xa8D1Q5B 26
xaM51Q5B 25
x4Gu1Q5B 24
xbGu1Q5F 22
xbG41Q1r 21
xbGt1QuB 18
8bGTqQ5B 0
[+] Key generation finished
[+] Validation passed
[+] YOUR KEY: 8xbxGxTxqxQx5xBx

5) Copy or write down the resulting key. It is very important for recovery!
6) Even if the decoder gave you a key, new Petya versions may come with some changes. That’s why, I cannot guarantee that this key will be valid for you!
I strongly recommend you to make a dump of full disk.
First mount an external disk of appropriate capacity and then dump there the full disk:

dd if=<path_to_infected_disk> of=<output_path>

example (dump an image of the infected disk into a file stored on external disk – in my case the external disk was mounted as kingston):

dd if=/dev/sda of=/media/root/kingston/disk_dump.bin

You can also clone one disk on another – read more here.

After that, you can reboot your system from the disk. If the Petya screen appear, supply the key that you got from the decoder:

After entering the key, Petya will inform you about the progress of decrypting your system. Wait for your system to decrypt.  It will inform you when you can reboot the computer. After that, your system should boot normally.

Posted in Malware, Malware Decryptor, Tools | 19 Comments

Introduction to ADS – Alternate Data Streams

Sometimes during automated malware analysis in a sandbox (i.e. Cuckoo), we can get in the report the following information: “creating alternate data streams”. It is related with an interesting feature of NTFS file system,  that can be used for hidden channels of storing and exchanging information.

Historical context

ADS are from an era when we had resource forks in HFS (Macintosh Hierarchical File System) and the idea was that files would “carry” everything with them, possibly even the application needed to open them, or the fonts needed to view them in many ways this was a beautiful design and idea which sadly never came to fruition. For example a text file could have carried its translations in ADS, a Braille version, RTF and TXT, etc. but also its images in various resolutions depending on screen DPI. All without cluttering the “main view” or those gigantic Word files. – via @cynicalsecurity


In FAT file system – used by old versions of windows – file consisted of 2 elements: attributes and data.

In NTFS it i different – file consists of attributes, security settings, main stream and alternate streams. By default, only the main stream is visible.

Let’s see how it works by creating a sample file: test.txt. At this moment it’s main stream will be empty. However, we will create an alternte data stream. We can write into it using echo command and simple stream redirection.
Naming convention:

optionally we can use ::$DATA at the end, i.e:

echo This message is saved in the ADS > testfile.txt:hidden_stream

echo This message is saved in the ADS > test.txt:hidden_stream

Let’s list the directory and see the newly created file (test.txt)



As we can notice, the file length is displayed as 0 bytes. If we try to open this file by some text editor (i.e notepad) we can see that it is empty. Does it really have something inside? Let’s confirm:

more < test.txt:hidden_stream

more < test.txt:hidden_stream

Now, finally, our text showed up.

So, how we will find out what are the alternate data streams available in particular files? There are several tools dedicated to reading and editing ADS, but if we don’t want to bother about it, we can just use a command dir, with an appropriate parameter:


dir /R – display alternate data streams of the file


Now we can see the same file, test.txt, listed twice: once with a size 0, and then again – with the size 35, with the ADS name added.

We can edit the file in a normal way, and the alternative stream will stay untouched. By the same way we can create several streams.


File in file using ADS

Example 1

We can also hide another file on the alternate data stream. On the below example – we create a new txt file on another. We can then edit it with typical tools:



Yet, opening the file by default way, we can only see it’s main stream:


Example 2

We can also paste an existing file on an alternate data stream, by using a command type

Let’s take as an example a demo.dll – it is a 32bit Portable Executable, exporting one function: Test1. We will place it in the alternate stream of test.txt

type demo.dll > test.txt:demo

type demo.dll > test.txt:demo

Maybe the alternate stream it is hard to notice – but running it is still very easy:


rundll32 test.txt:demo,Test1

Example 3
Exactly the same can be done with (malicious) macros:

type‬‬ ‫‪malware.vbs‬‬ > ‫‪readme.txt:malware.vbs‬‬
‫‪Wscript‬‬ ‫‪‫‪readme.txt:malware.vbs‬‬


One of the legitimate usages of alternate data streams is Zone.Identifier. It is a feature used to identify the file origin. In case if the file comes from some untrusted source, i.e. have been downloaded from the internet, Windows displays a security warning before it can be run.

There are several variants of Zone.Identifier value:

0 My Computer
1 Local Intranet Zone
2 Trusted sites Zone
3 Internet Zone
4 Restricted Sites Zone


Sample content of Zone.Identifier of the file downloaded from the internet:


Malware downloaders may edit Zone.Identifier of the downloaded file, in order to make it run without displaying alert.

ADS and PowerShell

PowerShell comes with a built-in feature to read ADS. There are several commands that can be used to read and edit them:

  • Get-Item
  • Set-Item
  • Remove-Item
  • Add-Content
  • Get-Content
  • Set-Content


Listing all the streams of a file:

Get-Item -Path [filename] -Stream *

Adding hidden message into ADS:

Add-Content -Path [filename] -Value [my hidden message] -Stream [new_stream]


Creating ADS from commandline:

‫‪echo This is a hidden message > testfile.txt:hidden_stream

Displaying files with their alternative data streams:

dir /r

Displaying stream of a file:

more < testfile.txt:hidden_stream::$DATA


Posted in Malware, Uncategorized | Leave a comment

DMA Unlocker

I managed to crack some of the variants of DMA Locker ransomware (version with RSA key), described [here].

For those who are hit by this version , there is an experimental decryptor for it. More details and updates you can find on the dedicated page: here

Posted in Malware, Malware Decryptor, Tools | Leave a comment

Solving KeygenMe V7 by MaxX0r – part 1

This KeygenMe I’ve got personally from the author (MaxX0r). Thank you for such a nice gift and congrats for a good job!

The difficulity level is 3 (according to
It doesn’t contain any advanced crypto, but is nicely obfuscated, self-modifying and containing some defensive features. Due to this properties, it have pretty high detection ratio on VirusTotal. But I ensure you, it is not a malware (just a false positive)! If you still have doubts, you can read thought the code (I will try to describe it clearly in my tutorial).


The KeygenME is a 32-bit PE file. You can download it here (also available on

KMv7_trialAuthor sets several achievement levels (see full list).


Here I would like to present my solution for GOLD, that is:

* create a working keygen
* remove the nag screen

KeyGen source:

+ the version with Nag removed:



Tutorial 1 – removing nag screen

Due to the fact, that I would like to make a detailed tutorial, that is easy to follow for beginners, I divided it in 2 parts. This one is dedicated to removing Nag screen. In the next part, I will describe keygenning. If you wish to see explanation of obfuscation techniques only, you can go directly to the end of the article.

First, I loaded the KeygenMe into ImmunityDebugger:


and searched for “All referenced text strings” (left mouse button click, then: Search for -> All referenced text strings)

It could possibly show as the place, where the “Success” message is displayed. But, no luck this time – no such thing is on the list. Taking a closer look, we notice, that neither the Nag message is present. For sure they are encrypted/obfuscated somehow!

By the way, I spotted some other interesting strings: ntdll.dll, wcsnmp, memset, wscsncpy, comctl32.dll, InitCommonControls, SeDebugPrivilege. It seems, the application loads some functions dynamically. We will not go in details right now, but lets keep it in mind.

Let’s first focus on finding the place, where the Nag and SuccessMsg are stored.

IDA graphs are going to be helpful, so I loaded the same file now to IDA.

Graph of start function have a weird shape, which suggest that some transitions between code chunks are obfuscated.

Also, there is some long piece of code on the stage, which seems to load some obfuscated string (WindowName). It moves a lot of bytes into consecutive variables (probably forming some buffer) . Then calls a function and performs some decoding with it’s help- I given to the called function a name _decode


The body of _decode function is simple. However, it also contain some obfuscation – by breaking standards of argument passing. Usually ECX register is used as a counter, ESI – as a source and EDI as destination. But not here, as you can see…


Let’s see Xrefs to this function – maybe it will help in finding Nag and SuccessMessage:


As we see it is called from 3 functions:

  • start – as we see above, it decodes WindowName
  • sub_401856
  • sub_402551

Let’s take a closer look at the 2 remaining calls.

sub_401856: Function _decode is called in several places. However it decodes some small (max 5-bytes long) chunks… But we are searching for the strings, so let’s skip it for now… (anyways this function looks interesting. It refers to function like GetDlgItemTextW – seems to be fetching user input and then processing it. Accidentally, we found the key verification function! So, from now I will refer sub_401856 as KeyVerification)


Let’s move on:

sub_402551: looks much more promising. It have two paths of execution, one of them is decoding some lengthy buffer (probably a string) + refer to MessageBoxW.


It is referred from the start  – so, seems to be the Nag rather than the SuccessMsg.


As we see, in this case the reference doesn’t mean a direct call. The address is pushed on the stack. After that, some other addresses are also pushed, and then there is RET. It is some complex version of calling via PUSH-to-RET. The function that is called from here is in reality sub_4013B0:


The function takes 2 parameters. It above case, one is the address 4029F6 and the other is the handle to CreateWindowExW.  The address of our interest: sub_402551(the potential Nag) is not among them. To see how it is called, we must first understand how the sub_4013B0 works.

Inside we see the heap allocation and changing access rights to allocated space. Seems, some code is going to be written.

Also, two functions are called by the handlers, that are dynamicaly filled somewhere else:

  • dword_40300C
  • dword_403000

Some dynamic analysis will be required (and I prefer doing it in ImmunityDebugger). So I loaded it in Immunity, set breakpoint at 4013B0 and run. The mysterious handlers got filled, and now we see that

  • dword_40300C -> ntdll.memcpy
  • dword_403000 -> ntdll.memset

Let’s see what they are used for. Here is dump of parameters:

|dest = 001F7268
|src = USER32.CreateWindowExW
\n = 5

|s = USER32.CreateWindowExW
|c = E9 -> JMP
\n = 1

|dest = USER32.7685EC7D
|src = 0012FEB0
\n = 4

|s = 001F726D
|c = E9 -> JMP
\n = 1

|dest = 001F726E
|src = 0012FEB0
\n = 4

Do you see the “code stealing”? First, 5 bytes from the beginning of CreateWindowExW is copied into allocated space. It is overwriten by the jump (E9 = opcode of JMP) to the address, stored on the stack (at 12FEB0). Then, another JMP is writen – this time into the allocated space. Let’s have a look how the CreateWindowExW looks after the modification:


Do you recognize the address of the jump? It’s sub_402551(the potential Nag)! So, finally we found how it is called – whenever CreateWindowExW is called, it jumps to our procedure first.

Let’s see what is written into the allocated space:

001F7268   8BFF             MOV EDI,EDI
001F726A   55               PUSH EBP
001F726B   8BEC             MOV EBP,ESP
001F726D   E9 0F7A6676      JMP USER32.7685EC81

It is the stolen prolog and redirection to the rest of the code of CreateWindowExW. Ok, now we know, that original CreateWindowExW can be now called by refering to the address 1F7268.

Coming back to the 4013B0 (from now I will refer is as: StealCode)

I also set the breakpoint on the return, and follow where the navigation goes next:

004029FC  MOV DWORD PTR DS:[403018],EAX
00402A01  PUSH KeygenMe.004017BE
00402A06  PUSH DWORD PTR DS:[<&USER32.CharUpperW>]
00402A0C  PUSH KeygenMe.00402A17
00402A11  PUSH KeygenMe.004013B0; StealCode
00402A16  RETN

Another “code stealing” is performed. Now the affected procedure is CharUpperW. I don’t think it is necessary to go again in details, just set a breakpoint at the end of StealCode and see what was overwritten:

At the beginning of CharUpperW the redirection has been placed:

7686E981 JMP KeygenMe.004017BE

The procedure at 4017BE takes as a parameter a  buffer of characters. It contains a big switch, which is substituting some characters by the others, by the defined pattern. I named it MixCharacters. So now we know, that CharUpperW leads to calling MixCharacters.

After the second “code stealing”, navigation goes to:


(in the procedure named start by IDA). It leads to the chunk that is decoding WindowName, which we already saw in the previous part of the research.


Then, the CreateWindowEx is called (as we found previously – it is hooked, and leads to execution of sub_402551(the potential Nag). And then the message dispatching loop is executed.

Let’s  set a breakpoint at call CreateWindowExW. It’s parameters are redirected to 402551. Now we can notice, that the parameter, deciding which path should be followed, is in reality the WindowWidth*!

* to be explicit: CreateWindow creates not Windows only, but a variety of controls, like labels, buttons, etc.


To sum up:

  • if the control width is different than 0x78 = 120 -> Execute original CreateWindowEx.


  • if the window width == 120 -> Execute the Nag.

Knowing this, we can remove a nag window by simply patching this check (at 40255E), and changing conditional jump to unconditional.

Obfuscation and defensive technics

The thing I enjoyed the most in this KeygenMe, was its obfuscation and set of defensive techniques (similar techniques are used by malware, that’s why AV systems picked on this).

I noted following:

  • dynamically loaded imports

(Handles to some functions were dynamically loaded and stored in variables. Then, calls were made via this variables  – to make static analysis more difficult)

Loading functions:


Using loaded function (example):


  • self-modifying code

Inside the key verification function we can find some points of code, that ends with Infinite loops (opcodes: EB FE)


It would not make any sense… However, during program execution, this places are modified, and they are converted to jumps to the continuation of the code!

  • calling functions in non-obvious way, i.e. complex “push-retn”KMv7_call_nag
  • “stolen code”, import hooking
    • beginning of the imported function is stolen (removed, and copied inside the KeygenMe space)
    • it is substituted by the call to “a proxy function” (belonging to a KeygenMe)
    • original code of imported function is called after the “proxy function” finished execution

Execution flow:

Let’s see all this elements on the example of the KeygemMe:
User32.dll -> CharUpperW (original):


User32.dll -> CharUpperW (modified):


stolen code + jump to rest of the function:


Function inside the KeygenMe, calling the stolen code via saved pointer (refered on the graph as: Application.myFunction ):


  • time-based anti-stepping protection:

RDTSC is called and it’s result is saved in a variable. In another place, RDTSC is called second time, and the result is compared with the saved one.

If the time difference is > 70000h, the process of generating key is distorted:

Posted in CrackMe | 3 Comments

PE-bear – version 0.3.7 available!

This time I focused on improving usability and implementing the features requested by the users
/* greetz to: Vincent, dummys, Ivan, sdax/ringi, Javier, and others! */


Major bugfixees:
– Support for unicode paths and tags

Major feature list:
– automatic calculation of: MD5/SHA1/Checksum
– opening files via shortcuts
– saving path to last open file
– added: reload PE

…and more!



Posted in PE-bear | 4 Comments

Solving 7+ years old crackme (CC-Team, Arigo CrackMe8)

[Polish version / polska wersja]


Yesterday night I’ve got an idea to search some unsolved Polish crackme. And I bumped into this one: There is no solution on the site, and I found none searching in Google. Perhaps, it was waiting for the solution above 7 years :D.

It is not difficult crackme ( maybe level 2-3 according to But yet entertaining and giving a good opportunity to illustrate some concepts to beginners.

This challenge don’t have one defined solution -but I hope mine is good enough for what the author expected 🙂 Here it is: loader.c


Let’s start by downloading the file: cm8 (mirror cm8).

Its 32- bit ELF.

The goal is: “deploying program so that it will print the congratulation message and return 0”. Binary edition is acceptable, but not desired.

After deploying it just prints the welcome message “crackme6 by arigo” end exits. So, the first guess is, it should be deployed with some args. After playing around I noticed, that supplying 3 random args will crash the app. OK, let’s look inside…


$ objdump -d ./cm8

./cm8:     file format elf32-i386

Disassembly of section .text:

080480a0 <_start>:
 80480a0:    89 e6                    mov    %esp,%esi
 80480a2:    31 c0                    xor    %eax,%eax
 80480a4:    b0 04                    mov    $0x4,%al
 80480a6:    31 db                    xor    %ebx,%ebx
 80480a8:    43                       inc    %ebx
 80480a9:    68 6f 0a 41 41           push   $0x41410a6f
 80480ae:    68 61 72 69 67           push   $0x67697261
 80480b3:    68 20 62 79 20           push   $0x20796220
 80480b8:    68 6b 6d 65 36           push   $0x36656d6b
 80480bd:    68 63 72 61 63           push   $0x63617263
 80480c2:    89 e1                    mov    %esp,%ecx
 80480c4:    31 d2                    xor    %edx,%edx
 80480c6:    b2 12                    mov    $0x12,%dl
 80480c8:    cd 80                    int    $0x80
 80480ca:    89 f4                    mov    %esi,%esp
 80480cc:    58                       pop    %eax
 80480cd:    5b                       pop    %ebx
 80480ce:    59                       pop    %ecx
 80480cf:    5a                       pop    %edx
 80480d0:    5e                       pop    %esi
 80480d1:    68 cb cd 80 90           push   $0x9080cdcb
 80480d6:    68 31 c0 40 fe           push   $0xfe40c031
 80480db:    68 ff e6 31 db           push   $0xdb31e6ff
 80480e0:    68 4b 75 e9 5e           push   $0x5ee9754b
 80480e5:    68 88 06 41 46           push   $0x46410688
 80480ea:    68 8a 11 28 d0           push   $0xd028118a
 80480ef:    68 75 04 89 f9           push   $0xf9890475
 80480f4:    68 fe ca fe c2           push   $0xc2fecafe
 80480f9:    68 8a 06 8a 11           push   $0x118a068a
 80480fe:    68 31 c0 31 d2           push   $0xd231c031
 8048103:    68 31 db b3 5c           push   $0x5cb3db31
 8048108:    68 52 56 8d 0f           push   $0xf8d5652
 804810d:    68 ba 12 89 e6           push   $0xe68912ba
 8048112:    68 24 68 83 ba           push   $0xba836824
 8048117:    68 68 48 fb b1           push   $0xb1fb4868
 804811c:    68 a3 16 84 90           push   $0x908416a3
 8048121:    68 48 de 3e 68           push   $0x683ede48
 8048126:    68 ac 1f 68 51           push   $0x51681fac
 804812b:    68 bc 68 90 0f           push   $0xf9068bc
 8048130:    68 68 02 a4 82           push   $0x82a40268
 8048135:    68 61 c6 da 70           push   $0x70dac661
 804813a:    68 94 73 a4 68           push   $0x68a47394
 804813f:    68 8e 6b 68 7a           push   $0x7a686b8e
 8048144:    68 cd 68 8b ae           push   $0xae8b68cd
 8048149:    68 68 e4 28 96           push   $0x9628e468
 804814e:    68 28 88 34 f1           push   $0xf1348828
 8048153:    68 3e c7 22 68           push   $0x6822c73e
 8048158:    68 13 94 68 96           push   $0x96689413
 804815d:    68 9f 68 7e db           push   $0xdb7e689f
 8048162:    68 68 4e ee 6b           push   $0x6bee4e68
 8048167:    68 02 51 32 b2           push   $0xb2325102
 804816c:    68 d9 4d 3a 68           push   $0x683a4dd9
 8048171:    68 fd d8 68 2f           push   $0x2f68d8fd
 8048176:    68 f8 68 e9 50           push   $0x50e968f8
 804817b:    68 68 80 c5 28           push   $0x28c58068
 8048180:    68 48 50 3a b0           push   $0xb03a5048
 8048185:    68 95 85 86 68           push   $0x68868595
 804818a:    68 65 23 68 2f           push   $0x2f682365
 804818f:    68 6d 68 9d 3b           push   $0x3b9d686d
 8048194:    68 68 ad 17 a7           push   $0xa717ad68
 8048199:    68 1f b0 02 b8           push   $0xb802b01f
 804819e:    68 00 00 5a 68           push   $0x685a0000
 80481a3:    68 0f 85 9c 00           push   $0x9c850f
 80481a8:    68 5f 5f 3c 04           push   $0x43c5f5f
 80481ad:    68 a6 00 00 00           push   $0xa6
 80481b2:    68 f8 4f 0f 84           push   $0x840f4ff8
 80481b7:    68 31 c0 5f 89           push   $0x895fc031
 80481bc:    89 e7                    mov    %esp,%edi
 80481be:    6a 00                    push   $0x0
 80481c0:    56                       push   %esi
 80481c1:    52                       push   %edx
 80481c2:    51                       push   %ecx
 80481c3:    53                       push   %ebx
 80481c4:    50                       push   %eax
 80481c5:    ff e7                    jmp    *%edi

The code is pretty obfuscated 🙂
First part (till 80480c8) calls int80 with eax = 4 (sys_write) and bl=0x12 – it prints 0x12 characters of string pushed on the stack in form of DWORDs (it is the welcome message).
The second part [80480ca to 80481c5] is more mysterious – and- easy to notice – here the key lies. Some content is pushed into the stack, and then it is executed…
It comes handy to open the crackme in gdb with TUI mode:
gdb -tui ./cm8
We will use 2 layouts: layout asm, layout regs.
First let’s open layout asm. Set breakpoint at the line with jump *edi, and then run the app with some 3 arguments…
Press enter, and when it breaks type
(for the next instruction – in this case it is within the stack!)
That’s cool, because now all the secrets revealed: we can see the obfuscated part of code.

For the convenience I copied it aside and added comments. (Mind that stack addresses are random each execution).

>│0xbffff4c8  xor  eax,eax  ; EAX = 0  
 │0xbffff4ca  pop  edi    ; EDI <- argc  
 │0xbffff4cb  mov  eax,edi   
 │0xbffff4cd  dec  edi   ; if (argc - 1) == 0  
 │0xbffff4ce  je  0xbffff57a ; goto exit_with_error  
 │0xbffff4d4  pop  edi    
 │0xbffff4d5  pop  edi    
 │0xbffff4d6  cmp  al,0x4  ; if (argc != 4) -> supplied NOT 3 args 
 │0xbffff4d8  jne  0xbffff57a ; goto exit_with_error  
 │0xbffff4de  pop  edx    

 │0xbffff4df  push  0xb802b01f   ; pushing on the stack 23 DWORDS -> 23 * 4 = 92 BYTEs 
 │0xbffff4e4  push  0x6da717ad  
 │0xbffff4e9  push  0x23653b9d  
 │0xbffff4ee  push  0x8685952f  
 │0xbffff4f3  push  0xb03a5048  
 │0xbffff4f8  push  0xf828c580  
 │0xbffff4fd  push  0xd8fd50e9  
 │0xbffff502  push  0x3a4dd92f  
 │0xbffff507  push  0xb2325102  
 │0xbffff50c  push  0x9f6bee4e  
 │0xbffff511  push  0x9413db7e  
 │0xbffff516  push  0x22c73e96  
 │0xbffff51b  push  0xf1348828  
 │0xbffff520  push  0xcd9628e4  
 │0xbffff525  push  0x6b8eae8b    
 │0xbffff52a  push  0xa473947a  
 │0xbffff52f  push  0x70dac661  
 │0xbffff534  push  0xbc82a402  
 │0xbffff539  push  0x1fac0f90  
 │0xbffff53e  push  0x3ede4851  
 │0xbffff543  push  0x908416a3  
 │0xbffff548  push  0x24b1fb48  
 │0xbffff54d  push  0x12baba83  
 │0xbffff552  mov  esi,esp  <- stack top  
 │0xbffff554  push  edx    
 │0xbffff555  push  esi    
 │0xbffff556  lea  ecx,[edi]  ; ECX =  1-st arg  
 │0xbffff558  xor  ebx,ebx  <- EBX = 0  
 │0xbffff55a  mov  bl,0x5c  <- EBX = 0x5c = 92  
 │0xbffff55c  xor  eax,eax  <- eax = 0  
 │0xbffff55e  xor  edx,edx  

loop_top : do EBX = 92 times:
 │0xbffff560  mov  al,BYTE PTR [esi]  <- take from the stack to AL    
 │0xbffff562  mov  dl,BYTE PTR [ecx]  <- take from 1st arg to DL 
 │0xbffff564  dec  dl  ;   
 │0xbffff566  inc  dl  ; test DL, DL   

  /0xbffff568  jne  0xbffff56e  ; if (DL != 0) goto decode
 │ 0xbffff56a  mov  ecx,edi  ; reset ECX (start buffer from the 1st arg beginning)  
 │ 0xbffff56c  mov  dl,BYTE PTR [ecx]  ; take character from the buffer
 │0xbffff56e  sub  al,dl  ; AL -= DL  
 │0xbffff570  mov  BYTE PTR [esi],al   ECX ++
 │0xbffff573  inc  esi  ; take next byte  -> ESI ++  
 │0xbffff574  dec  ebx  ; decrement the counter -> EBX--
^---0xbffff575  jne  0xbffff560  ; if (ebx != 0)  goto loop_top

 │0xbffff577  pop  esi  ; recover pointer to stack top   
 │0xbffff578  jmp  esi  ; redirect to the CODE ON THE STACK!
 │0xbffff57a  xor  ebx,ebx  <- ebx = 0  
 │0xbffff57c  xor  eax,eax  <- eax = 0  
 │0xbffff57e  inc  eax  ; eax = 1  
 │0xbffff57f  dec  bl   ; bl = (-1) = 0xff 
 │0xbffff581  int  0x80  ; sys_exit (-1)

Summing up:

What this code do:
1. check if there are 3 arguments -> if not: sys_exit (-1)
2. otherwise: push 92 bytes on the stack
3. take the 1-st argument as the KEY
4. decode the pushed content with the KEY (by simple subtraction), pseudocode:

for (i = 0, j = 0; i < 92; i++, j++) { 	
	if (j >= key_len) j = 0;
	pushed[i] = pushed[i] - key[j];

Means, the KEY can be anything that, subtracted from the pushed array, will give a valid output…
But what the valid output should be? This is not precisely defined. We only know that we must fit in 92 bytes, do printing and clean exit…
In the previous crackmes of the same author the congratulation message was:
got it 😉
so, let’s stick to it.
If we take a closer look, all the composing parts of the solution are already in the crackme, we must just assemble it. Let’s take string printing part and adjust it:

B0 04                 mov     al, 4
31 DB                 xor     ebx, ebx
43                    inc     ebx             ; fd
68 2D 29 0A 41        push    410A292Dh       ; '-)/0aA'
68 69 74 20 3B        push    3B207469h       ; 'it ;'
68 67 6F 74 20        push    20746F67h       ; 'got '
89 E1                 mov     ecx, esp        ; addr
31 D2                 xor     edx, edx
B2 0B                 mov     dl, 0Bh         ; len
CD 80                 int     80h             ; LINUX - sys_write

And the clean exit:

33 DB      xor  ebx,ebx  <- ebx = 0  
33 C0      xor  eax,eax  <- eax = 0  
40         inc  eax  <- eax = 1  
CD 80      int  0x80  ; sys_exit (0)

After writing down the opcodes it will look like:

char solution[] = { 0xB0, 0x04,
		0x31, 0xDB,
		0x68, 0x2d,  0x29, 0x0A, 0x41,
		0x68, 0x69, 0x74, 0x20, 0x3b,
		0x68, 0x67,  0x6f, 0x74, 0x20,
		0x89, 0xE1,
		0x31, 0xD2,
		0xB2, 0x0B,
		0xCD, 0x80,
		0x33 , 0xdb,
		0x33, 0xc0,
		0xcd, 0x80

And what more we need is the pushed content in the same form:

	char pushed[] = {0x83, 0xba, 0xba, 0x12, 0x48, 0xfb, 0xb1, 0x24,
		0xa3, 0x16, 0x84, 0x90, 0x51, 0x48, 0xde, 0x3e, 0x90, 0x0f,
		0xac, 0x1f, 0x02, 0xa4, 0x82, 0xbc, 0x61, 0xc6, 0xda, 0x70,
		0x7a, 0x94, 0x73, 0xa4, 0x8b, 0xae, 0x8e, 0x6b, 0xe4, 0x28,
		0x96, 0xcd, 0x28, 0x88, 0x34, 0xf1, 0x96, 0x3e, 0xc7, 0x22,
		0x7e, 0xdb, 0x13, 0x94, 0x4e, 0xee, 0x6b, 0x9f, 0x02, 0x51,
		0x32, 0xb2, 0x2f, 0xd9, 0x4d, 0x3a, 0xe9, 0x50, 0xfd, 0xd8,
		0x80, 0xc5, 0x28, 0xf8, 0x48, 0x50, 0x3a, 0xb0, 0x2f, 0x95,
		0x85, 0x86, 0x9d, 0x3b, 0x65, 0x23, 0xad, 0x17, 0xa7, 0x6d,
 		0x1f, 0xb0, 0x02, 0xb8};

The only remaining thing is decoding:

	size_t size = sizeof(solution);
	for (int i = 0, j = 0; i < size ; i++, j++) {
		unsigned char res = pushed[i]- solution[j];
		printf("\\x%02x", res);

And the result:
must be supplied as a 1-st argument to ./cm8. Can be done i.e by this small loader:

#include <stdio.h>
#include <unistd.h>

char *args[4] = {"0", 

int main()
	execve("./cm8", args, NULL);
	return 0;

That’s all! Hope you enjoyed 🙂

Posted in CrackMe | Tagged , , , | 2 Comments