4141import java .util .Map ;
4242import java .util .Set ;
4343import java .util .WeakHashMap ;
44+ import java .io .IOException ;
4445import javax .swing .JFrame ;
4546import javax .swing .JOptionPane ;
4647import javax .swing .SwingUtilities ;
48+ import java .util .prefs .Preferences ;
4749
4850public class FridaTracePlugin implements JadxPlugin {
4951 private JadxPluginContext pluginContext ;
@@ -66,6 +68,9 @@ public class FridaTracePlugin implements JadxPlugin {
6668 private FridaSessionConfig lastSessionConfig = new FridaSessionConfig ();
6769 private ScriptOptions lastScriptOptions = new ScriptOptions ();
6870 private FridaSessionConfig activeSessionConfig ;
71+ private String customScriptPaths = "" ;
72+ private final java .util .Set <String > missingCustomScripts = new java .util .HashSet <>();
73+ private static final Preferences PREFS = Preferences .userRoot ().node ("com.jarida.jadxfrida" );
6974
7075 @ Override
7176 public JadxPluginInfo getPluginInfo () {
@@ -86,6 +91,8 @@ public void init(JadxPluginContext context) {
8691 context .registerOptions (pluginOptions );
8792 this .lastSessionConfig = pluginOptions .toSessionConfig ();
8893 this .lastScriptOptions = pluginOptions .toScriptOptions ();
94+ this .customScriptPaths = pluginOptions .getCustomScriptPaths ();
95+ loadSavedPaths ();
8996 if (guiContext != null ) {
9097 initGui ();
9198 guiContext .settings ().setCustomSettingsGroup (
@@ -156,12 +163,15 @@ private void openConsole(boolean focus) {
156163 String pkg = PackageNameResolver .resolvePackageName (decompiler );
157164 String version = getVersionString ();
158165 connectionPanel = new JaridaConnectionPanel (fridaController , lastSessionConfig , pkg ,
159- this ::applyConnectionConfig , this ::startSessionFromConnection , this ::stopTrace );
160- consoleNode = new FridaConsoleNode (this ::removeHook , this ::toggleHook , this ::removeAllHooks , connectionPanel , version );
166+ this ::applyConnectionConfig , this ::startSessionFromConnection , this ::stopTrace , this ::savePathsConfig );
167+ consoleNode = new FridaConsoleNode (this ::removeHook , this ::toggleHook , this ::removeAllHooks ,
168+ connectionPanel , version , this ::applyCustomScriptsFromConsole );
161169 }
162- mainWindow .getTabsController ().openTab (consoleNode );
170+ jadx .gui .ui .tab .TabsController controller = mainWindow .getTabsController ();
171+ controller .openTab (consoleNode );
172+ ContentPanel consolePanelRef = tabs .getTabByNode (consoleNode );
163173 if (focus ) {
164- mainWindow . getTabsController (). selectTab ( consoleNode , true );
174+ selectConsoleTab ( tabs , controller , consoleNode , consolePanelRef );
165175 } else {
166176 if (prevPanel != null && tabs .indexOfComponent (prevPanel ) >= 0 ) {
167177 tabs .setSelectedComponent (prevPanel );
@@ -178,6 +188,7 @@ private void openConsole(boolean focus) {
178188 if (consolePanel != null ) {
179189 consolePanel .setSessionActive (fridaController .isRunning ());
180190 consolePanel .setConnectionVisible (!fridaController .isRunning ());
191+ consolePanel .setCustomScripts (customScriptPaths );
181192 }
182193 if (consolePanel != null && !pendingLogs .isEmpty ()) {
183194 for (String line : pendingLogs ) {
@@ -213,6 +224,30 @@ private void ensureTabComponent(TabbedPane tabs, FridaConsoleNode node) {
213224 }
214225 }
215226
227+ private void selectConsoleTab (TabbedPane tabs , Object controller , JNode node , ContentPanel panel ) {
228+ if (tabs != null && panel != null ) {
229+ try {
230+ tabs .setSelectedComponent (panel );
231+ return ;
232+ } catch (Exception ignored ) {
233+ }
234+ }
235+ if (controller == null || node == null ) {
236+ return ;
237+ }
238+ try {
239+ java .lang .reflect .Method method = controller .getClass ().getMethod ("selectTab" , JNode .class , boolean .class );
240+ method .invoke (controller , node , true );
241+ return ;
242+ } catch (Exception ignored ) {
243+ }
244+ try {
245+ java .lang .reflect .Method method = controller .getClass ().getMethod ("selectTab" , JNode .class );
246+ method .invoke (controller , node );
247+ } catch (Exception ignored ) {
248+ }
249+ }
250+
216251 private void openSettings (ICodeNodeRef ref , boolean patchDefault , boolean showReturnTab ,
217252 boolean focusReturnTab , boolean requireConnection ) {
218253 if (requireConnection && !fridaController .isRunning ()) {
@@ -269,7 +304,7 @@ record = new HookRecord(hookKey, target.getDisplaySignature(), ref);
269304 }
270305 HookSpec spec = new HookSpec (target , lastScriptOptions , patchRule , dialog .getExtraScript (), hookKey );
271306 hookSpecs .put (hookKey , spec );
272- String script = HookScriptGenerator . generateCombined ( getActiveSpecs () );
307+ String script = buildCombinedScript ( );
273308 boolean canReuseNow = fridaController .isRunning ()
274309 && activeSessionConfig != null
275310 && fixedConfig != null
@@ -582,7 +617,7 @@ private String getVersionString() {
582617 }
583618 } catch (Exception ignored ) {
584619 }
585- return "0.1.0 " ;
620+ return "unknown " ;
586621 }
587622
588623 private void showError (String message ) {
@@ -613,6 +648,72 @@ private void applyConnectionConfig(FridaSessionConfig cfg) {
613648 pluginOptions .isTemplateAppend (), pluginOptions .getTemplateName (), pluginOptions .getTemplateContent ());
614649 }
615650
651+ private void applyCustomScriptsFromConsole (String paths ) {
652+ customScriptPaths = paths == null ? "" : paths ;
653+ pluginOptions .setCustomScripts (customScriptPaths );
654+ String script = buildCombinedScript ();
655+ if (consolePanel != null ) {
656+ consolePanel .setScript (script );
657+ } else {
658+ pendingScript = script ;
659+ }
660+ if (fridaController .isRunning ()) {
661+ try {
662+ fridaController .updateSessionScript (script , this ::appendLog );
663+ } catch (IOException e ) {
664+ appendLog ("Failed to update Jarida session: " + e .getMessage ());
665+ }
666+ }
667+ }
668+
669+ private void savePathsConfig (FridaSessionConfig cfg ) {
670+ if (cfg == null ) {
671+ return ;
672+ }
673+ String adb = cfg .getAdbPath ();
674+ String frida = cfg .getFridaPath ();
675+ String fridaPs = cfg .getFridaPsPath ();
676+ try {
677+ if (adb != null && !adb .trim ().isEmpty ()) {
678+ PREFS .put ("path.adb" , adb .trim ());
679+ lastSessionConfig .setAdbPath (adb .trim ());
680+ }
681+ if (frida != null && !frida .trim ().isEmpty ()) {
682+ PREFS .put ("path.frida" , frida .trim ());
683+ lastSessionConfig .setFridaPath (frida .trim ());
684+ }
685+ if (fridaPs != null && !fridaPs .trim ().isEmpty ()) {
686+ PREFS .put ("path.fridaPs" , fridaPs .trim ());
687+ lastSessionConfig .setFridaPsPath (fridaPs .trim ());
688+ }
689+ } catch (Exception ignored ) {
690+ }
691+ pluginOptions .updateFrom (lastSessionConfig , lastScriptOptions ,
692+ pluginOptions .isTemplateAppend (), pluginOptions .getTemplateName (), pluginOptions .getTemplateContent ());
693+ }
694+
695+ private void loadSavedPaths () {
696+ try {
697+ String adb = PREFS .get ("path.adb" , null );
698+ String frida = PREFS .get ("path.frida" , null );
699+ String fridaPs = PREFS .get ("path.fridaPs" , null );
700+ if (adb != null && !adb .trim ().isEmpty ()) {
701+ lastSessionConfig .setAdbPath (adb .trim ());
702+ }
703+ if (frida != null && !frida .trim ().isEmpty ()) {
704+ lastSessionConfig .setFridaPath (frida .trim ());
705+ }
706+ if (fridaPs != null && !fridaPs .trim ().isEmpty ()) {
707+ lastSessionConfig .setFridaPsPath (fridaPs .trim ());
708+ }
709+ if (pluginOptions != null ) {
710+ pluginOptions .updateFrom (lastSessionConfig , lastScriptOptions ,
711+ pluginOptions .isTemplateAppend (), pluginOptions .getTemplateName (), pluginOptions .getTemplateContent ());
712+ }
713+ } catch (Exception ignored ) {
714+ }
715+ }
716+
616717 private void startSessionFromConnection (FridaSessionConfig cfg ) {
617718 if (cfg == null ) {
618719 return ;
@@ -627,7 +728,7 @@ private void startSessionFromConnection(FridaSessionConfig cfg) {
627728 return ;
628729 }
629730 try {
630- String script = HookScriptGenerator . generateCombined ( getActiveSpecs () );
731+ String script = buildCombinedScript ( );
631732 fridaController .startWithScript (cfg , script , this ::appendLog );
632733 activeSessionConfig = cfg ;
633734 if (consolePanel != null ) {
@@ -712,7 +813,7 @@ private void reloadCombinedHooks() {
712813 return ;
713814 }
714815 try {
715- String script = HookScriptGenerator . generateCombined ( getActiveSpecs () );
816+ String script = buildCombinedScript ( );
716817 fridaController .updateSessionScript (script , this ::appendLog );
717818 if (consolePanel != null ) {
718819 consolePanel .setScript (script );
@@ -733,6 +834,66 @@ private java.util.List<HookSpec> getActiveSpecs() {
733834 return active ;
734835 }
735836
837+ private String buildCombinedScript () {
838+ java .util .List <String > globals = new java .util .ArrayList <>();
839+ java .util .List <CustomScriptEntry > scripts = parseCustomScriptPaths (customScriptPaths );
840+ for (CustomScriptEntry entry : scripts ) {
841+ if (!entry .enabled || entry .path .isEmpty ()) {
842+ continue ;
843+ }
844+ try {
845+ java .nio .file .Path p = java .nio .file .Paths .get (entry .path );
846+ String content = java .nio .file .Files .readString (p );
847+ globals .add ("// -- " + entry .path + "\n " + content );
848+ } catch (Exception e ) {
849+ if (missingCustomScripts .add (entry .path )) {
850+ appendLog ("Custom script not found: " + entry .path );
851+ }
852+ }
853+ }
854+ return HookScriptGenerator .generateCombined (getActiveSpecs (), globals );
855+ }
856+
857+ private static final class CustomScriptEntry {
858+ final String path ;
859+ final boolean enabled ;
860+
861+ private CustomScriptEntry (String path , boolean enabled ) {
862+ this .path = path == null ? "" : path .trim ();
863+ this .enabled = enabled ;
864+ }
865+ }
866+
867+ private java .util .List <CustomScriptEntry > parseCustomScriptPaths (String raw ) {
868+ java .util .List <CustomScriptEntry > entries = new java .util .ArrayList <>();
869+ if (raw == null || raw .trim ().isEmpty ()) {
870+ return entries ;
871+ }
872+ String [] lines = raw .split ("\\ R" );
873+ for (String line : lines ) {
874+ if (line == null ) {
875+ continue ;
876+ }
877+ String trimmed = line .trim ();
878+ if (trimmed .isEmpty ()) {
879+ continue ;
880+ }
881+ boolean enabled = true ;
882+ String path = trimmed ;
883+ if (trimmed .startsWith ("1|" ) || trimmed .startsWith ("0|" )) {
884+ enabled = trimmed .startsWith ("1|" );
885+ path = trimmed .substring (2 ).trim ();
886+ } else if (trimmed .startsWith ("[x]" ) || trimmed .startsWith ("[ ]" )) {
887+ enabled = trimmed .startsWith ("[x]" );
888+ path = trimmed .substring (3 ).trim ();
889+ }
890+ if (!path .isEmpty ()) {
891+ entries .add (new CustomScriptEntry (path , enabled ));
892+ }
893+ }
894+ return entries ;
895+ }
896+
736897 private void toggleHook (HookRecord record ) {
737898 if (record == null ) {
738899 return ;
0 commit comments