Skip to content

Commit 864e650

Browse files
committed
Restore LPA patches to fix byte twiddling infinite loop
1 parent b74df95 commit 864e650

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed

frida/src/hooks/lpa-hooks.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { loadClass } from "../utils/classloader";
2+
import { createFillStoreMetadataRequestImplementation } from "../utils/lpa-parser";
3+
4+
export const setupLpaByteHooks = (): void => {
5+
console.log("[Frida] Setting up LPA byte twiddling hooks...");
6+
7+
try {
8+
const FillerEngine = loadClass(
9+
"es.com.valid.lib_lpa.controler.FillerEngine"
10+
);
11+
12+
if (FillerEngine?.fillStoreMetadataRequest) {
13+
FillerEngine.fillStoreMetadataRequest.implementation =
14+
createFillStoreMetadataRequestImplementation();
15+
console.log(
16+
"[Frida] Patched FillerEngine.fillStoreMetadataRequest() with corrected BF25 parsing logic"
17+
);
18+
} else {
19+
console.error(
20+
"[Frida] Could not find FillerEngine.fillStoreMetadataRequest to patch"
21+
);
22+
}
23+
} catch (err) {
24+
console.error("[Frida] Error hooking FillerEngine: " + err);
25+
}
26+
};

frida/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { chooseLiveClasses } from "./utils/classloader";
22
import { setupNetworkHooks, setDnsProvider } from "./hooks/network";
33
import { setupCliCallbackHooks } from "./hooks/cli-callbacks";
4+
import { setupLpaByteHooks } from "./hooks/lpa-hooks";
45

56
const initialize = (): void => {
67
setupNetworkHooks();
78
setupCliCallbackHooks();
9+
setupLpaByteHooks();
810

911
const dnsProviderInstances = chooseLiveClasses(
1012
"com.penumbraos.bridge_system.provider.DnsProvider"

frida/src/utils/lpa-parser.ts

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
import { loadClass } from "./classloader";
2+
3+
const MAX_ITERATIONS = 200;
4+
const BF25_TAG = "BF25";
5+
const HEADER_LENGTH_NIBBLES = 4;
6+
7+
const LPA_TAG_ICCID = "5A";
8+
const LPA_TAG_SERVICE_PROVIDER_NAME = "91";
9+
const LPA_TAG_PROFILE_NAME = "92";
10+
const LPA_TAG_ICON_TYPE = "93";
11+
const LPA_TAG_ICON = "94";
12+
const LPA_TAG_PROFILE_CLASS = "95";
13+
const LPA_TAG_PROFILE_POLICY_RULES = "99";
14+
const LPA_TAG_NOTIFICATION_CONFIG = "B6";
15+
const LPA_TAG_OPERATOR_ID = "B7";
16+
17+
export const createFillStoreMetadataRequestImplementation = () => {
18+
return function (this: any, str: string, i: number): any {
19+
console.log(
20+
"[Frida] ----> FE.fillStoreMetadataRequest entered with str_len:" +
21+
(str ? str.length : "null") +
22+
", initial_offset_i:" +
23+
i
24+
);
25+
const startTime = new Date().getTime();
26+
27+
const StoreMetadataRequestClass = loadClass(
28+
"es.com.valid.lib_lpa.dataClasses.StoreMetadataRequest"
29+
);
30+
const ControlerUtil = loadClass("es.com.valid.lib_lpa.controler.Util");
31+
32+
if (!StoreMetadataRequestClass) {
33+
throw new Error("StoreMetadataRequest class not found");
34+
}
35+
if (!ControlerUtil) {
36+
throw new Error("ControlerUtil class not found");
37+
}
38+
39+
let storeMetadataRequest = StoreMetadataRequestClass.$new();
40+
41+
if (!str || str.length < 4) {
42+
console.error("[Frida] Input string is too short.");
43+
throw new Error("Input string too short for BF25 processing.");
44+
}
45+
46+
let topLevelTag = str.substring(i, i + 4).toUpperCase();
47+
if (topLevelTag !== BF25_TAG) {
48+
console.error(
49+
"[Frida] Expected BF25 tag at offset " +
50+
i +
51+
", got: " +
52+
topLevelTag +
53+
". Calling original."
54+
);
55+
return this.constructor.prototype.fillStoreMetadataRequest.call(
56+
this,
57+
str,
58+
i
59+
);
60+
}
61+
62+
let headerLenNibbles = HEADER_LENGTH_NIBBLES;
63+
// Use the correctly loaded ControlerUtil
64+
let lengthOfBF25Bytes = ControlerUtil.getBERLengthInInt(
65+
str,
66+
i + headerLenNibbles
67+
);
68+
let lengthFieldSizeNibbles = ControlerUtil.getBERLengthSizeInNibbles(
69+
str,
70+
i + headerLenNibbles
71+
);
72+
73+
let dataStartOffsetInNibbles =
74+
i + headerLenNibbles + lengthFieldSizeNibbles;
75+
let endOfBF25DataNibbles = dataStartOffsetInNibbles + lengthOfBF25Bytes * 2;
76+
77+
console.log(
78+
"[Frida] BF25 tag found. Declared length: " +
79+
lengthOfBF25Bytes +
80+
" bytes. Data starts at nibble-offset: " +
81+
dataStartOffsetInNibbles +
82+
", ends at nibble-offset: " +
83+
endOfBF25DataNibbles +
84+
" (relative to start of string)."
85+
);
86+
87+
// Current parsing offset within BF25 data content
88+
let currentParseOffsetNibbles = dataStartOffsetInNibbles;
89+
90+
let iterationCount = 0;
91+
92+
while (
93+
currentParseOffsetNibbles < endOfBF25DataNibbles &&
94+
iterationCount < MAX_ITERATIONS
95+
) {
96+
iterationCount++;
97+
if (currentParseOffsetNibbles + 2 > str.length) {
98+
console.error(
99+
"[Frida] Offset " +
100+
currentParseOffsetNibbles +
101+
" out of bounds for reading tag (str.length " +
102+
str.length +
103+
")"
104+
);
105+
break;
106+
}
107+
let currentTag = str
108+
.substring(currentParseOffsetNibbles, currentParseOffsetNibbles + 2)
109+
.toUpperCase();
110+
console.log(
111+
"[Frida] Loop: " +
112+
iterationCount +
113+
", OffsetInBF25Data: " +
114+
(currentParseOffsetNibbles - dataStartOffsetInNibbles) +
115+
" (abs: " +
116+
currentParseOffsetNibbles +
117+
"), Tag: " +
118+
currentTag
119+
);
120+
121+
let tagCompletelyConsumedNibbles = 0;
122+
123+
try {
124+
if (currentTag === LPA_TAG_ICCID) {
125+
let iccidObj = this.fillIccid(str, currentParseOffsetNibbles);
126+
tagCompletelyConsumedNibbles = iccidObj.getSize();
127+
storeMetadataRequest.setIccid(iccidObj);
128+
} else if (currentTag === LPA_TAG_SERVICE_PROVIDER_NAME) {
129+
let hexStrObj = this.fillHexString(str, currentParseOffsetNibbles);
130+
tagCompletelyConsumedNibbles = hexStrObj.getSize();
131+
storeMetadataRequest.setServiceProviderName(hexStrObj);
132+
} else if (currentTag === LPA_TAG_PROFILE_NAME) {
133+
let hexStrObj = this.fillHexString(str, currentParseOffsetNibbles);
134+
tagCompletelyConsumedNibbles = hexStrObj.getSize();
135+
storeMetadataRequest.setProfileName(hexStrObj);
136+
} else if (currentTag === LPA_TAG_ICON_TYPE) {
137+
let iconTypeObj = this.fillIconType(str, currentParseOffsetNibbles);
138+
tagCompletelyConsumedNibbles = iconTypeObj.getSize();
139+
storeMetadataRequest.setIconType(iconTypeObj);
140+
} else if (currentTag === LPA_TAG_ICON) {
141+
let iconBytes = this.fillIcon(str, currentParseOffsetNibbles);
142+
let lenOfIconBytes = ControlerUtil.getBERLengthInInt(
143+
str,
144+
currentParseOffsetNibbles + 2
145+
);
146+
let lenFieldSizeForIcon = ControlerUtil.getBERLengthSizeInNibbles(
147+
str,
148+
currentParseOffsetNibbles + 2
149+
);
150+
tagCompletelyConsumedNibbles =
151+
2 + lenFieldSizeForIcon + lenOfIconBytes * 2;
152+
storeMetadataRequest.setIcon(iconBytes);
153+
} else if (currentTag === LPA_TAG_PROFILE_CLASS) {
154+
let profileClassObj = this.fillProfileClass(
155+
str,
156+
currentParseOffsetNibbles
157+
);
158+
tagCompletelyConsumedNibbles = profileClassObj.getSize();
159+
storeMetadataRequest.setProfileClass(profileClassObj);
160+
} else if (currentTag === LPA_TAG_PROFILE_POLICY_RULES) {
161+
let pprIdsObj = this.fillPprIds(str, currentParseOffsetNibbles);
162+
tagCompletelyConsumedNibbles = pprIdsObj.getSize();
163+
storeMetadataRequest.setProfilePolicyRules(pprIdsObj);
164+
} else if (currentTag === LPA_TAG_NOTIFICATION_CONFIG) {
165+
let notifConfigArray = this.fillNotificationConfigurationInfo(
166+
str,
167+
currentParseOffsetNibbles
168+
);
169+
storeMetadataRequest.setNotificationConfigurationInfo(
170+
notifConfigArray
171+
);
172+
let lenOfB6Bytes = ControlerUtil.getBERLengthInInt(
173+
str,
174+
currentParseOffsetNibbles + 2
175+
);
176+
let lenFieldSizeForB6 = ControlerUtil.getBERLengthSizeInNibbles(
177+
str,
178+
currentParseOffsetNibbles + 2
179+
);
180+
tagCompletelyConsumedNibbles =
181+
2 + lenFieldSizeForB6 + lenOfB6Bytes * 2;
182+
} else if (currentTag === LPA_TAG_OPERATOR_ID) {
183+
let operatorIdObj = this.fillOperatorId(
184+
str,
185+
currentParseOffsetNibbles
186+
);
187+
tagCompletelyConsumedNibbles = operatorIdObj.getSize();
188+
storeMetadataRequest.setProfileOwner(operatorIdObj);
189+
} else {
190+
console.warn(
191+
"[Frida] Unhandled Tag: " +
192+
currentTag +
193+
" at offset " +
194+
currentParseOffsetNibbles +
195+
". Attempting to skip."
196+
);
197+
if (currentParseOffsetNibbles + 4 > str.length) {
198+
console.error(
199+
"[Frida] Not enough data to parse length of unhandled tag " +
200+
currentTag +
201+
". Breaking."
202+
);
203+
break;
204+
}
205+
let lenOfUnhandledBytes = ControlerUtil.getBERLengthInInt(
206+
str,
207+
currentParseOffsetNibbles + 2
208+
);
209+
let lenFieldSizeUnhandled = ControlerUtil.getBERLengthSizeInNibbles(
210+
str,
211+
currentParseOffsetNibbles + 2
212+
);
213+
tagCompletelyConsumedNibbles =
214+
2 + lenFieldSizeUnhandled + lenOfUnhandledBytes * 2;
215+
console.log(
216+
"[Frida] Skipped " +
217+
tagCompletelyConsumedNibbles +
218+
" nibbles for unhandled tag " +
219+
currentTag
220+
);
221+
}
222+
223+
if (tagCompletelyConsumedNibbles > 0) {
224+
currentParseOffsetNibbles += tagCompletelyConsumedNibbles;
225+
} else {
226+
console.error(
227+
"[Frida] Tag " +
228+
currentTag +
229+
" was not processed correctly (consumed 0 nibbles). Breaking loop to prevent infinite loop."
230+
);
231+
break;
232+
}
233+
} catch (e) {
234+
console.error(
235+
"[Frida] Error processing tag " +
236+
currentTag +
237+
" at offset " +
238+
currentParseOffsetNibbles +
239+
": " +
240+
e
241+
);
242+
console.error("[Frida] Stack: " + (e as Error).stack);
243+
try {
244+
if (currentParseOffsetNibbles + 4 <= str.length) {
245+
let lenOfUnhandledBytes = ControlerUtil.getBERLengthInInt(
246+
str,
247+
currentParseOffsetNibbles + 2
248+
);
249+
let lenFieldSizeUnhandled = ControlerUtil.getBERLengthSizeInNibbles(
250+
str,
251+
currentParseOffsetNibbles + 2
252+
);
253+
let skippedNibbles =
254+
2 + lenFieldSizeUnhandled + lenOfUnhandledBytes * 2;
255+
console.warn(
256+
"[Frida] Attempting to skip " +
257+
skippedNibbles +
258+
" nibbles after error on tag " +
259+
currentTag
260+
);
261+
currentParseOffsetNibbles += skippedNibbles;
262+
} else {
263+
console.error(
264+
"[Frida] Not enough data to skip tag " +
265+
currentTag +
266+
" after error. Breaking."
267+
);
268+
break;
269+
}
270+
} catch (skipError) {
271+
console.error(
272+
"[Frida] Error while trying to skip tag " +
273+
currentTag +
274+
" after initial error: " +
275+
skipError +
276+
". Breaking."
277+
);
278+
break;
279+
}
280+
}
281+
}
282+
283+
if (iterationCount >= MAX_ITERATIONS) {
284+
console.warn(
285+
"[Frida] Max iterations (" +
286+
MAX_ITERATIONS +
287+
") reached for fillStoreMetadataRequest loop."
288+
);
289+
}
290+
const duration = new Date().getTime() - startTime;
291+
console.log(
292+
"[Frida] <---- FE.fillStoreMetadataRequest exited. Duration: " +
293+
duration +
294+
"ms. Processed " +
295+
iterationCount +
296+
" tags."
297+
);
298+
return storeMetadataRequest;
299+
};
300+
};

0 commit comments

Comments
 (0)