Created
April 25, 2022 21:04
-
-
Save alexander-hanel/2d59a41bf870c2d843e4019a71fcb196 to your computer and use it in GitHub Desktop.
logs exported APIs in JVM
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
# 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