Skip to content

Instantly share code, notes, and snippets.

@tahaconfiant
Created December 10, 2019 10:10
Show Gist options
  • Save tahaconfiant/36bd7594f094e4d1b2afc14264f923dc to your computer and use it in GitHub Desktop.
Save tahaconfiant/36bd7594f094e4d1b2afc14264f923dc to your computer and use it in GitHub Desktop.
bundlore_python_dump2
# by [email protected]
# LLDB custom command to dump OSX/Bundlore Loader python payload
# tested on $lldb --version
# lldb-1100.0.30.6
# Apple Swift version 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
# (lldb) script
# Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
# >>> import sys
# >>> print(sys.version)
# 3.7.3 (default, Sep 5 2019, 17:14:41)
# [Clang 11.0.0 (clang-1100.0.33.8)]
# Can be run via :
# (lldb) command import script /path/to/bundlore_python_dump.py
# (lldb) bundlore_python_dump
# (lldb) r
import lldb
import time
import struct
from subprocess import check_output
def custom_breakpoints(debugger, command, result, internal_dict):
target = debugger.GetSelectedTarget()
breakpoint = target.BreakpointCreateByName("fork")
breakpoint.SetScriptCallbackFunction('bundlore_python_dump2.fork_callback')
breakpoint = target.BreakpointCreateByName("write", "libsystem_kernel.dylib")
breakpoint.SetScriptCallbackFunction('bundlore_python_dump2.write_callback')
breakpoint = target.BreakpointCreateByName("waitpid")
breakpoint.SetScriptCallbackFunction('bundlore_python_dump2.waitpid_callback')
def fork_callback(frame, bp_loc, dict):
print ("fork() detected!")
patch_address = 0x10000ac87
error = lldb.SBError()
backup_bytes = frame.thread.process.ReadPointerFromMemory(patch_address, error)
# backup_bytes = 0x7d8b
if error.Success():
print('backup_bytes : 0x%x' % backup_bytes)
else:
print('error: ', error)
# Write infinite loop (EB FE)
new_value = struct.pack('H', 0xfeeb)
result = frame.thread.process.WriteMemory(patch_address, new_value, error)
if not error.Success() or result != len(new_value):
print('SBProcess.WriteMemory() failed!')
print ('child process fully patched')
# Continue on the parent process
frame.thread.process.Continue()
def write_callback(frame, bp_loc, dict):
print ("write() detected!")
print ("dumping python code from $rsi register")
memory_address = 0
registerSet = frame.GetRegisters() # Returns an SBValueList.
for regs in registerSet:
if 'general purpose registers' in regs.name.lower():
GPRs = regs
break
print('%s (number of children = %d):' % (GPRs.name, GPRs.num_children))
for reg in GPRs:
if reg.name == "rsi":
memory_address = int(reg.value, 0)
bytes_count = 0
if memory_address:
error = lldb.SBError()
while frame.thread.process.ReadUnsignedFromMemory(memory_address, 1, error):
c = frame.thread.process.ReadUnsignedFromMemory(memory_address, 1, error)
open('/tmp/dumped.py', 'ab').write(bytes([c]))
memory_address += 1
bytes_count +=1
if error.Success():
print("sucessfully written %i bytes" % bytes_count)
else:
print('error: %s\n' % error)
else:
print ('error getting memory address')
frame.thread.process.Continue()
def get_child_pid(process_name, parent_pid):
child_pid = 0
for pid in check_output(["pgrep", process_name]).split():
print ("found new pid: %i\n" % int(pid))
if parent_pid == int(pid):
continue
elif int(pid) > parent_pid:
child_pid = int(pid)
break
else:
continue
return child_pid
def waitpid_callback(frame, bp_loc, dict):
print ("waitpid() detected!")
# let's attach to the child process:
# get the current process id
curr_target = frame.thread.process.GetTarget()
pid = frame.thread.process.GetProcessID()
print ("current pid is %s\n" % str(pid))
# let get the current filename of the current target
file_name = curr_target.GetExecutable().GetFilename()
print ("attempting to attach to any new instance of %s" % file_name)
# we have to detach from the parent process
frame.thread.process.Detach()
child_pid = get_child_pid(file_name, pid)
# Let's attempt to attach to the child process
listener = lldb.SBListener('listener')
error = lldb.SBError()
child = curr_target.AttachToProcessWithID(listener, child_pid, error)
if not error.Success():
print ("error %s\n", error.GetCString())
raise Exception('Failed to attach to the process.')
assert child.IsValid()
else:
print ("sucessfully attached to child, with pid : %s\n" % str(child.GetProcessID()))
# we stop and kill the child
print ("killing the child process")
child.Stop()
child.Kill()
print ("bundlore_python_dump has finished, dumped python code is here: /tmp/dumped.py")
# we stop and kill the parent
frame.thread.process.Stop()
frame.thread.process.Kill()
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f bundlore_python_dump2.custom_breakpoints bundlore_python_dump')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment