diff --git a/src/binary-exploitation/common-exploiting-problems.md b/src/binary-exploitation/common-exploiting-problems.md
index 925dc3c534c..a844491acad 100644
--- a/src/binary-exploitation/common-exploiting-problems.md
+++ b/src/binary-exploitation/common-exploiting-problems.md
@@ -35,7 +35,139 @@ In order to bypass this the **escape character `\x16` must be prepended to any `
**Here you can** [**find an example of this behaviour**](https://ir0nstone.gitbook.io/hackthebox/challenges/pwn/dream-diary-chapter-1/unlink-exploit)**.**
-{{#include ../banners/hacktricks-training.md}}
+## Android AArch64 shared-library fuzzing & LD_PRELOAD hooking
+
+When an Android app ships only a stripped AArch64 `.so`, you can still fuzz exported logic directly on-device without rebuilding the APK. A practical workflow:
+
+1. **Locate callable entry points.** `objdump -T libvalidate.so | grep -E "validate"` quickly lists exported functions. Decompilers (Ghidra, IDA, BN) reveal the real signature, e.g. `int validate(const uint8_t *buf, uint64_t len)`.
+2. **Write a standalone harness.** Load a file, keep the buffer alive, and call the exported symbol exactly as the app would. Cross-compile with the NDK (e.g. `aarch64-linux-android21-clang harness.c -L. -lvalidate -fPIE -pie`).
+
+
+Minimal file-driven harness
+
+```c
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern int validate(const uint8_t *buf, uint64_t len);
+
+int main(int argc, char **argv) {
+ if (argc < 2) return 1;
+ int fd = open(argv[1], O_RDONLY);
+ if (fd < 0) return 1;
+ struct stat st = {0};
+ if (fstat(fd, &st) < 0) return 1;
+ uint8_t *buffer = malloc(st.st_size + 1);
+ read(fd, buffer, st.st_size);
+ close(fd);
+ int ret = validate(buffer, st.st_size);
+ free(buffer);
+ return ret;
+}
+```
+
+
+
+3. **Reconstruct the expected structure.** Error strings and comparisons in Ghidra showed the function parsed strict JSON with constant keys (`magic`, `version`, nested `root.children.*`) and arithmetic checks (e.g., `value * 2 == 84` ⇒ `value` must be `42`). Feeding syntactically valid JSON that progressively satisfies each branch lets you map the schema without instrumentation.
+4. **Bypass anti-debug to leak secrets.** Because the `.so` imports `snprintf`, override it with `LD_PRELOAD` to dump sensitive format strings even when breakpoints are blocked:
+
+
+Minimal snprintf leak hook
+
+```c
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+
+typedef int (*vsnprintf_t)(char *, size_t, const char *, va_list);
+
+int snprintf(char *str, size_t size, const char *fmt, ...) {
+ static vsnprintf_t real_vsnprintf;
+ if (!real_vsnprintf)
+ real_vsnprintf = (vsnprintf_t)dlsym(RTLD_NEXT, "vsnprintf");
+
+ va_list args;
+ va_start(args, fmt);
+ va_list args_copy;
+ va_copy(args_copy, args);
+ if (fmt && strstr(fmt, "MHL{")) {
+ fprintf(stdout, "[LD_PRELOAD] flag: ");
+ vfprintf(stdout, fmt, args);
+ fputc('\n', stdout);
+ }
+ int ret = real_vsnprintf(str, size, fmt, args_copy);
+ va_end(args_copy);
+ va_end(args);
+ return ret;
+}
+```
+
+
+
+
+`LD_PRELOAD=./hook.so ./validate_harness payload.json` exfiltrates the internal flag and confirms the crash oracle without patching the binary.
+5. **Shrink the fuzz space.** Disassembly exposed an XOR key reused across the flag comparison, meaning the first seven bytes of `flag` were known. Only fuzz the nine unknown bytes.
+6. **Embed fuzz bytes inside a valid JSON envelope.** The AFL harness reads exactly nine bytes from `stdin`, copies them into the flag suffix, and hard-codes every other field (constants, tree depths, arithmetic preimage). Any malformed read simply exits, so AFL spends cycles on meaningful testcases.
+
+
+AFL-friendly harness for structured JSON
+
+```c
+#include
+#include
+#include
+#include
+
+extern int validate(unsigned char *bytes, size_t len);
+
+#define FUZZ_SIZE 9
+
+int main(void) {
+ uint8_t blob[FUZZ_SIZE];
+ if (read(STDIN_FILENO, blob, FUZZ_SIZE) != FUZZ_SIZE) return 0;
+ char suffix[FUZZ_SIZE + 1];
+ memcpy(suffix, blob, FUZZ_SIZE);
+ suffix[FUZZ_SIZE] = '\0';
+ char json[512];
+ int len = snprintf(json, sizeof(json),
+ "{\"magic\":16909060,\"version\":1,\"padding\":0,\"flag\":\"MHL{827b07c%s}\"," \
+ "\"root\":{\"type\":16,\"level\":3,\"num_children\":1,\"children\":[" \
+ "{\"type\":32,\"level\":2,\"num_children\":1,\"subchildren\":[" \
+ "{\"type\":48,\"level\":1,\"num_children\":1,\"leaves\":[" \
+ "{\"type\":64,\"level\":0,\"reserved\":0,\"value\":42}]}}]}}",
+ suffix);
+ if (len <= 0 || (size_t)len >= sizeof(json)) return 0;
+ validate((unsigned char *)json, len);
+ return 0;
+}
+```
+
+7. **Run AFL with the crash-as-success oracle.** Any input that satisfies every semantic check and guesses the correct nine-byte suffix triggers the deliberate crash; those files land in `output/crashes` and can be replayed through the simple harness to recover the secret.
+This workflow lets you triage anti-debug-protected JNI validators quickly, leak secrets when needed, then fuzz only the meaningful bytes, all without touching the original APK.
+
+### Related pages
+
+{{#ref}}
+../mobile-pentesting/android-app-pentesting/reversing-native-libraries.md
+{{#endref}}
+
+{{#ref}}
+../reversing/reversing-tools-basic-methods/README.md
+{{#endref}}
+
+## References
+
+- [FD duplication exploit example](https://ir0nstone.gitbook.io/notes/types/stack/exploiting-over-sockets/exploit)
+- [Socat delete-character behaviour](https://ir0nstone.gitbook.io/hackthebox/challenges/pwn/dream-diary-chapter-1/unlink-exploit)
+- [FuzzMe – Reverse Engineering and Fuzzing an Android Shared Library](https://hackmd.io/@sal/fuzzme-mobilehackinglab-ctf-writeup)
+
+{{#include ../banners/hacktricks-training.md}}