Skip to content

Instantly share code, notes, and snippets.

@alexander-hanel
Created April 25, 2022 21:04
Show Gist options
  • Save alexander-hanel/2d59a41bf870c2d843e4019a71fcb196 to your computer and use it in GitHub Desktop.
Save alexander-hanel/2d59a41bf870c2d843e4019a71fcb196 to your computer and use it in GitHub Desktop.
logs exported APIs in JVM
# Created By: Alexander Hanel
# Date: 20220425
# Version 2.0
# Purpose: Simple API logger for a subset of API's used by Java's JVM
# C:\tt\pypyp>C:\Python37\python.exe jvm_logger.py -file "C:\Progra~1\Java\jdk1.8.0_191\bin\java.exe" -args " -jar C:\tt\pypyp\victim-app-0.0.1-SNAPSHOT.jar"
import sys
import _ptrace
import argparse
import socket
DEBUG = True
exports = {"bp_set": False}
HOOK_LIST = ["JVM_FindClassFromBootLoader",
"JVM_FindClassFromClass",
"JVM_DefineClass",
"JVM_DefineClassWithSource",
"JVM_DefineClassWithSourceCond",
"JVM_Open",
"JVM_Close",
"JVM_Read",
"JVM_Write",
"JVM_Available",
"JVM_Lseek",
"JVM_SetLength",
"JVM_Sync",
"JVM_SocketClose",
"JVM_SocketShutdown",
"JVM_Recv",
"JVM_Send",
"JVM_Listen",
"JVM_Connect",
"JVM_Bind",
"JVM_Accept",
"JVM_RecvFrom",
"JVM_GetSockName",
"JVM_SendTo",
"JVM_SocketAvailable",
"JVM_GetSockOpt",
"JVM_SetSockOpt",
"JVM_LoadLibrary",
"JVM_FindLibraryEntry",
"JVM_IsSupportedJNIVersion"
]
def logger(cookie, string):
print(string, end="")
def attached_handler(process):
"""
First breakpoint called by _ptrace
:param process:
:return:
"""
main_bp = _ptrace.breakpoint_sw("kernel32!CreateThread", add_export_bps)
process.breakpoint_set(main_bp)
def add_export_bps(breakpoint, thread):
"""
handlers responsible for creating breakpoints
:param breakpoint:
:param thread:
:return:
"""
global exports
if exports["bp_set"] == False:
for func in exports.keys():
if func == "bp_set":
continue
if exports[func] in HOOK_LIST:
if DEBUG: print(hex(func), exports[func])
bp_func = _ptrace.breakpoint_sw(func, handler_func)
thread.process.breakpoint_set(bp_func)
exports["bp_set"] = True
def handler_func(breakpoint, thread):
"""
Prints details of the hooked API
:param breakpoint:
:param thread:
:return:
"""
global exports
retaddr = _ptrace.cconv.retaddr_get(thread)
if "eip" in thread.registers:
ip = thread.registers["eip"]
sp = thread.registers["esp"]
bp = thread.registers["ebp"]
else:
ip = thread.registers["rip"]
sp = thread.registers["rsp"]
bp = thread.registers["rbp"]
func = exports[ip]
if func == "JVM_FindClassFromBootLoader":
handle_find_class_from_bootLoader(breakpoint, thread)
if func == "JVM_FindClassFromClass":
handle_find_class_from_class(breakpoint, thread)
if func == "JVM_DefineClass":
handle_define_class(breakpoint, thread)
if func == "JVM_DefineClassWithSource":
handle_define_class_with_source(breakpoint, thread)
if func == "JVM_DefineClassWithSourceCond":
handle_define_class_with_source_cond(breakpoint, thread)
if func == "JVM_Open":
handle_open(breakpoint, thread)
if func == "JVM_Close":
handle_close(breakpoint, thread)
if func == "JVM_Read":
handle_read(breakpoint, thread)
if func == "JVM_Write":
handle_write(breakpoint, thread)
if func == "JVM_Available":
handle_available(breakpoint, thread)
if func == "JVM_Lseek":
handle_lseek(breakpoint, thread)
if func == "JVM_SetLength":
handle_set_length(breakpoint, thread)
if func == "JVM_Sync":
handle_sync(breakpoint, thread)
if func == "JVM_SocketClose":
handle_socket_close(breakpoint, thread)
if func == "JVM_SocketShutdown":
handle_socket_shutdown(breakpoint, thread)
if func == "JVM_Recv":
handle_recv(breakpoint, thread)
if func == "JVM_Send":
handle_send(breakpoint, thread)
if func == "JVM_Listen":
handle_listen(breakpoint, thread)
if func == "JVM_Connect":
handle_connect(breakpoint, thread)
if func == "JVM_Bind":
handle_bind(breakpoint, thread)
if func == "JVM_Accept":
handle_accept(breakpoint, thread)
if func == "JVM_RecvFrom":
handle_recv_from(breakpoint, thread)
if func == "JVM_GetSockName":
handle_get_sock_name(breakpoint, thread)
if func == "JVM_SendTo":
handle_send_to(breakpoint, thread)
if func == "JVM_SocketAvailable":
handle_socket_available(breakpoint, thread)
if func == "JVM_GetSockOpt":
handle_get_sock_opt(breakpoint, thread)
if func == "JVM_SetSockOpt":
handle_set_sock_opt(breakpoint, thread)
if func == "JVM_LoadLibrary":
handle_load_library(breakpoint, thread)
if func == "JVM_FindLibraryEntry":
handle_find_library_entry(breakpoint, thread)
if func == "JVM_IsSupportedJNIVersion":
handle_is_supported_jni_version(breakpoint, thread)
def handle_find_class_from_bootLoader(breakpoint, thread):
"""
Break point on
JVM_FindClassFromBootLoader(JNIEnv* env, const char* name)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(env, ptr_name) = _ptrace.cconv.args_get(thread, "%lu%p")
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_FindClassFromBootLoader: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_find_class_from_class(breakpoint, thread):
"""
JVM_FindClassFromClass(JNIEnv *env, const char *name,
jboolean init, jclass from))
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(env, ptr_name, jboolean, jclass) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu")
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_FindClassFromClass: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_define_class(breakpoint, thread):
"""
JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(env, ptr_name, loader, jbyte, len, pd) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu%lu%lu")
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_DefineClass: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_define_class_with_source(breakpoint, thread):
"""
JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd,
const char *source)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(env, ptr_name, loader, jbyte, len, pd, source) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu%lu%lu%p")
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_DefineClassWithSource: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_define_class_with_source_cond(breakpoint, thread):
"""
JVM_DefineClassWithSourceCond(JNIEnv *env, const char *name,
jobject loader, const jbyte *buf,
jsize len, jobject pd,
const char *source, jboolean verify)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(env, ptr_name, loader, jbyte, len, pd, source, verify) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu%lu%lu%p%lu")
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_DefineClassWithSourceCond: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_open(breakpoint, thread):
"""
JVM_Open(const char *fname, jint flags, jint mode)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(ptr_name, flags, mode) = _ptrace.cconv.args_get(thread, "%p%lu%lu")
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_Open: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_close(breakpoint, thread):
"""
JVM_Close(jint fd)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd) = _ptrace.cconv.args_get(thread, "%lu")
print("T{}: JVM_Close; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_read(breakpoint, thread):
"""
JVM_Read(jint fd, char *buf, jint nbytes)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, buf, nbytes) = _ptrace.cconv.args_get(thread, "%lu%p%lu")
print("T{}: JVM_Read; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_write(breakpoint, thread):
"""
JVM_Write(jint fd, char *buf, jint nbytes)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, buf, nbytes) = _ptrace.cconv.args_get(thread, "%lu%p%lu")
print("T{}: JVM_Write; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_available(breakpoint, thread):
"""
JVM_Available(jint fd, jlong *pbytes)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, nbytes) = _ptrace.cconv.args_get(thread, "%lu%p")
print("T{}: JVM_Available; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_lseek(breakpoint, thread):
"""
JVM_Lseek(jint fd, jlong offset, jint whence)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, offset,whence ) = _ptrace.cconv.args_get(thread, "%lu%lu%lu")
print("T{}: JVM_Lseek; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_set_length(breakpoint, thread):
"""
JVM_SetLength(jint fd, jlong length)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, length) = _ptrace.cconv.args_get(thread, "%lu%lu")
print("T{}: JVM_SetLength; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_sync(breakpoint, thread):
"""
JVM_Sync(jint fd)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd) = _ptrace.cconv.args_get(thread, "%lu")
print("T{}: JVM_Sync; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_socket_close(breakpoint, thread):
"""
JVM_SocketClose(jint fd)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd) = _ptrace.cconv.args_get(thread, "%lu")
print("T{}: JVM_SocketClose; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_socket_shutdown(breakpoint, thread):
"""
JVM_SocketShutdown(jint fd, jint howto)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, howto) = _ptrace.cconv.args_get(thread, "%lu%lu")
print("T{}: JVM_SocketShutdown; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_recv(breakpoint, thread):
"""
JVM_Recv(jint fd, char *buf, jint nBytes, jint flags)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, buf, nBytes, flags) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu")
print("T{}: JVM_Recv; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_send(breakpoint, thread):
"""
JVM_Send(jint fd, char *buf, jint nBytes, jint flags)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, buf, nBytes, flags) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu")
print("T{}: JVM_Send; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_listen(breakpoint, thread):
"""
JVM_Listen(jint fd, jint count)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, count) = _ptrace.cconv.args_get(thread, "%lu%lu")
print("T{}: JVM_Listen; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_connect(breakpoint, thread):
"""
JVM_Connect(jint fd, struct sockaddr *him, jint len)
:param breakpoint:
:param thread:
:return:
"""
# TODO parse out socket address
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, him, len) = _ptrace.cconv.args_get(thread, "%lu%p%lu")
print("T{}: JVM_Connect; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_bind(breakpoint, thread):
"""
JVM_Bind(jint fd, struct sockaddr *him, jint len)
:param breakpoint:
:param thread:
:return:
"""
# TODO parse out socket address
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, him, len) = _ptrace.cconv.args_get(thread, "%lu%p%lu")
print("T{}: JVM_Bind; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_accept(breakpoint, thread):
"""
JVM_Accept(jint fd, struct sockaddr *him, jint *len)
:param breakpoint:
:param thread:
:return:
"""
# TODO parse out socket address
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, him, len) = _ptrace.cconv.args_get(thread, "%lu%p%lu")
print("T{}: JVM_Accept; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_recv_from(breakpoint, thread):
"""
JVM_RecvFrom(jint fd, char *buf, int nBytes, int flags, struct sockaddr *from, int *fromlen)
:param breakpoint:
:param thread:
:return:
"""
# TODO parse out socket address
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, buf, nBytes, flags, from_, fromlen) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu%p%p")
print("T{}: JVM_RecvFrom; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_get_sock_name(breakpoint, thread):
"""
JVM_GetSockName(jint fd, struct sockaddr *him, int *len)
:param breakpoint:
:param thread:
:return:
"""
# TODO parse out socket address
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, him, len) = _ptrace.cconv.args_get(thread, "%lu%p%lu") # if needed, confirm len is ptr
print("T{}: JVM_GetSockName; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_send_to(breakpoint, thread):
"""
JVM_SendTo(jint fd, char *buf, int len, int flags, struct sockaddr *to, int tolen)
:param breakpoint:
:param thread:
:return:
"""
# TODO parse out socket address
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, buf, len, flags, to_, tolen) = _ptrace.cconv.args_get(thread, "%lu%p%lu%lu%p%p")
print("T{}: JVM_SendTo; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_socket_available(breakpoint, thread):
"""
JVM_SocketAvailable(jint fd, jint *pbytes)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, pbytes) = _ptrace.cconv.args_get(thread, "%lu%p%") # if needed, confirm len is ptr
print("T{}: JVM_SocketAvailable; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_get_sock_opt(breakpoint, thread):
"""
JVM_GetSockOpt(jint fd, int level, int optname, char *optval, int *optlen)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, level, optname, optval, optlen) = _ptrace.cconv.args_get(thread, "%lu%lu%lu%p%lu") # if needed, confirm len is ptr
print("T{}: JVM_GetSockOpt; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_set_sock_opt(breakpoint, thread):
"""
JVM_SetSockOpt(jint fd, int level, int optname, const char *optval, int optlen)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(fd, level, optname, optval, optlen) = _ptrace.cconv.args_get(thread,"%lu%lu%lu%p%lu")
print("T{}: JVM_SetSockOpt; RetAddr:{:08x}".format(thread.id, retaddr))
def handle_load_library(breakpoint, thread):
"""
JVM_LoadLibrary(const char* name)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
ptr_name = _ptrace.cconv.args_get(thread, "%p")[0]
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_LoadLibrary: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_find_library_entry(breakpoint, thread):
"""
JVM_FindLibraryEntry(void* handle, const char* name)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(handle, ptr_name) = _ptrace.cconv.args_get(thread, "%lu%p")
name = thread.process.read_utf8(ptr_name)
print("T{}: JVM_FindLibraryEntry: {}; RetAddr:{:08x}".format(thread.id, name, retaddr))
def handle_is_supported_jni_version(breakpoint, thread):
"""
JVM_IsSupportedJNIVersion(jint version)
:param breakpoint:
:param thread:
:return:
"""
retaddr = _ptrace.cconv.retaddr_get(thread)
(version) = _ptrace.cconv.args_get(thread, "%lu")
print("T{}: JVM_IsSupportedJNIVersion: {}; RetAddr:{:08x}".format(thread.id, version, retaddr))
def process_exit(process):
"""
Informational default libptrace code
:param process:
:return:
"""
print("[{}] exited".format(process.id))
def thread_create(process, thread):
"""
Informational default libptrace code
:param process:
:return:
"""
print("[{}] Created thread with tid {}".format(process.id, thread.id))
def thread_exit(process, thread):
"""
Informational default libptrace code
:param process:
:return:
"""
print("[{}] Thread with tid {} exited".format(process.id, thread.id))
def module_load(process, module):
"""
Extracts exports from jvm.dll if loaded into memory.
:param process:
:return:
"""
if module.name == "jvm":
global exports
for name, address in module.exports.items():
# demangle text for vtable exports. the offsets were causing issues with libptrace
if name.endswith("@@6B@"):
continue
exports[address] = name
print("[{}] Module {} loaded at 0x{:08x}".format(process.id, module.name, module.base))
def module_unload(process, module):
"""
Informational default libptrace code
:param process:
:return:
"""
print("[{}] Module {} unloaded".format(process.id, module.name))
def breakpoint(process, breakpoint, chance):
"""
Informational default libptrace code
:param process:
:return:
"""
print("[{}] Breakpoint".format(process.id))
# parse command line arguments
parser = argparse.ArgumentParser(description='Go Portable Executbale Function Hooker.')
parser.add_argument('-file', nargs='?', metavar='filename', help='executable.')
parser.add_argument('-args', nargs='*', metavar='args', help='arguments.')
parser.add_argument('--debug', '-d', action='store_true')
parser.add_argument('--second-chance', '-s', action='store_true')
args = parser.parse_args(sys.argv[1:])
if args.debug:
_ptrace.log_hook_add(_ptrace.log_hook(logger))
# must define handlers
handlers = _ptrace.event_handlers()
handlers.attached = attached_handler
handlers.process_exit = process_exit
handlers.thread_create = thread_create
handlers.thread_exit = thread_exit
handlers.module_load = module_load
handlers.module_unload = module_unload
handlers.breakpoint = breakpoint
options = 0
if args.file:
_ptrace.execv(args.file, args.args, handlers, options)
_ptrace.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment