mirror of
https://github.com/monoxgas/sRDI
synced 2026-06-06 16:14:36 +00:00
Initial Code
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
import sys, pefile
|
||||
from struct import pack
|
||||
|
||||
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
|
||||
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
|
||||
|
||||
def is64BitDLL(bytes):
|
||||
pe = pefile.PE(data=bytes, fast_load=True)
|
||||
return (pe.OPTIONAL_HEADER.Magic == 0x20b)
|
||||
|
||||
ror = lambda val, r_bits, max_bits: \
|
||||
((val & (2**max_bits-1)) >> r_bits%max_bits) | \
|
||||
(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
|
||||
|
||||
def HashFunctionName(name, module = None):
|
||||
|
||||
function = name.encode() + b'\x00'
|
||||
|
||||
if(module):
|
||||
module = module.upper().encode('UTF-16LE') + b'\x00\x00'
|
||||
|
||||
functionHash = 0
|
||||
|
||||
for b in function:
|
||||
functionHash = ror(functionHash, 13, 32)
|
||||
functionHash += b
|
||||
|
||||
moduleHash = 0
|
||||
|
||||
for b in module:
|
||||
moduleHash = ror(moduleHash, 13, 32)
|
||||
moduleHash += b
|
||||
|
||||
functionHash += moduleHash
|
||||
|
||||
if functionHash > 0xFFFFFFFF: functionHash -= 0x100000000
|
||||
|
||||
else:
|
||||
functionHash = 0
|
||||
|
||||
for b in function:
|
||||
functionHash = ror(functionHash, 13, 32)
|
||||
functionHash += b
|
||||
|
||||
return functionHash
|
||||
|
||||
def ConvertToShellcode(dllBytes, functionHash=0x10, userData=b'None'):
|
||||
|
||||
rdiShellcode64 = b"\xe9\x1b\x04\x00\x00\xcc\xcc\xcc\x48\x89\x5c\x24\x08\x48\x89\x74\x24\x10\x57\x48\x83\xec\x10\x65\x48\x8b\x04\x25\x60\x00\x00\x00\x8b\xf1\x48\x8b\x50\x18\x4c\x8b\x4a\x10\x4d\x8b\x41\x30\x4d\x85\xc0\x0f\x84\xb4\x00\x00\x00\x41\x0f\x10\x41\x58\x49\x63\x40\x3c\x33\xd2\x4d\x8b\x09\xf3\x0f\x7f\x04\x24\x42\x8b\x9c\x00\x88\x00\x00\x00\x85\xdb\x74\xd4\x48\x8b\x04\x24\x48\xc1\xe8\x10\x44\x0f\xb7\xd0\x45\x85\xd2\x74\x21\x48\x8b\x4c\x24\x08\x45\x8b\xda\x0f\xbe\x01\xc1\xca\x0d\x80\x39\x61\x7c\x03\x83\xc2\xe0\x03\xd0\x48\xff\xc1\x49\x83\xeb\x01\x75\xe7\x4d\x8d\x14\x18\x33\xc9\x41\x8b\x7a\x20\x49\x03\xf8\x41\x39\x4a\x18\x76\x8f\x8b\x1f\x45\x33\xdb\x49\x03\xd8\x48\x8d\x7f\x04\x0f\xbe\x03\x48\xff\xc3\x41\xc1\xcb\x0d\x44\x03\xd8\x80\x7b\xff\x00\x75\xed\x41\x8d\x04\x13\x3b\xc6\x74\x0d\xff\xc1\x41\x3b\x4a\x18\x72\xd1\xe9\x5b\xff\xff\xff\x41\x8b\x42\x24\x03\xc9\x49\x03\xc0\x0f\xb7\x14\x01\x41\x8b\x4a\x1c\x49\x03\xc8\x8b\x04\x91\x49\x03\xc0\xeb\x02\x33\xc0\x48\x8b\x5c\x24\x20\x48\x8b\x74\x24\x28\x48\x83\xc4\x10\x5f\xc3\xcc\xcc\xcc\x44\x89\x4c\x24\x20\x4c\x89\x44\x24\x18\x89\x54\x24\x10\x53\x55\x56\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x83\xec\x38\x48\x8b\xe9\x45\x8b\xe1\xb9\x4c\x77\x26\x07\x44\x8b\xf2\xe8\xd7\xfe\xff\xff\xb9\x49\xf7\x02\x78\x4c\x8b\xe8\xe8\xca\xfe\xff\xff\xb9\x58\xa4\x53\xe5\x48\x89\x84\x24\x80\x00\x00\x00\xe8\xb8\xfe\xff\xff\xb9\xaf\xb1\x5c\x94\x48\x8b\xd8\xe8\xab\xfe\xff\xff\x48\x63\x75\x3c\x33\xc9\x48\x03\xf5\x48\x89\x44\x24\x20\x41\xb8\x00\x30\x00\x00\x4c\x8b\xf8\x44\x8d\x49\x40\x8b\x56\x50\xff\xd3\x44\x8b\x46\x54\x48\x8b\xf8\x48\x8b\xcd\x41\xbb\x01\x00\x00\x00\x4d\x85\xc0\x74\x13\x48\x8b\xd0\x48\x2b\xd5\x8a\x01\x88\x04\x0a\x49\x03\xcb\x4d\x2b\xc3\x75\xf3\x44\x0f\xb7\x4e\x06\x0f\xb7\x46\x14\x4d\x85\xc9\x74\x38\x48\x8d\x4e\x2c\x48\x03\xc8\x8b\x51\xf8\x4d\x2b\xcb\x44\x8b\x01\x48\x03\xd7\x44\x8b\x51\xfc\x4c\x03\xc5\x4d\x85\xd2\x74\x10\x41\x8a\x00\x4d\x03\xc3\x88\x02\x49\x03\xd3\x4d\x2b\xd3\x75\xf0\x48\x83\xc1\x28\x4d\x85\xc9\x75\xcf\x8b\x9e\x90\x00\x00\x00\x48\x03\xdf\x8b\x43\x0c\x85\xc0\x0f\x84\x91\x00\x00\x00\x48\x8b\xac\x24\x80\x00\x00\x00\x8b\xc8\x48\x03\xcf\x41\xff\xd5\x44\x8b\x3b\x4c\x8b\xe0\x44\x8b\x73\x10\x4c\x03\xff\x4c\x03\xf7\xeb\x49\x49\x83\x3f\x00\x7d\x29\x49\x63\x44\x24\x3c\x41\x0f\xb7\x17\x42\x8b\x8c\x20\x88\x00\x00\x00\x42\x8b\x44\x21\x10\x42\x8b\x4c\x21\x1c\x48\x2b\xd0\x49\x03\xcc\x8b\x04\x91\x49\x03\xc4\xeb\x0f\x49\x8b\x16\x49\x8b\xcc\x48\x83\xc2\x02\x48\x03\xd7\xff\xd5\x49\x89\x06\x49\x83\xc6\x08\x49\x83\xc7\x08\x49\x83\x3e\x00\x75\xb1\x8b\x43\x20\x48\x83\xc3\x14\x85\xc0\x75\x8c\x44\x8b\xb4\x24\x88\x00\x00\x00\x4c\x8b\x7c\x24\x20\x44\x8b\xa4\x24\x98\x00\x00\x00\x4c\x8b\xd7\x41\xbd\x02\x00\x00\x00\x4c\x2b\x56\x30\x83\xbe\xb4\x00\x00\x00\x00\x41\x8d\x6d\xff\x0f\x84\x97\x00\x00\x00\x44\x8b\x86\xb0\x00\x00\x00\x4c\x03\xc7\x41\x8b\x40\x04\x85\xc0\x0f\x84\x81\x00\x00\x00\xbb\xff\x0f\x00\x00\x41\x8b\x10\x4d\x8d\x58\x08\x44\x8b\xc8\x48\x03\xd7\x49\x83\xe9\x08\x49\xd1\xe9\x74\x57\x41\x0f\xb7\x0b\x4c\x2b\xcd\x0f\xb7\xc1\x66\xc1\xe8\x0c\x66\x83\xf8\x0a\x75\x09\x48\x23\xcb\x4c\x01\x14\x11\xeb\x32\x66\x83\xf8\x03\x75\x09\x48\x23\xcb\x44\x01\x14\x11\xeb\x23\x66\x3b\xc5\x75\x10\x48\x23\xcb\x49\x8b\xc2\x48\xc1\xe8\x10\x66\x01\x04\x11\xeb\x0e\x66\x41\x3b\xc5\x75\x08\x48\x23\xcb\x66\x44\x01\x14\x11\x4d\x03\xdd\x4d\x85\xc9\x75\xa9\x41\x8b\x40\x04\x4c\x03\xc0\x41\x8b\x40\x04\x85\xc0\x75\x84\x8b\x5e\x28\x45\x33\xc0\x33\xd2\x48\x83\xc9\xff\x48\x03\xdf\x41\xff\xd7\x4c\x8b\xc5\x8b\xd5\x48\x8b\xcf\xff\xd3\x45\x85\xf6\x0f\x84\x93\x00\x00\x00\x83\xbe\x8c\x00\x00\x00\x00\x0f\x84\x86\x00\x00\x00\x8b\x96\x88\x00\x00\x00\x48\x03\xd7\x44\x8b\x5a\x18\x45\x85\xdb\x74\x74\x83\x7a\x14\x00\x74\x6e\x44\x8b\x52\x20\x33\xdb\x44\x8b\x4a\x24\x4c\x03\xd7\x4c\x03\xcf\x45\x85\xdb\x74\x59\x45\x8b\x02\x4c\x03\xc7\x33\xc9\x41\x0f\xbe\x00\x4c\x03\xc5\xc1\xc9\x0d\x03\xc8\x41\x80\x78\xff\x00\x75\xed\x44\x3b\xf1\x74\x10\x03\xdd\x49\x83\xc2\x04\x4d\x03\xcd\x41\x3b\xdb\x72\xd2\xeb\x29\x41\x0f\xb7\x01\x83\xf8\xff\x74\x20\x8b\x52\x1c\x48\x8b\x8c\x24\x90\x00\x00\x00\xc1\xe0\x02\x48\x98\x48\x03\xc7\x44\x8b\x04\x02\x41\x8b\xd4\x4c\x03\xc7\x41\xff\xd0\x48\x8b\xc7\x48\x83\xc4\x38\x41\x5f\x41\x5e\x41\x5d\x41\x5c\x5f\x5e\x5d\x5b\xc3\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x56\x48\x8b\xf4\x48\x83\xe4\xf0\x48\x83\xec\x20\xe8\xcf\xfc\xff\xff\x48\x8b\xe6\x5e\xc3";
|
||||
rdiShellcode32 = b"\x83\xec\x18\x53\x55\x56\x57\xb9\x4c\x77\x26\x07\xe8\x9d\x02\x00\x00\xb9\x49\xf7\x02\x78\x89\x44\x24\x18\xe8\x8f\x02\x00\x00\xb9\x58\xa4\x53\xe5\x89\x44\x24\x1c\xe8\x81\x02\x00\x00\xb9\xaf\xb1\x5c\x94\x8b\xf0\xe8\x75\x02\x00\x00\x8b\x6c\x24\x2c\x6a\x40\x68\x00\x30\x00\x00\x89\x44\x24\x2c\x8b\x5d\x3c\x03\xdd\x89\x5c\x24\x18\xff\x73\x50\x6a\x00\xff\xd6\x8b\x73\x54\x8b\xf8\x89\x7c\x24\x20\x8b\xd5\x85\xf6\x74\x0f\x8b\xcf\x2b\xcd\x8a\x02\x88\x04\x11\x42\x83\xee\x01\x75\xf5\x0f\xb7\x6b\x06\x0f\xb7\x43\x14\x85\xed\x74\x34\x8d\x4b\x2c\x03\xc8\x8b\x44\x24\x2c\x8b\x51\xf8\x4d\x8b\x31\x03\xd7\x8b\x59\xfc\x03\xf0\x85\xdb\x74\x0f\x8a\x06\x88\x02\x42\x46\x83\xeb\x01\x75\xf5\x8b\x44\x24\x2c\x83\xc1\x28\x85\xed\x75\xd9\x8b\x5c\x24\x10\x8b\xb3\x80\x00\x00\x00\x03\xf7\x89\x74\x24\x14\x8b\x46\x0c\x85\xc0\x74\x7d\x03\xc7\x50\xff\x54\x24\x1c\x8b\x6e\x10\x8b\xd8\x8b\x06\x03\xef\x03\xc7\x89\x44\x24\x2c\x83\x7d\x00\x00\x74\x4f\x8b\x74\x24\x1c\x8b\x08\x85\xc9\x74\x1e\x79\x1c\x8b\x43\x3c\x0f\xb7\xc9\x8b\x44\x18\x78\x2b\x4c\x18\x10\x8b\x44\x18\x1c\x8d\x04\x88\x8b\x04\x18\x03\xc3\xeb\x0c\x8b\x45\x00\x83\xc0\x02\x03\xc7\x50\x53\xff\xd6\x89\x45\x00\x83\xc5\x04\x8b\x44\x24\x2c\x83\xc0\x04\x89\x44\x24\x2c\x83\x7d\x00\x00\x75\xb9\x8b\x74\x24\x14\x8b\x46\x20\x83\xc6\x14\x89\x74\x24\x14\x85\xc0\x75\x87\x8b\x5c\x24\x10\x8b\xef\xc7\x44\x24\x18\x01\x00\x00\x00\x2b\x6b\x34\x83\xbb\xa4\x00\x00\x00\x00\x0f\x84\xaa\x00\x00\x00\x8b\x93\xa0\x00\x00\x00\x03\xd7\x89\x54\x24\x2c\x8d\x4a\x04\x8b\x01\x89\x4c\x24\x14\x85\xc0\x0f\x84\x8d\x00\x00\x00\x8b\x32\x8d\x58\xf8\x03\xf7\x8d\x42\x08\xd1\xeb\x89\x44\x24\x1c\x74\x60\x6a\x02\x8b\xf8\x5a\x0f\xb7\x0f\x4b\x66\x8b\xc1\x66\xc1\xe8\x0c\x66\x83\xf8\x0a\x74\x06\x66\x83\xf8\x03\x75\x0b\x81\xe1\xff\x0f\x00\x00\x01\x2c\x31\xeb\x27\x66\x3b\x44\x24\x18\x75\x11\x81\xe1\xff\x0f\x00\x00\x8b\xc5\xc1\xe8\x10\x66\x01\x04\x31\xeb\x0f\x66\x3b\xc2\x75\x0a\x81\xe1\xff\x0f\x00\x00\x66\x01\x2c\x31\x03\xfa\x85\xdb\x75\xb1\x8b\x7c\x24\x20\x8b\x54\x24\x2c\x8b\x4c\x24\x14\x03\x11\x89\x54\x24\x2c\x8d\x4a\x04\x8b\x01\x89\x4c\x24\x14\x85\xc0\x0f\x85\x77\xff\xff\xff\x8b\x5c\x24\x10\x8b\x73\x28\x6a\x00\x6a\x00\x6a\xff\x03\xf7\xff\x54\x24\x30\x33\xc0\x40\x50\x50\x57\xff\xd6\x83\x7c\x24\x30\x00\x74\x7c\x83\x7b\x7c\x00\x74\x76\x8b\x4b\x78\x03\xcf\x8b\x41\x18\x85\xc0\x74\x6a\x83\x79\x14\x00\x74\x64\x8b\x69\x20\x8b\x71\x24\x03\xef\x83\x64\x24\x2c\x00\x03\xf7\x85\xc0\x74\x51\x8b\x5d\x00\x03\xdf\x33\xd2\x0f\xbe\x03\xc1\xca\x0d\x03\xd0\x43\x80\x7b\xff\x00\x75\xf1\x39\x54\x24\x30\x74\x16\x8b\x44\x24\x2c\x83\xc5\x04\x40\x83\xc6\x02\x89\x44\x24\x2c\x3b\x41\x18\x72\xd0\xeb\x1f\x0f\xb7\x16\x83\xfa\xff\x74\x17\x8b\x41\x1c\xff\x74\x24\x38\xff\x74\x24\x38\x8d\x04\x90\x8b\x04\x38\x03\xc7\xff\xd0\x59\x59\x8b\xc7\x5f\x5e\x5d\x5b\x83\xc4\x18\xc3\x83\xec\x10\x64\xa1\x30\x00\x00\x00\x53\x55\x56\x8b\x40\x0c\x57\x89\x4c\x24\x18\x8b\x70\x0c\xe9\x8a\x00\x00\x00\x8b\x46\x30\x33\xc9\x8b\x5e\x2c\x8b\x36\x89\x44\x24\x14\x8b\x42\x3c\x8b\x6c\x10\x78\x89\x6c\x24\x10\x85\xed\x74\x6d\xc1\xeb\x10\x33\xff\x85\xdb\x74\x1f\x8b\x6c\x24\x14\x8a\x04\x2f\xc1\xc9\x0d\x3c\x61\x0f\xbe\xc0\x7c\x03\x83\xc1\xe0\x03\xc8\x47\x3b\xfb\x72\xe9\x8b\x6c\x24\x10\x8b\x44\x2a\x20\x33\xdb\x8b\x7c\x2a\x18\x03\xc2\x89\x7c\x24\x14\x85\xff\x74\x31\x8b\x28\x33\xff\x03\xea\x83\xc0\x04\x89\x44\x24\x1c\x0f\xbe\x45\x00\xc1\xcf\x0d\x03\xf8\x45\x80\x7d\xff\x00\x75\xf0\x8d\x04\x0f\x3b\x44\x24\x18\x74\x20\x8b\x44\x24\x1c\x43\x3b\x5c\x24\x14\x72\xcf\x8b\x56\x18\x85\xd2\x0f\x85\x6b\xff\xff\xff\x33\xc0\x5f\x5e\x5d\x5b\x83\xc4\x10\xc3\x8b\x74\x24\x10\x8b\x44\x16\x24\x8d\x04\x58\x0f\xb7\x0c\x10\x8b\x44\x16\x1c\x8d\x04\x88\x8b\x04\x10\x03\xc2\xeb\xdb";
|
||||
|
||||
if is64BitDLL(dllBytes):
|
||||
|
||||
rdiShellcode = rdiShellcode64
|
||||
|
||||
bootstrap = b''
|
||||
bootstrapSize = 34
|
||||
|
||||
# call next instruction (Pushes next instruction address to stack)
|
||||
bootstrap += b'\xe8\x00\x00\x00\x00'
|
||||
|
||||
#Here is where the we pop the address of our next instruction off the stack and into the first register
|
||||
# pop rcx
|
||||
bootstrap += b'\x59'
|
||||
|
||||
# mov r8, rcx - copy our location in memory to r8 before we start modifying RCX
|
||||
bootstrap += b'\x49\x89\xc8'
|
||||
|
||||
# Setup the location of the DLL into RCX
|
||||
# add rcx, 29 (Size of bootstrap from pop) + <Length of RDI Shellcode>
|
||||
bootstrap += b'\x48\x81\xc1'
|
||||
dllLocation = bootstrapSize - 5 + len(rdiShellcode);
|
||||
bootstrap += pack('I', dllLocation)
|
||||
|
||||
# mov edx, <hash of function>
|
||||
bootstrap += b'\xba'
|
||||
bootstrap += pack('I', functionHash)
|
||||
|
||||
# Setup the location of our user data
|
||||
# add r8, (Size of bootstrap) + <Length of RDI Shellcode> + <Length of DLL>
|
||||
bootstrap += b'\x49\x81\xc0'
|
||||
userDataLocation = bootstrapSize - 5 + len(rdiShellcode) + len(dllBytes);
|
||||
bootstrap += pack('I', userDataLocation)
|
||||
|
||||
# mov r9d, <Length of User Data>
|
||||
bootstrap += b'\x41\xb9'
|
||||
bootstrap += pack('I', len(userData))
|
||||
|
||||
# Ends up looking like this in memory:
|
||||
# Bootstrap shellcode
|
||||
# RDI shellcode
|
||||
# DLL bytes
|
||||
# User data
|
||||
return bootstrap + rdiShellcode + dllBytes + userData
|
||||
|
||||
else: # 32 bit
|
||||
rdiShellcode = rdiShellcode32
|
||||
|
||||
bootstrap = b''
|
||||
bootstrapSize = 40
|
||||
|
||||
# call next instruction (Pushes next instruction address to stack)
|
||||
bootstrap += b'\xe8\x00\x00\x00\x00'
|
||||
|
||||
#Here is where the we pop the address of our next instruction off the stack and into the first register
|
||||
# pop rcx
|
||||
bootstrap += b'\x58'
|
||||
|
||||
# mov ebx, eax - copy our location in memory to ebx before we start modifying eax
|
||||
bootstrap += b'\x89\xc3'
|
||||
|
||||
# add eax, <size of bootstrap> + <Size of RDI Shellcode>
|
||||
bootstrap += b'\x05'
|
||||
dllLocation = bootstrapSize - 5 + len(rdiShellcode);
|
||||
bootstrap += pack('I', dllLocation)
|
||||
|
||||
# add ebx, <size of bootstrap> + <Size of RDI Shellcode> + <Size of DLL>
|
||||
bootstrap += b'\x81\xc3'
|
||||
userDataLocation = bootstrapSize - 5 + len(rdiShellcode) + len(dllBytes);
|
||||
bootstrap += pack('I', userDataLocation)
|
||||
|
||||
# push <Length of User Data>
|
||||
bootstrap += b'\x68'
|
||||
bootstrap += pack('I', len(userData))
|
||||
|
||||
# push ebx
|
||||
bootstrap += b'\x53'
|
||||
|
||||
# push <hash of function>
|
||||
bootstrap += b'\x68'
|
||||
bootstrap += pack('I', functionHash)
|
||||
|
||||
# push eax
|
||||
bootstrap += b'\x50'
|
||||
|
||||
# call instruction - We need to transfer execution to the RDI assembly this way (Skip over our next few op codes)
|
||||
bootstrap += b'\xe8\x04\x00\x00\x00'
|
||||
|
||||
# add esp, 0x10 - RDI pushes things to the stack it never removes, we need to make the correction ourselves
|
||||
bootstrap += b'\x83\xc4\x10'
|
||||
|
||||
# ret - because we used call earlier
|
||||
bootstrap += b'\xc3'
|
||||
|
||||
# Ends up looking like this in memory:
|
||||
# Bootstrap shellcode
|
||||
# RDI shellcode
|
||||
# DLL bytes
|
||||
# User data
|
||||
return bootstrap + rdiShellcode + dllBytes + userData
|
||||
|
||||
return False;
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print('Usage: RDIShellcodePyConverter.py [DLL File]')
|
||||
sys.exit()
|
||||
|
||||
print('Creating Shellcode: {}'.format(sys.argv[1].replace('.dll', '.bin')))
|
||||
dll = open(sys.argv[1], 'rb').read()
|
||||
|
||||
if len(dll) > 0: convertedDLL = ConvertToShellcode(dll, HashFunctionName("SayHello"))
|
||||
|
||||
with open(sys.argv[1].replace('.dll', '.bin'), 'wb') as f:
|
||||
f.write(convertedDLL)
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>be642266-f34d-43c3-b6e4-eebf8e489519</ProjectGuid>
|
||||
<ProjectHome>
|
||||
</ProjectHome>
|
||||
<StartupFile>
|
||||
</StartupFile>
|
||||
<SearchPath>
|
||||
</SearchPath>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<Name>Python</Name>
|
||||
<RootNamespace>RDIShellcodePyLoader</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ConvertToShellcode.py" />
|
||||
<Compile Include="pefile.py" />
|
||||
<Compile Include="peutils.py" />
|
||||
</ItemGroup>
|
||||
<!-- Uncomment the CoreCompile target to enable the Build command in
|
||||
Visual Studio and specify your pre- and post-build commands in
|
||||
the BeforeBuild and AfterBuild targets below. -->
|
||||
<!--<Target Name="CoreCompile" />-->
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
|
||||
</Project>
|
||||
+4811
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,581 @@
|
||||
# -*- coding: Latin-1 -*-
|
||||
"""peutils, Portable Executable utilities module
|
||||
|
||||
|
||||
Copyright (c) 2005-2012 Ero Carrera <ero.carrera@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
For detailed copyright information see the file COPYING in
|
||||
the root of the distribution archive.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import pefile
|
||||
|
||||
__author__ = 'Ero Carrera'
|
||||
__version__ = pefile.__version__
|
||||
__contact__ = 'ero.carrera@gmail.com'
|
||||
|
||||
|
||||
|
||||
|
||||
class SignatureDatabase:
|
||||
"""This class loads and keeps a parsed PEiD signature database.
|
||||
|
||||
Usage:
|
||||
|
||||
sig_db = SignatureDatabase('/path/to/signature/file')
|
||||
|
||||
and/or
|
||||
|
||||
sig_db = SignatureDatabase()
|
||||
sig_db.load('/path/to/signature/file')
|
||||
|
||||
Signature databases can be combined by performing multiple loads.
|
||||
|
||||
The filename parameter can be a URL too. In that case the
|
||||
signature database will be downloaded from that location.
|
||||
"""
|
||||
|
||||
def __init__(self, filename=None, data=None):
|
||||
|
||||
# RegExp to match a signature block
|
||||
#
|
||||
self.parse_sig = re.compile(
|
||||
'\[(.*?)\]\s+?signature\s*=\s*(.*?)(\s+\?\?)*\s*ep_only\s*=\s*(\w+)(?:\s*section_start_only\s*=\s*(\w+)|)', re.S)
|
||||
|
||||
# Signature information
|
||||
#
|
||||
# Signatures are stored as trees using dictionaries
|
||||
# The keys are the byte values while the values for
|
||||
# each key are either:
|
||||
#
|
||||
# - Other dictionaries of the same form for further
|
||||
# bytes in the signature
|
||||
#
|
||||
# - A dictionary with a string as a key (packer name)
|
||||
# and None as value to indicate a full signature
|
||||
#
|
||||
self.signature_tree_eponly_true = dict ()
|
||||
self.signature_count_eponly_true = 0
|
||||
self.signature_tree_eponly_false = dict ()
|
||||
self.signature_count_eponly_false = 0
|
||||
self.signature_tree_section_start = dict ()
|
||||
self.signature_count_section_start = 0
|
||||
|
||||
# The depth (length) of the longest signature
|
||||
#
|
||||
self.max_depth = 0
|
||||
|
||||
self.__load(filename=filename, data=data)
|
||||
|
||||
def generate_section_signatures(self, pe, name, sig_length=512):
|
||||
"""Generates signatures for all the sections in a PE file.
|
||||
|
||||
If the section contains any data a signature will be created
|
||||
for it. The signature name will be a combination of the
|
||||
parameter 'name' and the section number and its name.
|
||||
"""
|
||||
|
||||
section_signatures = list()
|
||||
|
||||
for idx, section in enumerate(pe.sections):
|
||||
|
||||
if section.SizeOfRawData < sig_length:
|
||||
continue
|
||||
|
||||
#offset = pe.get_offset_from_rva(section.VirtualAddress)
|
||||
offset = section.PointerToRawData
|
||||
|
||||
sig_name = '%s Section(%d/%d,%s)' % (
|
||||
name, idx + 1, len(pe.sections),
|
||||
''.join([c for c in section.Name if c in string.printable]))
|
||||
|
||||
section_signatures.append(
|
||||
self.__generate_signature(
|
||||
pe, offset, sig_name, ep_only=False,
|
||||
section_start_only=True,
|
||||
sig_length=sig_length) )
|
||||
|
||||
return '\n'.join(section_signatures)+'\n'
|
||||
|
||||
|
||||
|
||||
def generate_ep_signature(self, pe, name, sig_length=512):
|
||||
"""Generate signatures for the entry point of a PE file.
|
||||
|
||||
Creates a signature whose name will be the parameter 'name'
|
||||
and the section number and its name.
|
||||
"""
|
||||
|
||||
offset = pe.get_offset_from_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint)
|
||||
|
||||
return self.__generate_signature(
|
||||
pe, offset, name, ep_only=True, sig_length=sig_length)
|
||||
|
||||
|
||||
|
||||
def __generate_signature(self, pe, offset, name, ep_only=False,
|
||||
section_start_only=False, sig_length=512):
|
||||
|
||||
data = pe.__data__[offset:offset+sig_length]
|
||||
|
||||
signature_bytes = ' '.join(['%02x' % ord(c) for c in data])
|
||||
|
||||
if ep_only == True:
|
||||
ep_only = 'true'
|
||||
else:
|
||||
ep_only = 'false'
|
||||
|
||||
if section_start_only == True:
|
||||
section_start_only = 'true'
|
||||
else:
|
||||
section_start_only = 'false'
|
||||
|
||||
signature = '[%s]\nsignature = %s\nep_only = %s\nsection_start_only = %s\n' % (
|
||||
name, signature_bytes, ep_only, section_start_only)
|
||||
|
||||
return signature
|
||||
|
||||
def match(self, pe, ep_only=True, section_start_only=False):
|
||||
"""Matches and returns the exact match(es).
|
||||
|
||||
If ep_only is True the result will be a string with
|
||||
the packer name. Otherwise it will be a list of the
|
||||
form (file_ofsset, packer_name). Specifying where
|
||||
in the file the signature was found.
|
||||
"""
|
||||
|
||||
matches = self.__match(pe, ep_only, section_start_only)
|
||||
|
||||
# The last match (the most precise) from the
|
||||
# list of matches (if any) is returned
|
||||
#
|
||||
if matches:
|
||||
if ep_only == False:
|
||||
# Get the most exact match for each list of matches
|
||||
# at a given offset
|
||||
#
|
||||
return [(match[0], match[1][-1]) for match in matches]
|
||||
|
||||
return matches[1][-1]
|
||||
|
||||
return None
|
||||
|
||||
def match_all(self, pe, ep_only=True, section_start_only=False):
|
||||
"""Matches and returns all the likely matches."""
|
||||
|
||||
matches = self.__match(pe, ep_only, section_start_only)
|
||||
|
||||
if matches:
|
||||
if ep_only == False:
|
||||
# Get the most exact match for each list of matches
|
||||
# at a given offset
|
||||
#
|
||||
return matches
|
||||
|
||||
return matches[1]
|
||||
|
||||
return None
|
||||
|
||||
def __match(self, pe, ep_only, section_start_only):
|
||||
|
||||
# Load the corresponding set of signatures
|
||||
# Either the one for ep_only equal to True or
|
||||
# to False
|
||||
#
|
||||
if section_start_only is True:
|
||||
|
||||
# Fetch the data of the executable as it'd
|
||||
# look once loaded in memory
|
||||
#
|
||||
try :
|
||||
data = pe.__data__
|
||||
except Exception as excp :
|
||||
raise
|
||||
|
||||
# Load the corresponding tree of signatures
|
||||
#
|
||||
signatures = self.signature_tree_section_start
|
||||
|
||||
# Set the starting address to start scanning from
|
||||
#
|
||||
scan_addresses = [section.PointerToRawData for section in pe.sections]
|
||||
|
||||
elif ep_only is True:
|
||||
|
||||
# Fetch the data of the executable as it'd
|
||||
# look once loaded in memory
|
||||
#
|
||||
try :
|
||||
data = pe.get_memory_mapped_image()
|
||||
except Exception as excp :
|
||||
raise
|
||||
|
||||
# Load the corresponding tree of signatures
|
||||
#
|
||||
signatures = self.signature_tree_eponly_true
|
||||
|
||||
# Fetch the entry point of the PE file and the data
|
||||
# at the entry point
|
||||
#
|
||||
ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
|
||||
|
||||
# Set the starting address to start scanning from
|
||||
#
|
||||
scan_addresses = [ep]
|
||||
|
||||
else:
|
||||
|
||||
data = pe.__data__
|
||||
|
||||
signatures = self.signature_tree_eponly_false
|
||||
|
||||
scan_addresses = range( len(data) )
|
||||
|
||||
# For each start address, check if any signature matches
|
||||
#
|
||||
matches = []
|
||||
for idx in scan_addresses:
|
||||
result = self.__match_signature_tree(
|
||||
signatures,
|
||||
data[idx:idx+self.max_depth])
|
||||
if result:
|
||||
matches.append( (idx, result) )
|
||||
|
||||
# Return only the matched items found at the entry point if
|
||||
# ep_only is True (matches will have only one element in that
|
||||
# case)
|
||||
#
|
||||
if ep_only is True:
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
def match_data(self, code_data, ep_only=True, section_start_only=False):
|
||||
|
||||
data = code_data
|
||||
scan_addresses = [ 0 ]
|
||||
|
||||
# Load the corresponding set of signatures
|
||||
# Either the one for ep_only equal to True or
|
||||
# to False
|
||||
#
|
||||
if section_start_only is True:
|
||||
|
||||
# Load the corresponding tree of signatures
|
||||
#
|
||||
signatures = self.signature_tree_section_start
|
||||
|
||||
# Set the starting address to start scanning from
|
||||
#
|
||||
|
||||
elif ep_only is True:
|
||||
|
||||
# Load the corresponding tree of signatures
|
||||
#
|
||||
signatures = self.signature_tree_eponly_true
|
||||
|
||||
|
||||
# For each start address, check if any signature matches
|
||||
#
|
||||
matches = []
|
||||
for idx in scan_addresses:
|
||||
result = self.__match_signature_tree(
|
||||
signatures,
|
||||
data[idx:idx+self.max_depth])
|
||||
if result:
|
||||
matches.append( (idx, result) )
|
||||
|
||||
# Return only the matched items found at the entry point if
|
||||
# ep_only is True (matches will have only one element in that
|
||||
# case)
|
||||
#
|
||||
if ep_only is True:
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
def __match_signature_tree(self, signature_tree, data, depth = 0):
|
||||
"""Recursive function to find matches along the signature tree.
|
||||
|
||||
signature_tree is the part of the tree left to walk
|
||||
data is the data being checked against the signature tree
|
||||
depth keeps track of how far we have gone down the tree
|
||||
"""
|
||||
|
||||
|
||||
matched_names = list ()
|
||||
match = signature_tree
|
||||
|
||||
# Walk the bytes in the data and match them
|
||||
# against the signature
|
||||
#
|
||||
for idx, byte in enumerate ( [ord (b) for b in data] ):
|
||||
|
||||
# If the tree is exhausted...
|
||||
#
|
||||
if match is None :
|
||||
break
|
||||
|
||||
# Get the next byte in the tree
|
||||
#
|
||||
match_next = match.get(byte, None)
|
||||
|
||||
|
||||
# If None is among the values for the key
|
||||
# it means that a signature in the database
|
||||
# ends here and that there's an exact match.
|
||||
#
|
||||
if None in list(match.values()):
|
||||
# idx represent how deep we are in the tree
|
||||
#
|
||||
#names = [idx+depth]
|
||||
names = list()
|
||||
|
||||
# For each of the item pairs we check
|
||||
# if it has an element other than None,
|
||||
# if not then we have an exact signature
|
||||
#
|
||||
for item in list(match.items()):
|
||||
if item[1] is None :
|
||||
names.append (item[0])
|
||||
matched_names.append(names)
|
||||
|
||||
# If a wildcard is found keep scanning the signature
|
||||
# ignoring the byte.
|
||||
#
|
||||
if '??' in match :
|
||||
match_tree_alternate = match.get ('??', None)
|
||||
data_remaining = data[idx + 1 :]
|
||||
if data_remaining:
|
||||
matched_names.extend(
|
||||
self.__match_signature_tree(
|
||||
match_tree_alternate, data_remaining, idx+depth+1))
|
||||
|
||||
match = match_next
|
||||
|
||||
# If we have any more packer name in the end of the signature tree
|
||||
# add them to the matches
|
||||
#
|
||||
if match is not None and None in list(match.values()):
|
||||
#names = [idx + depth + 1]
|
||||
names = list()
|
||||
for item in list(match.items()) :
|
||||
if item[1] is None:
|
||||
names.append(item[0])
|
||||
matched_names.append(names)
|
||||
|
||||
return matched_names
|
||||
|
||||
def load(self , filename=None, data=None):
|
||||
"""Load a PEiD signature file.
|
||||
|
||||
Invoking this method on different files combines the signatures.
|
||||
"""
|
||||
|
||||
self.__load(filename=filename, data=data)
|
||||
|
||||
def __load(self, filename=None, data=None):
|
||||
|
||||
|
||||
if filename is not None:
|
||||
# If the path does not exist, attempt to open a URL
|
||||
#
|
||||
if not os.path.exists(filename):
|
||||
try:
|
||||
sig_f = urllib.request.urlopen(filename)
|
||||
sig_data = sig_f.read()
|
||||
sig_f.close()
|
||||
except IOError:
|
||||
# Let this be raised back to the user...
|
||||
raise
|
||||
else:
|
||||
# Get the data for a file
|
||||
#
|
||||
try:
|
||||
sig_f = file( filename, 'rt' )
|
||||
sig_data = sig_f.read()
|
||||
sig_f.close()
|
||||
except IOError:
|
||||
# Let this be raised back to the user...
|
||||
raise
|
||||
else:
|
||||
sig_data = data
|
||||
|
||||
# If the file/URL could not be read or no "raw" data
|
||||
# was provided there's nothing else to do
|
||||
#
|
||||
if not sig_data:
|
||||
return
|
||||
|
||||
# Helper function to parse the signature bytes
|
||||
#
|
||||
def to_byte(value) :
|
||||
if value == '??' or value == '?0' :
|
||||
return value
|
||||
return int (value, 16)
|
||||
|
||||
|
||||
# Parse all the signatures in the file
|
||||
#
|
||||
matches = self.parse_sig.findall(sig_data)
|
||||
|
||||
# For each signature, get the details and load it into the
|
||||
# signature tree
|
||||
#
|
||||
for packer_name, signature, superfluous_wildcards, ep_only, section_start_only in matches:
|
||||
|
||||
ep_only = ep_only.strip().lower()
|
||||
|
||||
signature = signature.replace('\\n', '').strip()
|
||||
|
||||
signature_bytes = [to_byte(b) for b in signature.split()]
|
||||
|
||||
if ep_only == 'true':
|
||||
ep_only = True
|
||||
else:
|
||||
ep_only = False
|
||||
|
||||
if section_start_only == 'true':
|
||||
section_start_only = True
|
||||
else:
|
||||
section_start_only = False
|
||||
|
||||
|
||||
depth = 0
|
||||
|
||||
if section_start_only is True:
|
||||
|
||||
tree = self.signature_tree_section_start
|
||||
self.signature_count_section_start += 1
|
||||
|
||||
else:
|
||||
if ep_only is True :
|
||||
tree = self.signature_tree_eponly_true
|
||||
self.signature_count_eponly_true += 1
|
||||
else :
|
||||
tree = self.signature_tree_eponly_false
|
||||
self.signature_count_eponly_false += 1
|
||||
|
||||
for idx, byte in enumerate (signature_bytes) :
|
||||
|
||||
if idx+1 == len(signature_bytes):
|
||||
|
||||
tree[byte] = tree.get( byte, dict() )
|
||||
tree[byte][packer_name] = None
|
||||
|
||||
else :
|
||||
|
||||
tree[byte] = tree.get ( byte, dict() )
|
||||
|
||||
tree = tree[byte]
|
||||
depth += 1
|
||||
|
||||
if depth > self.max_depth:
|
||||
self.max_depth = depth
|
||||
|
||||
|
||||
|
||||
|
||||
def is_valid( pe ):
|
||||
""""""
|
||||
pass
|
||||
|
||||
|
||||
def is_suspicious( pe ):
|
||||
"""
|
||||
unusual locations of import tables
|
||||
non recognized section names
|
||||
presence of long ASCII strings
|
||||
"""
|
||||
|
||||
relocations_overlap_entry_point = False
|
||||
sequential_relocs = 0
|
||||
|
||||
# If relocation data is found and the entries go over the entry point, and also are very
|
||||
# continuous or point outside section's boundaries => it might imply that an obfuscation
|
||||
# trick is being used or the relocations are corrupt (maybe intentionally)
|
||||
#
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_BASERELOC'):
|
||||
for base_reloc in pe.DIRECTORY_ENTRY_BASERELOC:
|
||||
last_reloc_rva = None
|
||||
for reloc in base_reloc.entries:
|
||||
if reloc.rva <= pe.OPTIONAL_HEADER.AddressOfEntryPoint <= reloc.rva + 4:
|
||||
relocations_overlap_entry_point = True
|
||||
|
||||
if last_reloc_rva is not None and last_reloc_rva <= reloc.rva <= last_reloc_rva + 4:
|
||||
sequential_relocs += 1
|
||||
|
||||
last_reloc_rva = reloc.rva
|
||||
|
||||
|
||||
|
||||
# If import tables or strings exist (are pointed to) to within the header or in the area
|
||||
# between the PE header and the first section that's supicious
|
||||
#
|
||||
# IMPLEMENT
|
||||
|
||||
|
||||
warnings_while_parsing = False
|
||||
# If we have warnings, that's suspicious, some of those will be because of out-of-ordinary
|
||||
# values are found in the PE header fields
|
||||
# Things that are reported in warnings:
|
||||
# (parsing problems, special section characteristics i.e. W & X, uncommon values of fields,
|
||||
# unusual entrypoint, suspicious imports)
|
||||
#
|
||||
warnings = pe.get_warnings()
|
||||
if warnings:
|
||||
warnings_while_parsing
|
||||
|
||||
# If there are few or none (should come with a standard "density" of strings/kilobytes of data) longer (>8)
|
||||
# ascii sequences that might indicate packed data, (this is similar to the entropy test in some ways but
|
||||
# might help to discard cases of legitimate installer or compressed data)
|
||||
|
||||
# If compressed data (high entropy) and is_driver => uuuuhhh, nasty
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def is_probably_packed( pe ):
|
||||
"""Returns True is there is a high likelihood that a file is packed or contains compressed data.
|
||||
|
||||
The sections of the PE file will be analyzed, if enough sections
|
||||
look like containing containing compressed data and the data makes
|
||||
up for more than 20% of the total file size. The function will
|
||||
return True.
|
||||
"""
|
||||
|
||||
# Calculate the lenth of the data up to the end of the last section in the
|
||||
# file. Overlay data won't be taken into account
|
||||
#
|
||||
total_pe_data_length = len( pe.trim() )
|
||||
has_significant_amount_of_compressed_data = False
|
||||
|
||||
# If some of the sections have high entropy and they make for more than 20% of the file's size
|
||||
# it's assumed that it could be an installer or a packed file
|
||||
|
||||
total_compressed_data = 0
|
||||
for section in pe.sections:
|
||||
s_entropy = section.get_entropy()
|
||||
s_length = len( section.get_data() )
|
||||
# The value of 7.4 is empircal, based of looking at a few files packed
|
||||
# by different packers
|
||||
if s_entropy > 7.4:
|
||||
total_compressed_data += s_length
|
||||
|
||||
if ((1.0 * total_compressed_data)/total_pe_data_length) > .2:
|
||||
has_significant_amount_of_compressed_data = True
|
||||
|
||||
return has_significant_amount_of_compressed_data
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user