Skip to content

Instantly share code, notes, and snippets.

@gdraperi
Forked from tahaconfiant/follow_fork_child.py
Created December 11, 2019 12:13
Show Gist options
  • Save gdraperi/0d88e856e2721164ae3444e6f4ca806b to your computer and use it in GitHub Desktop.
Save gdraperi/0d88e856e2721164ae3444e6f4ca806b to your computer and use it in GitHub Desktop.
follow_fork_child
# by [email protected]
# LLDB custom command follow-fork-child, equivalent to GDB "follow-fork-mode child"
# 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)]
# to run it :
## (lldb) command import script /path/to/follow_fork_child.py
## (lldb) follow-fork-child
## (lldb) r
import lldb
import time
import struct
from subprocess import check_output
# global variables
# this variable stores original bytes of the child process before the patch
backup_bytes = 0
# this is the "child entry-point"
patch_address = 0x10000ac87
def custom_breakpoints(debugger, command, result, internal_dict):
target = debugger.GetSelectedTarget()
breakpoint = target.BreakpointCreateByName("fork")
breakpoint.SetScriptCallbackFunction('follow_fork_child.fork_callback')
breakpoint = target.BreakpointCreateByName("waitpid")
breakpoint.SetScriptCallbackFunction('follow_fork_child.waitpid_callback')
def fork_callback(frame, bp_loc, dict):
global backup_bytes
global patch_address
print ("fork() detected!")
error = lldb.SBError()
backup_bytes = frame.thread.process.ReadUnsignedFromMemory(patch_address, 2, 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 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):
global backup_bytes
global patch_address
print ("waitpid() detected!")
# let's attach to the child process:
# Get the current debugged process pid
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 are attached to the child, we running on the child context:
# lets recover our backup_bytes
print('backup_bytes recovered : 0x%x' % backup_bytes)
# let's write the original backup_byte
new_value = struct.pack('h', backup_bytes)
error = lldb.SBError()
result = child.WriteMemory(patch_address, new_value, error)
if not error.Success() or result != len(new_value):
print('SBProcess.WriteMemory() failed!')
print ("error %s\n" % error.GetCString())
raise Exception('Failed to patch the child process with backup_bytes')
else:
print('sucessfully patched child process with backup_bytes 0x%x' % backup_bytes)
print ('We are now in the child process')
# <enter here your next commands>
# Optional: We stop/kill the parent
frame.thread.process.Stop()
frame.thread.process.Kill()
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f follow_fork_child.custom_breakpoints follow-fork-child')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment