22 January 2019

GandCrab Version 5.1 "Anti-Disassembly"


MD5: 3f61255bbe14bc3ecae46c0c7fb7b6d0
POST: https://twitter.com/ValthekOn/status/1085896672897650688

Again new trick related to the anti-disassembly from the GandCrab Ransomware in version 5.1.


Below the "problem", which can be found in most of the functions.



IDA is not able to disassembly correctly the opcodes. Find below a manually "fixed" disassembly.

Part 1:


Explanation (Part 1):
      1. Stores to ecx 0x1C3D2
      2. Calls sub_40572E, and makes [esp] = 0x405723 (return pointer)
      3. Adds to the return ptr [esp] + 0x12 == 0x405735. So ones the return instruction is found won't return to 0x405723 but to 0x405735.
      4. Jumps to 0x405723
      5. Compares the previously stored ecx value to 0x1FEFF58.
      6. Will jump to 0x405719+2 as the previous comparison will set the sign flag.

Part 2:

  

Explanation (Part 2):
      7. As mentioned in the 6th step the process will jump to the address 0x405719+2, the opcode at this location is C3, which is translated 
         to the retn instruction. Now the program will return to the new return pointer which was edited in 3rd step, 0x405735.
       8. First instruction out of the anti-disassembly technique.


By replacing those hex values "B9D2C30100E80B00000081F958FFFE0178F079F8E983042412E2EFE8", with "90909090909090909090909090909090909090909090909090909090" we manage to fix the code to be viewable in IDA Graph view, able to decompile corectly and to be usable (without changing addresses).

IDA Graph View after applying the "fix" 

1. Change Example (OEP)

2. Change Example (Now possible to decompile the code correctly)


Code to fix this "problem".
1:  # GandCrab version 5.1 anti-dissasembly fix  
2:  import os  
3:  import sys  
4:    
5:  import os.path  
6:    
7:  def pre_checks():  
8:      # pre-checks & returns in & out file  
9:      if len(sys.argv) not in [2,3]:  
10:          error_log('Too few or too many arguments, --help')  
11:      in_file = sys.argv[1]  
12:      try:  
13:          out_file = sys.argv[2]  
14:      except IndexError:  
15:          out_file = get_output(in_file)  
16:      if not os.path.isfile(in_file):  
17:          error_log("Input file doesn't exist")  
18:          exit()  
19:      return in_file, out_file  
20:    
21:  def error_log(msg):  
22:      print msg  
23:      exit()  
24:    
25:  def get_output(fname):  
26:      if not os.path.isfile(fname):  
27:          error_log("Input file doesn't exist")  
28:      path = os.path.dirname(os.path.realpath(fname))  
29:      return path + "/output.bin"  
30:        
31:  def main(in_, out_):  
32:      bytes1 = "\xB9\xD2\xC3\x01\x00\xE8\x0B\x00\x00\x00\x81\xF9\x58\xFF\xFE\x01\x78\xF0\x79\xF8\xE9\x83\x04\x24\x12\xE2\xEF\xE8"  
33:      bytes2 = "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
34:      fp1 = open(in_, 'rb')  
35:      fd1 = fp1.read()  
36:      fp1.close()  
37:      fp2 = open(out_, 'wb')  
38:      fd2 = fd1.replace(bytes1, bytes2)  
39:      fp2.write(fd2)  
40:      print out_, " successfully created!!"  
41:    
42:  if __name__ == "__main__":  
43:      in_file, out_file = pre_checks()  
44:      main(in_file, out_file)  

No comments:

Post a Comment