From 5ed8b3c1a5b0aaca925d590552852e756a22c65a Mon Sep 17 00:00:00 2001 From: Felix <24938145+Nxtmaster10@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:25:47 +0200 Subject: [PATCH] fix: terminate dumpsys adb multiline values at structural lines (#842) * fix: terminate dumpsys adb multiline values at structural lines * fix dumpsys ADB multiline boundaries --------- Co-authored-by: Janik Besendorf --- src/mvt/android/artifacts/dumpsys_adb.py | 21 +++++- tests/android/test_artifact_dumpsys_adb.py | 60 ++++++++++++++++++ .../android_data/dumpsys_adb_wifi.txt | Bin 0 -> 2037 bytes 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 tests/artifacts/android_data/dumpsys_adb_wifi.txt diff --git a/src/mvt/android/artifacts/dumpsys_adb.py b/src/mvt/android/artifacts/dumpsys_adb.py index d2a33fb..e5438c9 100644 --- a/src/mvt/android/artifacts/dumpsys_adb.py +++ b/src/mvt/android/artifacts/dumpsys_adb.py @@ -13,6 +13,13 @@ from .artifact import AndroidArtifact class DumpsysADBArtifact(AndroidArtifact): multiline_fields = ["user_keys", "keystore"] + @staticmethod + def is_structural_line(key: str, vals: list) -> bool: + if key == "}": + return True + # XML keystore continuations also split on "=", but never into an identifier. + return len(vals) == 2 and key.isidentifier() + def indented_dump_parser(self, dump_data): """ Parse the indented dumpsys output, generated by DualDumpOutputStream in Android. @@ -41,10 +48,18 @@ class DumpsysADBArtifact(AndroidArtifact): if key == "": # If the line is empty, it's the terminator for the multiline value in_multiline = False - stack.pop() - else: + if isinstance(stack[-1], list): + stack.pop() + continue + + if not self.is_structural_line(key, vals): current_dict.append(line.lstrip()) - continue + continue + + in_multiline = False + if isinstance(stack[-1], list): + stack.pop() + current_dict = stack[-1] if key == "}": stack.pop() diff --git a/tests/android/test_artifact_dumpsys_adb.py b/tests/android/test_artifact_dumpsys_adb.py index 1d3d76b..47dc3df 100644 --- a/tests/android/test_artifact_dumpsys_adb.py +++ b/tests/android/test_artifact_dumpsys_adb.py @@ -30,6 +30,66 @@ class TestDumpsysADBArtifact: ) assert user_key["user"] == "user@linux" + def test_parsing_adb_wifi(self): + da_adb = DumpsysADBArtifact() + file = get_artifact("android_data/dumpsys_adb_wifi.txt") + with open(file, "rb") as f: + data = f.read() + + da_adb.parse(data) + + assert len(da_adb.results) == 1 + adb_data = da_adb.results[0] + assert "user_keys" in adb_data + assert len(adb_data["user_keys"]) == 1 + + user_key = adb_data["user_keys"][0] + assert ( + user_key["fingerprint"] == "F0:A1:3D:8C:B3:F4:7B:09:9F:EE:8B:D8:38:2E:BD:C6" + ) + assert user_key["user"] == "user@linux" + + # The adb_wifi block following the keystore is not part of the keystore. + assert b"adb_wifi" not in adb_data["keystore"] + + def test_parsing_multiline_terminated_by_structural_line(self): + dump_data = ( + b"debugging_manager={\n" + b" keystore=ABX\x00\x0bkeyStore\x00\x02\x11\n" + b" connected_to_adb=true\n" + b" adb_wifi={\n" + b" enabled=false\n" + b" tls_port=0\n" + b" }\n" + ) + + parsed = DumpsysADBArtifact().indented_dump_parser(dump_data) + + debugging_manager = parsed["debugging_manager"] + assert debugging_manager["keystore"] == [b"ABX\x00\x0bkeyStore\x00\x02\x11"] + assert debugging_manager["connected_to_adb"] == b"true" + assert debugging_manager["adb_wifi"] == { + "enabled": b"false", + "tls_port": b"0", + } + + def test_parsing_multiline_terminated_by_closing_brace(self): + dump_data = ( + b"debugging_manager={\n" + b" keystore=ABX\x00\x0bkeyStore\x00\x02\x11\n" + b"}\n" + b"other={\n" + b" value=true\n" + b"}\n" + ) + + parsed = DumpsysADBArtifact().indented_dump_parser(dump_data) + + assert parsed["debugging_manager"]["keystore"] == [ + b"ABX\x00\x0bkeyStore\x00\x02\x11" + ] + assert parsed["other"] == {"value": b"true"} + def test_parsing_adb_xml(self): da_adb = DumpsysADBArtifact() file = get_artifact("android_data/dumpsys_adb_xml.txt") diff --git a/tests/artifacts/android_data/dumpsys_adb_wifi.txt b/tests/artifacts/android_data/dumpsys_adb_wifi.txt new file mode 100644 index 0000000000000000000000000000000000000000..ecd7f159c378d80eb525055a5dc35632d44033ac GIT binary patch literal 2037 zcmeH|*^c8x6oxZ{1Sl5>-oTe2)5L3HTZ%N^5+_dL@t#CN!Ap6IV<+C-s2>gSNW210 zx*6b}D>S82sq~*kRnqzOJw5%WYb==CkC3u}bWyWbya+kI%ZyopgSPCBU5Xl{8>}Hh z-~09DTK45V{sS}qbqqm1aO*IPv+#6rGA9JZ{H{WfmuDI9D&U{0{4}J`tD**vNa~c; z=?wZ)0Xz^lz@IM=hD8}%U?`s9aArX;Gmb%MhF&nD$WR<3Pz+8nn8Jk+9 zF6CThhxO6qN-IvJp(T)US2XI{&&U<5!aoIKbjY@+_`;`C}B zkwF+``ihT=5HY)*l6ZW#J&?KMR%B|g4n(wuW3-8H-VpcJ3%0R|37ufAcsec!hv08Fm2Q;e+v9Ot|v9?!2u4a zimGOs)6kJKJwCUdS)243mfD-hXO`p}=eAHAzv{BEjupA?cd%9@OAbDo%GB`$u+$0; z!-|GYFf}$NslP*7cON?~h7G@==_MNb#4g^g+r*IjRKl|wH@l_>jagwOrn(o2NJ!qW zQl_9xogno?eql%jGPl!E4>_0)HJ(3_R(hlCZqJvET~nu*|Q7 zQP}3Kg%{mNeaj>Mes7uHwz=wAj6~fgWT})~t_{ShDIw|i%6@$fRXQV5Kakih)$hC6 z&alquF-}>Zbz7t8x3!*%JI^w5bY+=ZiNjYsZIsLGZ0e$z5w?KSm$23vN+l0djf^&d zBr7zLOH-+FLx7OvRo{dJMcYh^)Ik-#){p2m>i=pQIaQ|$N&-VBq*yDAohjaAJ9U%NG zULF>BH>kgKhoAA$dwewd?%|`s3#T>?;x`&W5Xc-i9as}MY5B?_2+Ba!=EeEsmSybE zA9a6=|J360G~lbe)9kJ0Uwx{yJYDmmnj?=MFT?pVei?s4G=%(wAZQ7-P6<^Jfc&~} ns^RHS9t`(SME!Aiq(6`paw-PG5R7<2@Fxs~P?SMt42Jy)NcfnA literal 0 HcmV?d00001