frooky 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frooky
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Frida-powered hook runner based on JSON hook files.
5
5
  Author-email: Carlos Holguera <holguera.cybersec@gmail.com>, Stefan Bernhardsgrütter <stefan.bernhardsgruetter@redguard.ch>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -711,283 +711,105 @@ Dynamic: license-file
711
711
  |___/
712
712
  ```
713
713
 
714
- A Frida-based dynamic analysis tool for Android and iOS applications.
714
+ `frooky` is a [Frida](https://www.frida.re/)-based dynamic analysis tool for Android and iOS apps based on JSON hook files.
715
+
716
+ [![PyPi](https://badge.fury.io/py/frooky.svg)](https://pypi.python.org/pypi/frooky)
717
+
718
+ - Hook Java/Kotlin methods and native C/C++ functions
719
+ - Simple JSON hook file format
720
+ - Support for method overloads and stack trace capturing
721
+ - Argument capturing with various data types
722
+ - Filtering hooks by argument values or stack trace patterns
723
+ - Output events in JSON Lines format for easy processing
724
+
725
+ See more in [docs/usage.md](docs/usage.md).
715
726
 
716
727
  ## Installation
717
728
 
729
+ Simply install via pip and you'll get the `frooky` CLI tool:
730
+
718
731
  ```bash
719
- pip install frooky
732
+ pip3 install frooky
720
733
  ```
721
734
 
722
735
  ## Usage
723
736
 
737
+ Create a hook file (e.g., `hooks.json`) as described in [docs/usage.md](docs/usage.md), then run `frooky` with the desired options:
738
+
724
739
  ```bash
725
740
  # Attach by app name
726
- frooky -U -n "My App" hooks.json
741
+ frooky -U -n "My App" --platform android hooks.json
727
742
 
728
743
  # Spawn and add multiple hook files (hooks are merged)
729
- frooky -U -f com.example.app hooks.json hooks2.json more_hooks.json
744
+ frooky -U -f com.example.app --platform android storage.json crypto.json
730
745
  ```
731
746
 
732
747
  See `frooky -h` for more options.
733
748
 
734
- ## Hook Files
749
+ ## Example
735
750
 
736
- Hook files use JSON format. When multiple hook files are provided, their `hooks` arrays are merged together.
751
+ We'll use the OWASP MAS [MASTG-DEMO-0072](https://mas.owasp.org/MASTG/demos/android/MASVS-CRYPTO/MASTG-DEMO-0072/MASTG-DEMO-0072/) app to demonstrate hooking a cryptographic key generation method.
737
752
 
738
- ### Basic Structure
753
+ First you need to create a hook file, e.g., `crypto.json`:
739
754
 
740
755
  ```json
741
756
  {
742
- "category": "STORAGE",
757
+ "category": "CRYPTO",
743
758
  "hooks": [
744
759
  {
745
- "class": "com.example.MyClass",
746
- "methods": ["method1", "method2"]
760
+ "class": "android.security.keystore.KeyGenParameterSpec$Builder",
761
+ "method": "$init",
762
+ "maxFrames": 10
747
763
  }
748
764
  ]
749
765
  }
750
766
  ```
751
767
 
752
- ### Java/Kotlin Hooks
753
-
754
- #### Simple Method Hook
755
-
756
- ```json
757
- {
758
- "class": "java.io.File",
759
- "method": "exists"
760
- }
761
- ```
762
-
763
- #### Multiple Methods
764
-
765
- ```json
766
- {
767
- "class": "java.io.FileOutputStream",
768
- "methods": ["write", "close", "flush"]
769
- }
770
- ```
771
-
772
- #### Method Overloads
773
-
774
- Specify exact method signatures using `overloads`:
775
-
776
- ```json
777
- {
778
- "class": "java.io.FileOutputStream",
779
- "method": "write",
780
- "overloads": [
781
- { "args": ["[B"] },
782
- { "args": ["[B", "int", "int"] },
783
- { "args": ["int"] }
784
- ]
785
- }
786
- ```
787
-
788
- #### Stack Traces
789
-
790
- Control stack trace depth with `maxFrames`:
791
-
792
- ```json
793
- {
794
- "class": "javax.crypto.Cipher",
795
- "method": "doFinal",
796
- "maxFrames": 10
797
- }
798
- ```
799
-
800
- ### Native Hooks
801
-
802
- Native hooks intercept C/C++ functions. Set `native: true` and specify the symbol.
803
-
804
- #### Basic Native Hook
805
-
806
- ```json
807
- {
808
- "native": true,
809
- "symbol": "open",
810
- "module": "libc.so"
811
- }
812
- ```
813
-
814
- #### Argument Descriptors
815
-
816
- Define how arguments should be captured:
817
-
818
- ```json
819
- {
820
- "native": true,
821
- "symbol": "write",
822
- "module": "libc.so",
823
- "args": [
824
- { "name": "fd", "type": "int32" },
825
- { "name": "buf", "type": "bytes", "length": 256 },
826
- { "name": "count", "type": "int32" }
827
- ]
828
- }
829
- ```
830
-
831
- #### Dynamic Length from Another Argument
832
-
833
- Use `lengthInArg` to read length from another argument:
834
-
835
- ```json
836
- {
837
- "native": true,
838
- "symbol": "send",
839
- "module": "libc.so",
840
- "args": [
841
- { "name": "sockfd", "type": "int32" },
842
- { "name": "buf", "type": "bytes", "lengthInArg": 2 },
843
- { "name": "len", "type": "int32" },
844
- { "name": "flags", "type": "int32" }
845
- ]
846
- }
847
- ```
848
-
849
- #### Capture Return Values
850
-
851
- Set `returnValue: true` on the last argument:
852
-
853
- ```json
854
- {
855
- "native": true,
856
- "symbol": "read",
857
- "module": "libc.so",
858
- "args": [
859
- { "name": "fd", "type": "int32" },
860
- { "name": "buf", "type": "bytes", "lengthInArg": 2 },
861
- { "name": "count", "type": "int32" },
862
- { "name": "result", "type": "int32", "returnValue": true }
863
- ]
864
- }
865
- ```
866
-
867
- #### Outbound Parameters
868
-
869
- Use `direction: "out"` for output parameters that should be read after the function returns:
768
+ Then run `frooky` with the hook file against your target app:
870
769
 
871
- ```json
872
- {
873
- "native": true,
874
- "symbol": "CCCrypt",
875
- "module": "libcommonCrypto.dylib",
876
- "args": [
877
- { "name": "op", "type": "int32" },
878
- { "name": "alg", "type": "int32" },
879
- { "name": "dataOut", "type": "bytes", "length": 256, "direction": "out" },
880
- { "name": "dataOutMoved", "type": "pointer", "direction": "out" }
881
- ]
882
- }
770
+ ```bash
771
+ frooky -U -n "MASTestApp" --platform android crypto.json
883
772
  ```
884
773
 
885
- #### Filter by Value
774
+ Output (pretty-printed for readability):
886
775
 
887
- Only capture events when arguments match specific values:
776
+ > Events are written to the output file in JSON Lines format (one JSON object per line, known as NDJSON). You can easily pretty-print it e.g. using `jq . output.json`.
888
777
 
889
778
  ```json
890
779
  {
891
- "native": true,
892
- "symbol": "open",
893
- "module": "libc.so",
894
- "args": [
895
- { "name": "pathname", "type": "string", "filter": ["/data/", "/sdcard/"] }
780
+ "id": "14535033-08ea-4063-897c-eacd4a885d8b",
781
+ "type": "hook",
782
+ "category": "CRYPTO",
783
+ "time": "2026-01-14T16:02:21.782Z",
784
+ "class": "android.security.keystore.KeyGenParameterSpec$Builder",
785
+ "method": "$init",
786
+ "instanceId": 35486102,
787
+ "stackTrace": [
788
+ "android.security.keystore.KeyGenParameterSpec$Builder.<init>(Native Method)",
789
+ "org.owasp.mastestapp.MastgTest.generateKey(MastgTest.kt:97)",
790
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:41)",
791
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)",
792
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)",
793
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
794
+ "java.lang.Thread.run(Thread.java:1012)"
795
+ ],
796
+ "inputParameters": [
797
+ {
798
+ "declaredType": "java.lang.String",
799
+ "value": "MultiPurposeKey"
800
+ },
801
+ {
802
+ "declaredType": "int",
803
+ "value": 15
804
+ }
805
+ ],
806
+ "returnValue": [
807
+ {
808
+ "declaredType": "void",
809
+ "value": "void"
810
+ }
896
811
  ]
897
812
  }
898
813
  ```
899
814
 
900
- #### Filter by Stack Trace
901
-
902
- Only capture events when the call stack contains specific patterns:
903
-
904
- ```json
905
- {
906
- "native": true,
907
- "symbol": "SSL_write",
908
- "module": "libssl.so",
909
- "filterEventsByStacktrace": ["com.example.network", "okhttp3"]
910
- }
911
- ```
912
-
913
- #### Debug Mode
914
-
915
- Enable verbose logging for troubleshooting:
916
-
917
- ```json
918
- {
919
- "native": true,
920
- "symbol": "problematic_function",
921
- "module": "libfoo.so",
922
- "debug": true
923
- }
924
- ```
925
-
926
- ### Argument Types
927
-
928
- | Type | Description |
929
- |------|-------------|
930
- | `string` | Null-terminated C string |
931
- | `int32` | 32-bit signed integer |
932
- | `uint32` | 32-bit unsigned integer |
933
- | `int64` | 64-bit signed integer |
934
- | `pointer` | Memory address |
935
- | `bytes` | Raw bytes (requires `length` or `lengthInArg`) |
936
- | `bool` | Boolean value |
937
- | `double` | 64-bit floating point |
938
- | `CFData` | iOS CFData object |
939
- | `CFDictionary` | iOS CFDictionary object |
940
-
941
- ### iOS Objective-C Hooks
942
-
943
- Hook Objective-C methods using `objClass` and `symbol`:
944
-
945
- ```json
946
- {
947
- "native": true,
948
- "objClass": "NSURLSession",
949
- "symbol": "dataTaskWithRequest:completionHandler:"
950
- }
951
- ```
952
-
953
- ## Output Format
954
-
955
- Events are written to the output file in JSON Lines format (one JSON object per line, know as NDJSON). You can easily pretty-print it e.g. using `jq . output.json`.
956
-
957
- Example event (pretty-printed for clarity):
958
-
959
- ```json
960
- {
961
- "id": "0117229c-b034-4676-ba33-075fc27922ba",
962
- "type": "hook",
963
- "category": "STORAGE",
964
- "time": "2026-01-18T16:17:25.470Z",
965
- "class": "android.app.SharedPreferencesImpl$EditorImpl",
966
- "method": "putString",
967
- "instanceId": 268282727,
968
- "stackTrace": [
969
- "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
970
- "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)",
971
- ...
972
- ],
973
- "inputParameters": [
974
- {
975
- "declaredType": "java.lang.String",
976
- "value": "AQMRC7OWD6/h1iJseuzJVrClpwKE8swB8gOrGnsdaN4="
977
- },
978
- {
979
- "declaredType": "java.lang.String",
980
- "value": "AX4R5MZu+J1p0U3hvKyuEnJDQopI+wupiSi8CAG8dzq0PU76NbbebjhqMtqCD7fFUy2SmmQuQVDlDrrj30d3GQes+PlD8HmRFszVTge039GQ"
981
- }
982
- ],
983
- "returnValue": [
984
- {
985
- "declaredType": "android.content.SharedPreferences$Editor",
986
- "value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>",
987
- "runtimeType": "android.app.SharedPreferencesImpl$EditorImpl",
988
- "instanceId": "268282727",
989
- "instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@ffdab67"
990
- }
991
- ]
992
- }
993
- ```
815
+ See more in [docs/usage.md](docs/usage.md) and see a full example in [docs/examples/example.md](docs/examples/example.md).
frooky-0.1.1/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # Frooky
2
+
3
+ ```txt
4
+ ___ ____
5
+ / __\ / _ | _ _ _ _ _ _
6
+ / _\ | (_) | / _ \ / _ \ | / / | | | |
7
+ / / / / | | | (_) | (_) || < | |_| |
8
+ \/ /_/ |_| \___/ \___/ |_|\_\ \__, |
9
+ |___/
10
+ ```
11
+
12
+ `frooky` is a [Frida](https://www.frida.re/)-based dynamic analysis tool for Android and iOS apps based on JSON hook files.
13
+
14
+ [![PyPi](https://badge.fury.io/py/frooky.svg)](https://pypi.python.org/pypi/frooky)
15
+
16
+ - Hook Java/Kotlin methods and native C/C++ functions
17
+ - Simple JSON hook file format
18
+ - Support for method overloads and stack trace capturing
19
+ - Argument capturing with various data types
20
+ - Filtering hooks by argument values or stack trace patterns
21
+ - Output events in JSON Lines format for easy processing
22
+
23
+ See more in [docs/usage.md](docs/usage.md).
24
+
25
+ ## Installation
26
+
27
+ Simply install via pip and you'll get the `frooky` CLI tool:
28
+
29
+ ```bash
30
+ pip3 install frooky
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ Create a hook file (e.g., `hooks.json`) as described in [docs/usage.md](docs/usage.md), then run `frooky` with the desired options:
36
+
37
+ ```bash
38
+ # Attach by app name
39
+ frooky -U -n "My App" --platform android hooks.json
40
+
41
+ # Spawn and add multiple hook files (hooks are merged)
42
+ frooky -U -f com.example.app --platform android storage.json crypto.json
43
+ ```
44
+
45
+ See `frooky -h` for more options.
46
+
47
+ ## Example
48
+
49
+ We'll use the OWASP MAS [MASTG-DEMO-0072](https://mas.owasp.org/MASTG/demos/android/MASVS-CRYPTO/MASTG-DEMO-0072/MASTG-DEMO-0072/) app to demonstrate hooking a cryptographic key generation method.
50
+
51
+ First you need to create a hook file, e.g., `crypto.json`:
52
+
53
+ ```json
54
+ {
55
+ "category": "CRYPTO",
56
+ "hooks": [
57
+ {
58
+ "class": "android.security.keystore.KeyGenParameterSpec$Builder",
59
+ "method": "$init",
60
+ "maxFrames": 10
61
+ }
62
+ ]
63
+ }
64
+ ```
65
+
66
+ Then run `frooky` with the hook file against your target app:
67
+
68
+ ```bash
69
+ frooky -U -n "MASTestApp" --platform android crypto.json
70
+ ```
71
+
72
+ Output (pretty-printed for readability):
73
+
74
+ > Events are written to the output file in JSON Lines format (one JSON object per line, known as NDJSON). You can easily pretty-print it e.g. using `jq . output.json`.
75
+
76
+ ```json
77
+ {
78
+ "id": "14535033-08ea-4063-897c-eacd4a885d8b",
79
+ "type": "hook",
80
+ "category": "CRYPTO",
81
+ "time": "2026-01-14T16:02:21.782Z",
82
+ "class": "android.security.keystore.KeyGenParameterSpec$Builder",
83
+ "method": "$init",
84
+ "instanceId": 35486102,
85
+ "stackTrace": [
86
+ "android.security.keystore.KeyGenParameterSpec$Builder.<init>(Native Method)",
87
+ "org.owasp.mastestapp.MastgTest.generateKey(MastgTest.kt:97)",
88
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:41)",
89
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)",
90
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)",
91
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
92
+ "java.lang.Thread.run(Thread.java:1012)"
93
+ ],
94
+ "inputParameters": [
95
+ {
96
+ "declaredType": "java.lang.String",
97
+ "value": "MultiPurposeKey"
98
+ },
99
+ {
100
+ "declaredType": "int",
101
+ "value": 15
102
+ }
103
+ ],
104
+ "returnValue": [
105
+ {
106
+ "declaredType": "void",
107
+ "value": "void"
108
+ }
109
+ ]
110
+ }
111
+ ```
112
+
113
+ See more in [docs/usage.md](docs/usage.md) and see a full example in [docs/examples/example.md](docs/examples/example.md).
@@ -33,6 +33,11 @@ def build_parser() -> argparse.ArgumentParser:
33
33
  )
34
34
  parser.add_argument("hooks", nargs="+", help="Path(s) to your input hook JSON file(s)")
35
35
  parser.add_argument("-o", "--output", default="output.json", help="Output JSON file")
36
+ parser.add_argument(
37
+ "--keep-artifacts",
38
+ action="store_true",
39
+ help="Keep temporary artifacts (tmp/, node_modules/, package.json, package-lock.json)",
40
+ )
36
41
 
37
42
  return parser
38
43
 
@@ -64,6 +69,7 @@ def main() -> int:
64
69
  attach_identifier=args.attach_identifier,
65
70
  attach_pid=args.attach_pid,
66
71
  spawn=args.spawn,
72
+ keep_artifacts=args.keep_artifacts,
67
73
  )
68
74
 
69
75
  runner = FrookyRunner(options)
@@ -6,6 +6,7 @@ import subprocess
6
6
  import os
7
7
  import sys
8
8
  import json
9
+ import shutil
9
10
  from dataclasses import dataclass
10
11
  from importlib import resources
11
12
  from pathlib import Path
@@ -34,6 +35,7 @@ class RunnerOptions:
34
35
  attach_identifier: Optional[str] = None
35
36
  attach_pid: Optional[int] = None
36
37
  spawn: Optional[str] = None
38
+ keep_artifacts: bool = False
37
39
 
38
40
 
39
41
  class FrookyRunner:
@@ -328,6 +330,25 @@ class FrookyRunner:
328
330
  else:
329
331
  raise RuntimeError("No target specified")
330
332
 
333
+ def _cleanup_artifacts(self) -> None:
334
+ """Remove temporary artifacts created during execution."""
335
+ artifacts = [
336
+ Path("tmp"),
337
+ Path("node_modules"),
338
+ Path("package.json"),
339
+ Path("package-lock.json"),
340
+ ]
341
+
342
+ for artifact in artifacts:
343
+ try:
344
+ if artifact.is_dir():
345
+ shutil.rmtree(artifact)
346
+ elif artifact.is_file():
347
+ artifact.unlink()
348
+ except Exception:
349
+ # Silently ignore cleanup errors
350
+ pass
351
+
331
352
  def run(self) -> int:
332
353
  """Run the Frooky hooks."""
333
354
  try:
@@ -393,5 +414,9 @@ class FrookyRunner:
393
414
  self.session.detach()
394
415
  except Exception:
395
416
  pass
417
+
418
+ # Clean up artifacts unless --keep-artifacts was specified
419
+ if not self.options.keep_artifacts:
420
+ self._cleanup_artifacts()
396
421
 
397
422
  return 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frooky
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Frida-powered hook runner based on JSON hook files.
5
5
  Author-email: Carlos Holguera <holguera.cybersec@gmail.com>, Stefan Bernhardsgrütter <stefan.bernhardsgruetter@redguard.ch>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -711,283 +711,105 @@ Dynamic: license-file
711
711
  |___/
712
712
  ```
713
713
 
714
- A Frida-based dynamic analysis tool for Android and iOS applications.
714
+ `frooky` is a [Frida](https://www.frida.re/)-based dynamic analysis tool for Android and iOS apps based on JSON hook files.
715
+
716
+ [![PyPi](https://badge.fury.io/py/frooky.svg)](https://pypi.python.org/pypi/frooky)
717
+
718
+ - Hook Java/Kotlin methods and native C/C++ functions
719
+ - Simple JSON hook file format
720
+ - Support for method overloads and stack trace capturing
721
+ - Argument capturing with various data types
722
+ - Filtering hooks by argument values or stack trace patterns
723
+ - Output events in JSON Lines format for easy processing
724
+
725
+ See more in [docs/usage.md](docs/usage.md).
715
726
 
716
727
  ## Installation
717
728
 
729
+ Simply install via pip and you'll get the `frooky` CLI tool:
730
+
718
731
  ```bash
719
- pip install frooky
732
+ pip3 install frooky
720
733
  ```
721
734
 
722
735
  ## Usage
723
736
 
737
+ Create a hook file (e.g., `hooks.json`) as described in [docs/usage.md](docs/usage.md), then run `frooky` with the desired options:
738
+
724
739
  ```bash
725
740
  # Attach by app name
726
- frooky -U -n "My App" hooks.json
741
+ frooky -U -n "My App" --platform android hooks.json
727
742
 
728
743
  # Spawn and add multiple hook files (hooks are merged)
729
- frooky -U -f com.example.app hooks.json hooks2.json more_hooks.json
744
+ frooky -U -f com.example.app --platform android storage.json crypto.json
730
745
  ```
731
746
 
732
747
  See `frooky -h` for more options.
733
748
 
734
- ## Hook Files
749
+ ## Example
735
750
 
736
- Hook files use JSON format. When multiple hook files are provided, their `hooks` arrays are merged together.
751
+ We'll use the OWASP MAS [MASTG-DEMO-0072](https://mas.owasp.org/MASTG/demos/android/MASVS-CRYPTO/MASTG-DEMO-0072/MASTG-DEMO-0072/) app to demonstrate hooking a cryptographic key generation method.
737
752
 
738
- ### Basic Structure
753
+ First you need to create a hook file, e.g., `crypto.json`:
739
754
 
740
755
  ```json
741
756
  {
742
- "category": "STORAGE",
757
+ "category": "CRYPTO",
743
758
  "hooks": [
744
759
  {
745
- "class": "com.example.MyClass",
746
- "methods": ["method1", "method2"]
760
+ "class": "android.security.keystore.KeyGenParameterSpec$Builder",
761
+ "method": "$init",
762
+ "maxFrames": 10
747
763
  }
748
764
  ]
749
765
  }
750
766
  ```
751
767
 
752
- ### Java/Kotlin Hooks
753
-
754
- #### Simple Method Hook
755
-
756
- ```json
757
- {
758
- "class": "java.io.File",
759
- "method": "exists"
760
- }
761
- ```
762
-
763
- #### Multiple Methods
764
-
765
- ```json
766
- {
767
- "class": "java.io.FileOutputStream",
768
- "methods": ["write", "close", "flush"]
769
- }
770
- ```
771
-
772
- #### Method Overloads
773
-
774
- Specify exact method signatures using `overloads`:
775
-
776
- ```json
777
- {
778
- "class": "java.io.FileOutputStream",
779
- "method": "write",
780
- "overloads": [
781
- { "args": ["[B"] },
782
- { "args": ["[B", "int", "int"] },
783
- { "args": ["int"] }
784
- ]
785
- }
786
- ```
787
-
788
- #### Stack Traces
789
-
790
- Control stack trace depth with `maxFrames`:
791
-
792
- ```json
793
- {
794
- "class": "javax.crypto.Cipher",
795
- "method": "doFinal",
796
- "maxFrames": 10
797
- }
798
- ```
799
-
800
- ### Native Hooks
801
-
802
- Native hooks intercept C/C++ functions. Set `native: true` and specify the symbol.
803
-
804
- #### Basic Native Hook
805
-
806
- ```json
807
- {
808
- "native": true,
809
- "symbol": "open",
810
- "module": "libc.so"
811
- }
812
- ```
813
-
814
- #### Argument Descriptors
815
-
816
- Define how arguments should be captured:
817
-
818
- ```json
819
- {
820
- "native": true,
821
- "symbol": "write",
822
- "module": "libc.so",
823
- "args": [
824
- { "name": "fd", "type": "int32" },
825
- { "name": "buf", "type": "bytes", "length": 256 },
826
- { "name": "count", "type": "int32" }
827
- ]
828
- }
829
- ```
830
-
831
- #### Dynamic Length from Another Argument
832
-
833
- Use `lengthInArg` to read length from another argument:
834
-
835
- ```json
836
- {
837
- "native": true,
838
- "symbol": "send",
839
- "module": "libc.so",
840
- "args": [
841
- { "name": "sockfd", "type": "int32" },
842
- { "name": "buf", "type": "bytes", "lengthInArg": 2 },
843
- { "name": "len", "type": "int32" },
844
- { "name": "flags", "type": "int32" }
845
- ]
846
- }
847
- ```
848
-
849
- #### Capture Return Values
850
-
851
- Set `returnValue: true` on the last argument:
852
-
853
- ```json
854
- {
855
- "native": true,
856
- "symbol": "read",
857
- "module": "libc.so",
858
- "args": [
859
- { "name": "fd", "type": "int32" },
860
- { "name": "buf", "type": "bytes", "lengthInArg": 2 },
861
- { "name": "count", "type": "int32" },
862
- { "name": "result", "type": "int32", "returnValue": true }
863
- ]
864
- }
865
- ```
866
-
867
- #### Outbound Parameters
868
-
869
- Use `direction: "out"` for output parameters that should be read after the function returns:
768
+ Then run `frooky` with the hook file against your target app:
870
769
 
871
- ```json
872
- {
873
- "native": true,
874
- "symbol": "CCCrypt",
875
- "module": "libcommonCrypto.dylib",
876
- "args": [
877
- { "name": "op", "type": "int32" },
878
- { "name": "alg", "type": "int32" },
879
- { "name": "dataOut", "type": "bytes", "length": 256, "direction": "out" },
880
- { "name": "dataOutMoved", "type": "pointer", "direction": "out" }
881
- ]
882
- }
770
+ ```bash
771
+ frooky -U -n "MASTestApp" --platform android crypto.json
883
772
  ```
884
773
 
885
- #### Filter by Value
774
+ Output (pretty-printed for readability):
886
775
 
887
- Only capture events when arguments match specific values:
776
+ > Events are written to the output file in JSON Lines format (one JSON object per line, known as NDJSON). You can easily pretty-print it e.g. using `jq . output.json`.
888
777
 
889
778
  ```json
890
779
  {
891
- "native": true,
892
- "symbol": "open",
893
- "module": "libc.so",
894
- "args": [
895
- { "name": "pathname", "type": "string", "filter": ["/data/", "/sdcard/"] }
780
+ "id": "14535033-08ea-4063-897c-eacd4a885d8b",
781
+ "type": "hook",
782
+ "category": "CRYPTO",
783
+ "time": "2026-01-14T16:02:21.782Z",
784
+ "class": "android.security.keystore.KeyGenParameterSpec$Builder",
785
+ "method": "$init",
786
+ "instanceId": 35486102,
787
+ "stackTrace": [
788
+ "android.security.keystore.KeyGenParameterSpec$Builder.<init>(Native Method)",
789
+ "org.owasp.mastestapp.MastgTest.generateKey(MastgTest.kt:97)",
790
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:41)",
791
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)",
792
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)",
793
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
794
+ "java.lang.Thread.run(Thread.java:1012)"
795
+ ],
796
+ "inputParameters": [
797
+ {
798
+ "declaredType": "java.lang.String",
799
+ "value": "MultiPurposeKey"
800
+ },
801
+ {
802
+ "declaredType": "int",
803
+ "value": 15
804
+ }
805
+ ],
806
+ "returnValue": [
807
+ {
808
+ "declaredType": "void",
809
+ "value": "void"
810
+ }
896
811
  ]
897
812
  }
898
813
  ```
899
814
 
900
- #### Filter by Stack Trace
901
-
902
- Only capture events when the call stack contains specific patterns:
903
-
904
- ```json
905
- {
906
- "native": true,
907
- "symbol": "SSL_write",
908
- "module": "libssl.so",
909
- "filterEventsByStacktrace": ["com.example.network", "okhttp3"]
910
- }
911
- ```
912
-
913
- #### Debug Mode
914
-
915
- Enable verbose logging for troubleshooting:
916
-
917
- ```json
918
- {
919
- "native": true,
920
- "symbol": "problematic_function",
921
- "module": "libfoo.so",
922
- "debug": true
923
- }
924
- ```
925
-
926
- ### Argument Types
927
-
928
- | Type | Description |
929
- |------|-------------|
930
- | `string` | Null-terminated C string |
931
- | `int32` | 32-bit signed integer |
932
- | `uint32` | 32-bit unsigned integer |
933
- | `int64` | 64-bit signed integer |
934
- | `pointer` | Memory address |
935
- | `bytes` | Raw bytes (requires `length` or `lengthInArg`) |
936
- | `bool` | Boolean value |
937
- | `double` | 64-bit floating point |
938
- | `CFData` | iOS CFData object |
939
- | `CFDictionary` | iOS CFDictionary object |
940
-
941
- ### iOS Objective-C Hooks
942
-
943
- Hook Objective-C methods using `objClass` and `symbol`:
944
-
945
- ```json
946
- {
947
- "native": true,
948
- "objClass": "NSURLSession",
949
- "symbol": "dataTaskWithRequest:completionHandler:"
950
- }
951
- ```
952
-
953
- ## Output Format
954
-
955
- Events are written to the output file in JSON Lines format (one JSON object per line, know as NDJSON). You can easily pretty-print it e.g. using `jq . output.json`.
956
-
957
- Example event (pretty-printed for clarity):
958
-
959
- ```json
960
- {
961
- "id": "0117229c-b034-4676-ba33-075fc27922ba",
962
- "type": "hook",
963
- "category": "STORAGE",
964
- "time": "2026-01-18T16:17:25.470Z",
965
- "class": "android.app.SharedPreferencesImpl$EditorImpl",
966
- "method": "putString",
967
- "instanceId": 268282727,
968
- "stackTrace": [
969
- "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
970
- "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)",
971
- ...
972
- ],
973
- "inputParameters": [
974
- {
975
- "declaredType": "java.lang.String",
976
- "value": "AQMRC7OWD6/h1iJseuzJVrClpwKE8swB8gOrGnsdaN4="
977
- },
978
- {
979
- "declaredType": "java.lang.String",
980
- "value": "AX4R5MZu+J1p0U3hvKyuEnJDQopI+wupiSi8CAG8dzq0PU76NbbebjhqMtqCD7fFUy2SmmQuQVDlDrrj30d3GQes+PlD8HmRFszVTge039GQ"
981
- }
982
- ],
983
- "returnValue": [
984
- {
985
- "declaredType": "android.content.SharedPreferences$Editor",
986
- "value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>",
987
- "runtimeType": "android.app.SharedPreferencesImpl$EditorImpl",
988
- "instanceId": "268282727",
989
- "instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@ffdab67"
990
- }
991
- ]
992
- }
993
- ```
815
+ See more in [docs/usage.md](docs/usage.md) and see a full example in [docs/examples/example.md](docs/examples/example.md).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "frooky"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Frida-powered hook runner based on JSON hook files."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
frooky-0.1.0/README.md DELETED
@@ -1,291 +0,0 @@
1
- # Frooky
2
-
3
- ```txt
4
- ___ ____
5
- / __\ / _ | _ _ _ _ _ _
6
- / _\ | (_) | / _ \ / _ \ | / / | | | |
7
- / / / / | | | (_) | (_) || < | |_| |
8
- \/ /_/ |_| \___/ \___/ |_|\_\ \__, |
9
- |___/
10
- ```
11
-
12
- A Frida-based dynamic analysis tool for Android and iOS applications.
13
-
14
- ## Installation
15
-
16
- ```bash
17
- pip install frooky
18
- ```
19
-
20
- ## Usage
21
-
22
- ```bash
23
- # Attach by app name
24
- frooky -U -n "My App" hooks.json
25
-
26
- # Spawn and add multiple hook files (hooks are merged)
27
- frooky -U -f com.example.app hooks.json hooks2.json more_hooks.json
28
- ```
29
-
30
- See `frooky -h` for more options.
31
-
32
- ## Hook Files
33
-
34
- Hook files use JSON format. When multiple hook files are provided, their `hooks` arrays are merged together.
35
-
36
- ### Basic Structure
37
-
38
- ```json
39
- {
40
- "category": "STORAGE",
41
- "hooks": [
42
- {
43
- "class": "com.example.MyClass",
44
- "methods": ["method1", "method2"]
45
- }
46
- ]
47
- }
48
- ```
49
-
50
- ### Java/Kotlin Hooks
51
-
52
- #### Simple Method Hook
53
-
54
- ```json
55
- {
56
- "class": "java.io.File",
57
- "method": "exists"
58
- }
59
- ```
60
-
61
- #### Multiple Methods
62
-
63
- ```json
64
- {
65
- "class": "java.io.FileOutputStream",
66
- "methods": ["write", "close", "flush"]
67
- }
68
- ```
69
-
70
- #### Method Overloads
71
-
72
- Specify exact method signatures using `overloads`:
73
-
74
- ```json
75
- {
76
- "class": "java.io.FileOutputStream",
77
- "method": "write",
78
- "overloads": [
79
- { "args": ["[B"] },
80
- { "args": ["[B", "int", "int"] },
81
- { "args": ["int"] }
82
- ]
83
- }
84
- ```
85
-
86
- #### Stack Traces
87
-
88
- Control stack trace depth with `maxFrames`:
89
-
90
- ```json
91
- {
92
- "class": "javax.crypto.Cipher",
93
- "method": "doFinal",
94
- "maxFrames": 10
95
- }
96
- ```
97
-
98
- ### Native Hooks
99
-
100
- Native hooks intercept C/C++ functions. Set `native: true` and specify the symbol.
101
-
102
- #### Basic Native Hook
103
-
104
- ```json
105
- {
106
- "native": true,
107
- "symbol": "open",
108
- "module": "libc.so"
109
- }
110
- ```
111
-
112
- #### Argument Descriptors
113
-
114
- Define how arguments should be captured:
115
-
116
- ```json
117
- {
118
- "native": true,
119
- "symbol": "write",
120
- "module": "libc.so",
121
- "args": [
122
- { "name": "fd", "type": "int32" },
123
- { "name": "buf", "type": "bytes", "length": 256 },
124
- { "name": "count", "type": "int32" }
125
- ]
126
- }
127
- ```
128
-
129
- #### Dynamic Length from Another Argument
130
-
131
- Use `lengthInArg` to read length from another argument:
132
-
133
- ```json
134
- {
135
- "native": true,
136
- "symbol": "send",
137
- "module": "libc.so",
138
- "args": [
139
- { "name": "sockfd", "type": "int32" },
140
- { "name": "buf", "type": "bytes", "lengthInArg": 2 },
141
- { "name": "len", "type": "int32" },
142
- { "name": "flags", "type": "int32" }
143
- ]
144
- }
145
- ```
146
-
147
- #### Capture Return Values
148
-
149
- Set `returnValue: true` on the last argument:
150
-
151
- ```json
152
- {
153
- "native": true,
154
- "symbol": "read",
155
- "module": "libc.so",
156
- "args": [
157
- { "name": "fd", "type": "int32" },
158
- { "name": "buf", "type": "bytes", "lengthInArg": 2 },
159
- { "name": "count", "type": "int32" },
160
- { "name": "result", "type": "int32", "returnValue": true }
161
- ]
162
- }
163
- ```
164
-
165
- #### Outbound Parameters
166
-
167
- Use `direction: "out"` for output parameters that should be read after the function returns:
168
-
169
- ```json
170
- {
171
- "native": true,
172
- "symbol": "CCCrypt",
173
- "module": "libcommonCrypto.dylib",
174
- "args": [
175
- { "name": "op", "type": "int32" },
176
- { "name": "alg", "type": "int32" },
177
- { "name": "dataOut", "type": "bytes", "length": 256, "direction": "out" },
178
- { "name": "dataOutMoved", "type": "pointer", "direction": "out" }
179
- ]
180
- }
181
- ```
182
-
183
- #### Filter by Value
184
-
185
- Only capture events when arguments match specific values:
186
-
187
- ```json
188
- {
189
- "native": true,
190
- "symbol": "open",
191
- "module": "libc.so",
192
- "args": [
193
- { "name": "pathname", "type": "string", "filter": ["/data/", "/sdcard/"] }
194
- ]
195
- }
196
- ```
197
-
198
- #### Filter by Stack Trace
199
-
200
- Only capture events when the call stack contains specific patterns:
201
-
202
- ```json
203
- {
204
- "native": true,
205
- "symbol": "SSL_write",
206
- "module": "libssl.so",
207
- "filterEventsByStacktrace": ["com.example.network", "okhttp3"]
208
- }
209
- ```
210
-
211
- #### Debug Mode
212
-
213
- Enable verbose logging for troubleshooting:
214
-
215
- ```json
216
- {
217
- "native": true,
218
- "symbol": "problematic_function",
219
- "module": "libfoo.so",
220
- "debug": true
221
- }
222
- ```
223
-
224
- ### Argument Types
225
-
226
- | Type | Description |
227
- |------|-------------|
228
- | `string` | Null-terminated C string |
229
- | `int32` | 32-bit signed integer |
230
- | `uint32` | 32-bit unsigned integer |
231
- | `int64` | 64-bit signed integer |
232
- | `pointer` | Memory address |
233
- | `bytes` | Raw bytes (requires `length` or `lengthInArg`) |
234
- | `bool` | Boolean value |
235
- | `double` | 64-bit floating point |
236
- | `CFData` | iOS CFData object |
237
- | `CFDictionary` | iOS CFDictionary object |
238
-
239
- ### iOS Objective-C Hooks
240
-
241
- Hook Objective-C methods using `objClass` and `symbol`:
242
-
243
- ```json
244
- {
245
- "native": true,
246
- "objClass": "NSURLSession",
247
- "symbol": "dataTaskWithRequest:completionHandler:"
248
- }
249
- ```
250
-
251
- ## Output Format
252
-
253
- Events are written to the output file in JSON Lines format (one JSON object per line, know as NDJSON). You can easily pretty-print it e.g. using `jq . output.json`.
254
-
255
- Example event (pretty-printed for clarity):
256
-
257
- ```json
258
- {
259
- "id": "0117229c-b034-4676-ba33-075fc27922ba",
260
- "type": "hook",
261
- "category": "STORAGE",
262
- "time": "2026-01-18T16:17:25.470Z",
263
- "class": "android.app.SharedPreferencesImpl$EditorImpl",
264
- "method": "putString",
265
- "instanceId": 268282727,
266
- "stackTrace": [
267
- "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
268
- "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)",
269
- ...
270
- ],
271
- "inputParameters": [
272
- {
273
- "declaredType": "java.lang.String",
274
- "value": "AQMRC7OWD6/h1iJseuzJVrClpwKE8swB8gOrGnsdaN4="
275
- },
276
- {
277
- "declaredType": "java.lang.String",
278
- "value": "AX4R5MZu+J1p0U3hvKyuEnJDQopI+wupiSi8CAG8dzq0PU76NbbebjhqMtqCD7fFUy2SmmQuQVDlDrrj30d3GQes+PlD8HmRFszVTge039GQ"
279
- }
280
- ],
281
- "returnValue": [
282
- {
283
- "declaredType": "android.content.SharedPreferences$Editor",
284
- "value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>",
285
- "runtimeType": "android.app.SharedPreferencesImpl$EditorImpl",
286
- "instanceId": "268282727",
287
- "instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@ffdab67"
288
- }
289
- ]
290
- }
291
- ```
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes