# Diving into Pluroxs DNS based protection layer **[sysopfb.github.io/malware,/crypters/2019/09/23/Plurox-packer-layer-unpacked.html](https://sysopfb.github.io/malware,/crypters/2019/09/23/Plurox-packer-layer-unpacked.html)** Random RE September 23, 2019 Sep 23, 2019 ## Intro Recently saw someone mentioning a sample of Plurox performing code flow obfuscation based on the result of a DNS request, kind of interesting and I have apparently lost the link to the person that originally mentioned the hash… so if you recognize it let me know and I’ll update this post. The file we’ll be looking at is 0385038427750543d98ce02a2a24aef45a937ef226a53fc3f995b5cea513b1c8 PDB: ``` E:\OldSoftware\Generating\Crypto\crypto.pdb ## Strings ``` Encoded strings is pretty common in malware and especially in protection layers such as crypters/packers and droppers/loaders. Near the entry point of the sample we’ll find a call instruction with an immediate access of the stack. You find this pretty frequently in frameworks that use shellcode such as Metasploit, it’s basically getting the address of the data immediately following the call instruction. What are we doing with this data? Further down we find a loop using XOR while loading the first DWORD of the data as the XOR key. ----- Doing this statically in python: ``` Python>data = GetManyBytes(0x43f007, 0x100) Python>key = data[:4] Python>data = data[4:] Python>key = bytearray(key) Python>data = bytearray(data) Python>for i in range(len(data)): Python> data[i] ^= key[i%len(key)] Python> Python>data AZ� $9� R9� GetProcAddress � Python>data.split('\x00') [bytearray(b'\x12\xf1\x0e\x03AZ\xf1$9\x99\xaf\xfaR\x039\x98GetProcAddress'), bytearray(b'VirtualFree'), bytearray(b'UnmapViewOfFile'), bytearray(b'htonl'), bytearray(b'ntdll.dll'), bytearray(b'ws2_32.dll'), bytearray(b'ExitProcess'), bytearray(b'gethostbyname'), bytearray(b'LoadLibraryA'), bytearray(b'VirtualAlloc'), bytearray(b'User32.dll'), bytearray(b'RtlDecompressBuffer'), bytearray(b'WSAStartup'), bytearray(b'VirtualProtect'), bytearray(b'google-publicdns-b.google.com'), bytearray(b''), bytearray(b''), bytearray(b'\x8f'), bytearray(b',\xe1T\r\x08\x08\xfb\xfb\xf7\xf7\xfb\xfb\xf7\xf7\xfb\xfb\xf7\xf7\xfb\xfb\x ``` This is our string block along with the domain that will be resolved, the function names and domain immediately make me think this is more related to a protection or crypter layer as opposed to directly associated with the underlying malware Plurox. Assumptions however are just things that need to be proven. Continuing on with the code it will enumerate it’s own memory space looking for the start value ‘MZ’. ----- After finding that address it begins utilizing the bytes at the beginning of the decoded string block, turns out these values are headers used to identify two blocks of data that will be saved off. After saving off those chunks of data it will perform the resolution of the domain from the decoded strings. ----- It will then use that resolved IP as the DWORD XOR key for the smaller layer that was previously saved off. This layer is designed to rebuild the larger blob that was previously saved off and then XOR decoded and LZNT decompressed if needed. Unfortunately the values used to rebuild are not static, you will need the number of bytes to copy over and the number of bytes to skip. Pulling out these values is possible through a number of ways but probably the easiest is to simply regex them out of the decoded layer. ----- So for creating an unpacker we need to do the following steps: ``` 1. Load the PE file in memory mapped form 2. Get the OEP (entry point) 3. Find the encoded blob of strings 4. Find the two data blobs using the byte chunks from the decoded strings 5. Find the XOR key 6. Decode the second layer code 7. Get the value for bytes copied and bytes skipped 8. Rebuild the encoded payload using values from 7 9. Decode rebuilt payload 10. Check if compressed 11. Write to disk ``` Some of the pieces from above will be quickly glossed over because they are self explanatory, if these pieces are more technically advanced than you are ready for you can skip them by all means and just read the comments that way you can hopefully understand my thought process while I constructed the code which could be beneficial while learning. For loading the PE file into memory and getting the OEP we will use pefile in python. ``` if __name__ == "__main__": fdata = open(sys.argv[1], 'rb').read() pe = pefile.PE(data=fdata) oep = pe.OPTIONAL_HEADER.AddressOfEntryPoint found = False memmapped = pe.get_memory_mapped_image() ``` At this point we want to try to limit our scope, the encoded blob of strings appears to normally be near the OEP so we can probably try to brute it out within a limited scope of bytes. ----- ``` data memmapped[oep:oep+0x1000] for i in range(len(data)-24): test_k = bytearray(data[i:i+4]) test_v = bytearray(data[i+4:i+0x100]) for j in range(len(test_v)): test_v[j] ^= test_k[j%len(test_k)] if 'Alloc' in test_v or 'Process' in test_v or 'Decompress' in test_v: print("Found it") found = True break ``` So we are looping through the first 0x1000 bytes after the OEP to try to find encoded string bytes. After finding the start we just need to XOR decode the chunk which is semi redundant because we just did it for our testing loop and then split up the strings. Technically we only need the first 16 bytes but I figured it’d be good to add in code that can quickly parse and dump the strings to look for the different domains being used. ``` if found == True: blob = bytearray(data[i+4:i+0x100]) key = bytearray(data[i:i+4]) for i in range(len(blob)): blob[i] ^= key[i%len(key)] if '\x00\x00\x00' in blob: conf = str(blob.split('\x00\x00\x00')[0]).split('\x00') else: conf = str(blob).split('\x00')[:-1] ``` Next we will pull out the two blobs using the first 16 bytes from the decoded strings ``` off1 = fdata.find(conf[0][:8]) l = fdata[off1+8:].find(conf[0][:8]) blob1 = fdata[off1+8:off1+l+8] off2 = fdata.find(conf[0][8:16]) l = fdata[off2+8:].find(conf[0][8:16]) blob2 = fdata[off2+8:off2+l+8] ``` Now we’re going to do something a little interesting, this is why I enjoy writing scripts like this also. After gathering enough samples and dumping enough layer2s we can start to see byte patterns emerge, now we could just resolve the domain and boom we have our XOR key but that’s no fun. So if you go back up to the comparison picture you’ll notice some overlap of bytes in the decoded layer2 but more importantly I noticed the 4 bytes after the first 3 bytes remain pretty static across many samples tested. ``` known_val = bytearray('\x01\xc0\x6a\x04') ``` So to get the XOR key we just need to XOR the known value with the encoded bytes in the same place and then fixup the key position because we went 3 bytes in instead of 4. ----- ``` key bytearray(blob1[3:7]) for i in range(len(key)): key[i] ^= known_val[i] key = key[1:]+key[:1] temp = bytearray(blob1) for i in range(len(temp)): temp[i] ^= key[i%len(key)] ``` Now we have our layer2 decoded code that we can regex out the values we need. ``` matches = re.findall('''\xb9.\x00\x00\x00\xf3\xa4\x83\xc6.\x83\xe8''', temp) if len(matches) > 0: (dc, chunk_length, dc, dc, dc, addval) = struct.unpack_from('