├── LICENSE ├── PlugX ├── PlugX_GULP │ ├── plugx_gulp.py │ ├── plugx_gulp_cfg_dec.py │ ├── plugx_versions.txt │ └── x64dbg_extract_payload.py ├── PlugX_THOR │ ├── plugx_thor.py │ ├── plugx_thor_cfg_dec.py │ └── x64dbg_extract_payload.py └── PlugX_XV │ ├── lznt1dec.py │ ├── plugx_xv.py │ ├── plugx_xv_cfg_dec.py │ ├── plugx_xv_cfg_parse.py │ ├── plugx_xv_keylog_dec.py │ ├── plugx_xv_versions.txt │ └── x64dbg_extract_payload.py ├── PoisonIvy └── parse_cfg.py ├── RCSession ├── RCSession.yar ├── rcsession_cfg_decrypt.py └── rcsession_keylog_dec.py └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Andrey Zhdanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PlugX/PlugX_GULP/plugx_gulp.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | def decrypt(data): 5 | dec_data = b'' 6 | data_len = len(data) 7 | n1, = struct.unpack_from('> 3) - 0x11111111) & 0xFFFFFFFF 13 | n2 = (n2 + (n2 >> 5) - 0x22222222) & 0xFFFFFFFF 14 | n3 = (n3 + (0x33333333 - (n3 << 7))) & 0xFFFFFFFF 15 | n4 = (n4 + (0x44444444 - (n4 << 9))) & 0xFFFFFFFF 16 | b = (data[i] ^ (n2 + n1 + n3 + n4)) & 0xFF 17 | dec_data += bytes([b]) 18 | return dec_data 19 | 20 | 21 | if __name__ == '__main__': 22 | import sys 23 | import io 24 | 25 | if len(sys.argv) != 2: 26 | print('Usage: '+ sys.argv[0] + ' filename') 27 | sys.exit(0) 28 | 29 | file_name = sys.argv[1] 30 | with io.open(file_name, 'rb') as f: 31 | data = f.read() 32 | 33 | dec_data = decrypt(data) 34 | 35 | new_file_name = file_name + '.dec' 36 | with io.open(new_file_name, 'wb') as f: 37 | f.write(dec_data) 38 | 39 | print('Done!') 40 | -------------------------------------------------------------------------------- /PlugX/PlugX_GULP/plugx_gulp_cfg_dec.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import io 3 | import plugx_gulp 4 | 5 | 6 | if len(sys.argv) != 2: 7 | print('Usage: '+ sys.argv[0] + ' filename') 8 | sys.exit(0) 9 | 10 | file_name = sys.argv[1] 11 | 12 | with io.open(file_name, 'rb') as f: 13 | data = f.read() 14 | 15 | dec_data = plugx_gulp.decrypt(data) 16 | 17 | new_file_name = file_name + '.dec' 18 | with io.open(new_file_name, 'wb') as f: 19 | f.write(dec_data) 20 | 21 | print('Done!') 22 | -------------------------------------------------------------------------------- /PlugX/PlugX_GULP/plugx_versions.txt: -------------------------------------------------------------------------------- 1 | GULP 2 | ---- 3 | SHFolder.dll/SHFolder.dll.shf 4 | MAPI32.dll/OutlookBar.Doc 5 | NvSmartMax.dll/boot.ldr 6 | NSLS.dll/msimain17.sdb 7 | McUtil.dll/McUtil.dll.url 8 | WINMM.dll/sep_NE.slf 9 | PNIPCN.dll/PipSEC 10 | ?/qbcore.dll.url 11 | NvSmartMax.dll/HRB.bmp 12 | HID.dll/HID.dllx 13 | 14 | XXXX 15 | ---- 16 | McUtil.dll/McUtil.dll.url 17 | 18 | IP 19 | -- 20 | NvSmartMax.dll/boot.ldr 21 | 22 | msvsct.exe/msvsct.ini 23 | -------------------------------------------------------------------------------- /PlugX/PlugX_GULP/x64dbg_extract_payload.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | from x64dbgpy.pluginsdk import * 4 | 5 | 6 | def x64dbg_save_data(dest_dir, file_name, addr, size): 7 | """Save data to file""" 8 | 9 | # Read data 10 | read_bytes = bytearray(size) 11 | result, read_size = x64dbg.Memory_Read(addr, read_bytes, size) 12 | 13 | # Save data to file 14 | with io.open(os.path.join(dest_dir, file_name), 'wb') as f: 15 | f.write(read_bytes[:read_size]) 16 | 17 | 18 | def x64dbg_run_to_addr(addr): 19 | """Run to specifed address""" 20 | 21 | x64dbg.SetHardwareBreakpoint(addr, x64dbg.HardwareExecute) 22 | x64dbg.Run() 23 | x64dbg.DeleteHardwareBreakpoint(addr) 24 | 25 | 26 | def x64dbg_get_ep_addr(mod_addr): 27 | """Get PE module entry point address""" 28 | 29 | nt_hdr_addr = mod_addr + x64dbg.ReadDword(mod_addr + 0x3C) 30 | 31 | num_sections = x64dbg.ReadWord(nt_hdr_addr + 6) 32 | ep_rva = x64dbg.ReadDword(nt_hdr_addr + 0x28) 33 | opt_hdr_size = x64dbg.ReadWord(nt_hdr_addr + 0x14) 34 | nt_hdr_size = 4 + 0x14 + opt_hdr_size 35 | section_hdr_addr = nt_hdr_addr + nt_hdr_size 36 | 37 | for i in range(num_sections): 38 | 39 | s_vsize = x64dbg.ReadWord(section_hdr_addr + 8) 40 | s_rva = x64dbg.ReadWord(section_hdr_addr + 12) 41 | s_psize = x64dbg.ReadWord(section_hdr_addr + 16) 42 | s_pos = x64dbg.ReadWord(section_hdr_addr + 20) 43 | 44 | if (s_pos != 0) and (ep_rva >= s_rva): 45 | offset = ep_rva - s_rva 46 | if (offset < min(s_vsize, s_psize)): 47 | return (mod_addr + s_pos + offset) 48 | 49 | section_hdr_addr += 0x28 50 | 51 | return 0 52 | 53 | 54 | # 55 | # Main 56 | # 57 | 58 | dest_dir = os.path.abspath(os.path.dirname(__file__)) 59 | 60 | x64dbg.Run() 61 | 62 | # Break on RtlDecompressBuffer 63 | addr = ResolveLabel('RtlDecompressBuffer') 64 | if addr == 0: 65 | raise Exception('Failed to resolve RtlDecompressBuffer.') 66 | x64dbg_run_to_addr(addr) 67 | 68 | addr = x64dbg.GetESP() 69 | 70 | # Check COMPRESSION_FORMAT_LZNT1 71 | if x64dbg.ReadDword(addr + 4) != 2: 72 | raise Exception('Invalid compression format.') 73 | 74 | # Save payload 75 | payload_addr = x64dbg.ReadPtr(addr + 8) 76 | payload_size = x64dbg.ReadPtr(addr + 12) 77 | print('Payload address: %08X' % payload_addr) 78 | print('Payload size: %d' % payload_size) 79 | x64dbg.StepOut() 80 | x64dbg_save_data(dest_dir, 'payload.dll_', payload_addr, payload_size) 81 | 82 | # Get payload entry point address 83 | ep_addr = x64dbg_get_ep_addr(payload_addr) 84 | if ep_addr == 0: 85 | raise Exception('Couldn\'t get payload entry point address.') 86 | print('Payload entry point address: %08X' % ep_addr) 87 | 88 | # Write int 3 to payload entry point 89 | x64dbg.WriteByte(ep_addr, 0xCC) 90 | 91 | x64dbg.Run() 92 | 93 | addr = x64dbg.GetESP() 94 | payload_base = x64dbg.ReadPtr(addr + 4) 95 | print('Payload base address: %08X' % payload_base) 96 | 97 | # PlugX payload signature 98 | plugx_sign = x64dbg.ReadDword(payload_base) 99 | print('Payload signature: %08X' % plugx_sign) 100 | 101 | # Shellcode 102 | shellcode_addr = x64dbg.ReadPtr(payload_base + 4) 103 | shellcode_size = x64dbg.ReadPtr(payload_base + 8) 104 | print('Shellcode address: %08X' % shellcode_addr) 105 | print('Shellcode size: %d' % shellcode_size) 106 | x64dbg_save_data(dest_dir, 'shellcode.bin', shellcode_addr, shellcode_size) 107 | 108 | # Packed payload 109 | packed_payload_addr = x64dbg.ReadPtr(payload_base + 0xC) 110 | packed_payload_size = x64dbg.ReadPtr(payload_base + 0x10) 111 | print('Packed payload address: %08X' % packed_payload_addr) 112 | print('Packed payload size: %d' % packed_payload_size) 113 | x64dbg_save_data(dest_dir, 'payload.pak', 114 | packed_payload_addr, packed_payload_size) 115 | 116 | # Configuration data 117 | cfg_addr = x64dbg.ReadPtr(payload_base + 0x14) 118 | cfg_size = x64dbg.ReadPtr(payload_base + 0x18) 119 | print('Config address: %08X' % cfg_addr) 120 | print('Config size: %d' % cfg_size) 121 | x64dbg_save_data(dest_dir, 'config.enc', cfg_addr, cfg_size) 122 | -------------------------------------------------------------------------------- /PlugX/PlugX_THOR/plugx_thor.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | def decrypt1(data): 5 | 6 | dec_data = b'' 7 | 8 | n1, = struct.unpack_from('> 3) - 0x56565656) & 0xFFFFFFFF 15 | n2 = (n2 + (n2 >> 5) - 0x36363636) & 0xFFFFFFFF 16 | n3 = (n3 - (n3 << 7) + 0x57575757) & 0xFFFFFFFF 17 | n4 = (n4 - (n4 << 9) - 0x76767677) & 0xFFFFFFFF 18 | b = (data[i] ^ (n1 + n2 + n3 + n4)) & 0xFF 19 | dec_data += bytes([b]) 20 | 21 | return dec_data 22 | 23 | 24 | def decrypt2(data, start_pos=16): 25 | 26 | dec_data = b'' 27 | 28 | n1, = struct.unpack_from('> 3) - 0x66666666) & 0xFFFFFFFF 35 | n2 = (n2 + (n2 >> 5) + 0x76767677) & 0xFFFFFFFF 36 | n3 = (n3 - (n3 << 7) + 0x67676767) & 0xFFFFFFFF 37 | n4 = (n4 - (n4 << 9) - 0x66666667) & 0xFFFFFFFF 38 | b = (data[i] ^ (n1 + n2 + n3 + n4)) & 0xFF 39 | dec_data += bytes([b]) 40 | 41 | return dec_data 42 | 43 | 44 | if __name__ == '__main__': 45 | import sys 46 | import io 47 | 48 | if len(sys.argv) != 2: 49 | print('Usage: '+ sys.argv[0] + ' filename') 50 | sys.exit(0) 51 | 52 | file_name = sys.argv[1] 53 | with io.open(file_name, 'rb') as f: 54 | data = f.read() 55 | 56 | dec_data = decrypt(data) 57 | 58 | new_file_name = file_name + '.dec' 59 | with io.open(new_file_name, 'wb') as f: 60 | f.write(dec_data) 61 | 62 | print('Done!') 63 | -------------------------------------------------------------------------------- /PlugX/PlugX_THOR/plugx_thor_cfg_dec.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import io 3 | import plugx_thor 4 | 5 | 6 | if len(sys.argv) != 2: 7 | print('Usage: '+ sys.argv[0] + ' filename') 8 | sys.exit(0) 9 | 10 | file_name = sys.argv[1] 11 | 12 | with io.open(file_name, 'rb') as f: 13 | data = f.read() 14 | 15 | dec_data = plugx_thor.decrypt1(data) 16 | 17 | new_file_name = file_name + '.dec' 18 | with io.open(new_file_name, 'wb') as f: 19 | f.write(dec_data) 20 | 21 | print('Done!') 22 | -------------------------------------------------------------------------------- /PlugX/PlugX_THOR/x64dbg_extract_payload.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | from x64dbgpy.pluginsdk import * 4 | 5 | 6 | def x64dbg_save_data(dest_dir, file_name, addr, size): 7 | """Save data to file""" 8 | 9 | # Read data 10 | read_bytes = bytearray(size) 11 | result, read_size = x64dbg.Memory_Read(addr, read_bytes, size) 12 | 13 | # Save data to file 14 | with io.open(os.path.join(dest_dir, file_name), 'wb') as f: 15 | f.write(read_bytes[:read_size]) 16 | 17 | 18 | def x64dbg_run_to_addr(addr): 19 | """Run to specifed address""" 20 | 21 | x64dbg.SetHardwareBreakpoint(addr, x64dbg.HardwareExecute) 22 | x64dbg.Run() 23 | x64dbg.DeleteHardwareBreakpoint(addr) 24 | 25 | 26 | def x64dbg_get_ep_addr(mod_addr): 27 | """Get PE module entry point address""" 28 | 29 | nt_hdr_addr = mod_addr + x64dbg.ReadDword(mod_addr + 0x3C) 30 | 31 | num_sections = x64dbg.ReadWord(nt_hdr_addr + 6) 32 | ep_rva = x64dbg.ReadDword(nt_hdr_addr + 0x28) 33 | opt_hdr_size = x64dbg.ReadWord(nt_hdr_addr + 0x14) 34 | nt_hdr_size = 4 + 0x14 + opt_hdr_size 35 | section_hdr_addr = nt_hdr_addr + nt_hdr_size 36 | 37 | for i in range(num_sections): 38 | 39 | s_vsize = x64dbg.ReadWord(section_hdr_addr + 8) 40 | s_rva = x64dbg.ReadWord(section_hdr_addr + 12) 41 | s_psize = x64dbg.ReadWord(section_hdr_addr + 16) 42 | s_pos = x64dbg.ReadWord(section_hdr_addr + 20) 43 | 44 | if (s_pos != 0) and (ep_rva >= s_rva): 45 | offset = ep_rva - s_rva 46 | if (offset < min(s_vsize, s_psize)): 47 | return (mod_addr + s_pos + offset) 48 | 49 | section_hdr_addr += 0x28 50 | 51 | return 0 52 | 53 | 54 | # 55 | # Main 56 | # 57 | 58 | dest_dir = os.path.abspath(os.path.dirname(__file__)) 59 | 60 | x64dbg.Run() 61 | 62 | # Break on RtlDecompressBuffer 63 | addr = ResolveLabel('RtlDecompressBuffer') 64 | if addr == 0: 65 | raise Exception('Failed to resolve RtlDecompressBuffer.') 66 | x64dbg_run_to_addr(addr) 67 | 68 | addr = x64dbg.GetESP() 69 | 70 | # Check COMPRESSION_FORMAT_LZNT1 71 | if x64dbg.ReadDword(addr + 4) != 2: 72 | raise Exception('Invalid compression format.') 73 | 74 | # Save payload 75 | payload_addr = x64dbg.ReadPtr(addr + 8) 76 | payload_size = x64dbg.ReadDword(addr + 12) 77 | print('Payload address: %08X' % payload_addr) 78 | print('Payload size: %d' % payload_size) 79 | x64dbg.StepOut() 80 | x64dbg_save_data(dest_dir, 'payload.dll_', payload_addr, payload_size) 81 | 82 | # Get payload entry point address 83 | ep_addr = x64dbg_get_ep_addr(payload_addr) 84 | if ep_addr == 0: 85 | raise Exception('Couldn\'t get payload entry point address.') 86 | print('Payload entry point address: %08X' % ep_addr) 87 | 88 | # Write int 3 to payload entry point 89 | x64dbg.WriteByte(ep_addr, 0xCC) 90 | 91 | x64dbg.Run() 92 | 93 | addr = x64dbg.GetESP() 94 | payload_base = x64dbg.ReadPtr(addr + 4) 95 | print('Payload base address: %08X' % payload_base) 96 | 97 | # PlugX payload signature 98 | plugx_sign = x64dbg.ReadDword(payload_base) 99 | print('Payload signature: %08X' % plugx_sign) 100 | 101 | # Shellcode 102 | shellcode_addr = x64dbg.ReadPtr(payload_base + 4) 103 | shellcode_size = x64dbg.ReadDword(payload_base + 8) 104 | print('Shellcode address: %08X' % shellcode_addr) 105 | print('Shellcode size: %d' % shellcode_size) 106 | x64dbg_save_data(dest_dir, 'shellcode.bin', shellcode_addr, shellcode_size) 107 | 108 | # Packed payload 109 | packed_payload_addr = x64dbg.ReadPtr(payload_base + 0xC) 110 | packed_payload_size = x64dbg.ReadDword(payload_base + 0x10) 111 | print('Packed payload address: %08X' % packed_payload_addr) 112 | print('Packed payload size: %d' % packed_payload_size) 113 | x64dbg_save_data(dest_dir, 'payload.pak', 114 | packed_payload_addr, packed_payload_size) 115 | 116 | # Configuration data 117 | cfg_addr = x64dbg.ReadPtr(payload_base + 0x14) 118 | cfg_size = x64dbg.ReadDword(payload_base + 0x18) 119 | print('Config address: %08X' % cfg_addr) 120 | print('Config size: %d' % cfg_size) 121 | x64dbg_save_data(dest_dir, 'config.enc', cfg_addr, cfg_size) 122 | -------------------------------------------------------------------------------- /PlugX/PlugX_XV/lznt1dec.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | def decompress(data): 5 | """LZNT1 decompression""" 6 | 7 | dec_data = bytearray() 8 | data_len = len(data) 9 | written = 0 10 | i = 0 11 | while (i < data_len): 12 | chunk_hdr, = struct.unpack_from('> 8) & 0x70) != 0x30): 14 | break 15 | chunk_len = (chunk_hdr & 0x0FFF) + 1 16 | i += 2 17 | if (((chunk_hdr >> 8) & 0x80) != 0): 18 | chunk_written = 0 19 | next_threshold = 16 20 | split = 12 21 | tag_bit = 0 22 | j = 0 23 | while (j < chunk_len): 24 | if (tag_bit == 0): 25 | tag = data[i + j] 26 | j += 1 27 | if ((tag & 1) != 0): 28 | while (chunk_written > next_threshold): 29 | split -= 1 30 | next_threshold <<= 1 31 | n, = struct.unpack_from('> split) + 1) 35 | for k in range(copy_len): 36 | dec_data.append(dec_data[copy_from + k]) 37 | chunk_written += 1 38 | written += 1 39 | else: 40 | dec_data.append(data[i + j]) 41 | j += 1 42 | chunk_written += 1 43 | written += 1 44 | tag >>= 1 45 | tag_bit = (tag_bit + 1) & 7 46 | else: 47 | for j in range(chunk_len): 48 | dec_data.append(data[i + j]) 49 | written += 1 50 | i += chunk_len 51 | return bytes(dec_data) 52 | 53 | 54 | if __name__ == '__main__': 55 | import sys 56 | import io 57 | 58 | if len(sys.argv) != 2: 59 | print('Usage: '+ sys.argv[0] + ' filename') 60 | sys.exit(0) 61 | 62 | file_name = sys.argv[1] 63 | with io.open(file_name, 'rb') as f: 64 | data = f.read() 65 | 66 | dec_data = decompress(data) 67 | 68 | new_file_name = file_name + '.dec' 69 | with io.open(new_file_name, 'wb') as f: 70 | f.write(dec_data) 71 | 72 | print('Done!') 73 | -------------------------------------------------------------------------------- /PlugX/PlugX_XV/plugx_xv.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import lznt1dec 3 | 4 | 5 | def decrypt1(data, n_add, start_pos): 6 | """PlugX XV decrypt data universal function (variant 1)""" 7 | 8 | dec_data = b'' 9 | 10 | n, = struct.unpack_from('> 3) + (i - start_pos) + n_add) & 0xFFFFFFFF 14 | b = (data[i] ^ (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ n) & 0xFF 15 | dec_data += bytes([b]) 16 | 17 | return dec_data 18 | 19 | 20 | convolute_dword = lambda n: (((n - (n >> 8)) ^ (n >> 16)) - (n >> 24)) & 0xFF 21 | 22 | 23 | def decrypt2(data, n1_xor, n2_xor, n1_add, n2_sub): 24 | """PlugX XV decrypt data universal function (variant 2)""" 25 | 26 | dec_data = b'' 27 | 28 | n, = struct.unpack_from(' 0): 168 | print_cfg_dword_val(data, pos, 'Process injection') 169 | pos += 4 170 | for i in range(parse_params.num_target_processes): 171 | print_cfg_str_val(data, pos, 'Target process[' + str(i) + ']') 172 | pos += STR_VALUE_SIZE 173 | if (parse_params.num_uacbypass_processes > 0): 174 | print_cfg_dword_val(data, pos, 'UAC Bypass process injection') 175 | pos += 4 176 | for i in range(parse_params.num_uacbypass_processes): 177 | print_cfg_str_val(data, pos, 'UAC Bypass target process[' + str(i) + ']') 178 | pos += STR_VALUE_SIZE 179 | 180 | print_cfg_str_val(data, pos, 'Actor Id') 181 | pos += STR_VALUE_SIZE 182 | print_cfg_str_val(data, pos, 'Target Id') 183 | pos += STR_VALUE_SIZE 184 | print_cfg_str_val(data, pos, 'Mutex name') 185 | pos += STR_VALUE_SIZE 186 | 187 | if parse_params.screenshot_present: 188 | print_cfg_dword_val(data, pos, 'Screenshots') 189 | pos += 4 190 | print_cfg_dword_val(data, pos, 'Screenshot frequency (secs)') 191 | pos += 4 192 | print_cfg_dword_val(data, pos, 'Screenshot zoom') 193 | pos += 4 194 | print_cfg_dword_val(data, pos, 'Screenshot color bits') 195 | pos += 4 196 | print_cfg_dword_val(data, pos, 'Screenshot quality') 197 | pos += 4 198 | print_cfg_dword_val(data, pos, 'Screenshot remain days') 199 | pos += 4 200 | print_cfg_str_val(data, pos, 'Sckreenshot save directory') 201 | pos += STR_VALUE_SIZE 202 | -------------------------------------------------------------------------------- /PlugX/PlugX_XV/plugx_xv_keylog_dec.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import io 3 | import struct 4 | import plugx_xv 5 | 6 | 7 | if len(sys.argv) != 3: 8 | print('Usage: '+ sys.argv[0] + ' version filename') 9 | exit(0) 10 | 11 | ver = int(sys.argv[1]) 12 | file_name = sys.argv[2] 13 | 14 | with io.open(file_name, 'rb') as f: 15 | data = f.read() 16 | 17 | dec_data = b'' 18 | i = 0 19 | while (i < len(data)): 20 | size, = struct.unpack_from('= s_rva): 45 | offset = ep_rva - s_rva 46 | if (offset < min(s_vsize, s_psize)): 47 | return (mod_addr + s_pos + offset) 48 | 49 | section_hdr_addr += 0x28 50 | 51 | return 0 52 | 53 | 54 | # 55 | # Main 56 | # 57 | 58 | dest_dir = os.path.abspath(os.path.dirname(__file__)) 59 | 60 | x64dbg.Run() 61 | 62 | # Break on RtlDecompressBuffer 63 | addr = ResolveLabel('RtlDecompressBuffer') 64 | if addr == 0: 65 | raise Exception('Failed to resolve RtlDecompressBuffer.') 66 | x64dbg_run_to_addr(addr) 67 | 68 | addr = x64dbg.GetESP() 69 | 70 | # Check COMPRESSION_FORMAT_LZNT1 71 | if x64dbg.ReadDword(addr + 4) != 2: 72 | raise Exception('Invalid compression format.') 73 | 74 | # Save payload 75 | payload_addr = x64dbg.ReadPtr(addr + 8) 76 | payload_size = x64dbg.ReadPtr(addr + 12) 77 | print('Payload address: %08X' % payload_addr) 78 | print('Payload size: %d' % payload_size) 79 | x64dbg.StepOut() 80 | x64dbg_save_data(dest_dir, 'payload.dll_', payload_addr, payload_size) 81 | 82 | # Get payload entry point address 83 | ep_addr = x64dbg_get_ep_addr(payload_addr) 84 | if ep_addr == 0: 85 | raise Exception('Couldn\'t get payload entry point address.') 86 | print('Payload entry point address: %08X' % ep_addr) 87 | 88 | # Write int 3 to payload entry point 89 | x64dbg.WriteByte(ep_addr, 0xCC) 90 | 91 | x64dbg.Run() 92 | 93 | addr = x64dbg.GetESP() 94 | payload_base = x64dbg.ReadPtr(addr + 4) 95 | print('Payload base address: %08X' % payload_base) 96 | 97 | # PlugX payload signature 98 | plugx_sign = x64dbg.ReadDword(payload_base) 99 | print('Payload signature: %08X' % plugx_sign) 100 | 101 | # Payload params 102 | payload_param_addr = x64dbg.ReadPtr(addr + 12) 103 | print('Payload param address: %08X' % payload_param_addr) 104 | 105 | # Shellcode 106 | shellcode_addr = x64dbg.ReadPtr(payload_param_addr) 107 | shellcode_size = x64dbg.ReadPtr(payload_param_addr + 4) 108 | print('Shellcode address: %08X' % shellcode_addr) 109 | print('Shellcode size: %d' % shellcode_size) 110 | x64dbg_save_data(dest_dir, 'shellcode.bin', shellcode_addr, shellcode_size) 111 | 112 | # Packed payload 113 | packed_payload_addr = x64dbg.ReadPtr(payload_param_addr + 8) 114 | packed_payload_size = x64dbg.ReadPtr(payload_param_addr + 0xC) 115 | print('Packed payload address: %08X' % packed_payload_addr) 116 | print('Packed payload size: %d' % packed_payload_size) 117 | x64dbg_save_data(dest_dir, 'payload.pak', 118 | packed_payload_addr, packed_payload_size) 119 | 120 | # Configuration data 121 | cfg_addr = x64dbg.ReadPtr(payload_param_addr + 0x10) 122 | cfg_size = x64dbg.ReadPtr(payload_param_addr + 0x14) 123 | print('Config address: %08X' % cfg_addr) 124 | print('Config size: %d' % cfg_size) 125 | x64dbg_save_data(dest_dir, 'config.enc', cfg_addr, cfg_size) 126 | -------------------------------------------------------------------------------- /PoisonIvy/parse_cfg.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import io 3 | import struct 4 | 5 | 6 | CFG_FIELD_UNKNOWN = 0 7 | CFG_FIELD_STRING = 1 8 | CFG_FIELD_NUMBER = 2 9 | CFG_FIELD_CONN_INFO = 3 10 | 11 | CFG_FIELD_TYPE_LIST = { 12 | 0x0145: ('Encryption key', CFG_FIELD_STRING), 13 | 0x018C: ('C&C max index', CFG_FIELD_NUMBER), 14 | 0x0190: ('C&C', CFG_FIELD_CONN_INFO), 15 | 0x02C1: ('Proxy max index', CFG_FIELD_NUMBER), 16 | 0x02C5: ('Proxy', CFG_FIELD_CONN_INFO), 17 | 0x03FB: ('Mutex name', CFG_FIELD_STRING), 18 | 0x040F: ('Active Setup reg value name', CFG_FIELD_STRING), 19 | 0x0418: ('Default browser path reg key', CFG_FIELD_STRING), 20 | 0x0456: ('Active Setup reg key', CFG_FIELD_STRING), 21 | 0x0AFA: ('Campaign Id', CFG_FIELD_STRING), 22 | 0x0BF9: ('Group Id', CFG_FIELD_STRING), 23 | 0x0D09: ('HKLM/HKCU autorun reg key flag', CFG_FIELD_NUMBER), 24 | 0x0E12: ('Autorun reg value name', CFG_FIELD_STRING), 25 | 0xEB00: ('Unknown_EB00', CFG_FIELD_STRING), 26 | 0xEC00: ('Unknown_EC00', CFG_FIELD_STRING), 27 | 0xED00: ('Unknown_ED00', CFG_FIELD_STRING), 28 | 0xEF7C: ('Unknown_EF7C', CFG_FIELD_NUMBER), 29 | 0xEF8C: ('Unknown_EF8C', CFG_FIELD_NUMBER), 30 | 0xEF90: ('Unknown_EF90', CFG_FIELD_NUMBER), 31 | 0xEF94: ('Unknown_EF94', CFG_FIELD_NUMBER), 32 | 0xEFF8: ('Unknown_EFF8', CFG_FIELD_NUMBER), 33 | 0xEFFC: ('Unknown_EFFC', CFG_FIELD_NUMBER) 34 | } 35 | 36 | 37 | GET_CFG_DATA_CODE = b'\xE8\0\0\0\0' 38 | 39 | 40 | def extract_cfg_data(data): 41 | pos = 0 42 | while True: 43 | # call $+5 44 | pos = data.find(GET_CFG_DATA_CODE, pos) 45 | if (pos < 0): 46 | break 47 | pos += len(GET_CFG_DATA_CODE) 48 | # pop esi 49 | if ((data[pos] & 0xF0) != 0x50): 50 | continue 51 | # add esi, cfg_rel_offset 52 | if ((data[pos + 1] == 0x81) and ((data[pos + 2] & 0xF0) == 0xC0)): 53 | cfg_rel_ofs, = struct.unpack_from('> 4) 19 | elif (m == 1): 20 | n -= 2 * n 21 | elif (m == 2): 22 | n -= (n >> 2) 23 | else: 24 | n *= 9 25 | n &= 0xFFFFFFFF 26 | dec_data += bytes([b ^ (n & 0xFF)]) 27 | 28 | return dec_data 29 | 30 | 31 | if len(sys.argv) != 2: 32 | print('Usage: '+ sys.argv[0] + ' filename') 33 | exit(0) 34 | 35 | filename = sys.argv[1] 36 | 37 | with io.open(filename, 'rb') as f: 38 | data = f.read() 39 | 40 | seed, = struct.unpack_from("