1fn u16le(b: &[u8], off: usize) -> Option<u16> {
14 let s = b.get(off..off + 2)?;
15 Some(u16::from_le_bytes([s[0], s[1]]))
16}
17
18fn u32le(b: &[u8], off: usize) -> Option<u32> {
19 let s = b.get(off..off + 4)?;
20 Some(u32::from_le_bytes([s[0], s[1], s[2], s[3]]))
21}
22
23fn uleb128(b: &[u8], off: &mut usize) -> Option<u32> {
24 let mut result = 0u32;
25 let mut shift = 0u32;
26 loop {
27 let byte = *b.get(*off)?;
28 *off += 1;
29 result |= ((byte & 0x7f) as u32) << shift;
30 if byte & 0x80 == 0 { return Some(result); }
31 shift += 7;
32 if shift >= 35 { return None; }
33 }
34}
35
36const ZIP_EOCD_SIG: [u8; 4] = [0x50, 0x4b, 0x05, 0x06];
39const ZIP_CD_SIG: [u8; 4] = [0x50, 0x4b, 0x01, 0x02];
40const ZIP_LOCAL_SIG: [u8; 4] = [0x50, 0x4b, 0x03, 0x04];
41const ZIP_STORED: u16 = 0;
42
43struct ZipEntry {
44 local_off: usize,
45 comp_size: usize,
46 fname_hash: u64, }
48
49fn djb2(s: &[u8]) -> u64 {
50 let mut h = 5381u64;
51 for &b in s { h = h.wrapping_mul(33).wrapping_add(b as u64); }
52 h
53}
54
55fn zip_find_dex_entries(data: &[u8]) -> Vec<ZipEntry> {
56 let scan_start = data.len().saturating_sub(65558);
58 let eocd_pos = match data[scan_start..]
59 .windows(4)
60 .rposition(|w| w == ZIP_EOCD_SIG)
61 {
62 Some(p) => scan_start + p,
63 None => return Vec::new(),
64 };
65
66 let cd_size = match u32le(data, eocd_pos + 12) { Some(v) => v as usize, None => return Vec::new() };
67 let cd_off = match u32le(data, eocd_pos + 16) { Some(v) => v as usize, None => return Vec::new() };
68
69 let mut pos = cd_off;
70 let cd_end = cd_off.saturating_add(cd_size);
71 let mut entries = Vec::new();
72
73 while pos + 46 <= cd_end && pos + 46 <= data.len() {
74 if data.get(pos..pos + 4) != Some(&ZIP_CD_SIG) { break; }
75
76 let compression = match u16le(data, pos + 10) { Some(v) => v, None => break };
77 let comp_size = match u32le(data, pos + 20) { Some(v) => v as usize, None => break };
78 let local_off = match u32le(data, pos + 42) { Some(v) => v as usize, None => break };
79 let fname_len = match u16le(data, pos + 28) { Some(v) => v as usize, None => break };
80 let extra_len = match u16le(data, pos + 30) { Some(v) => v as usize, None => break };
81 let comment_len = match u16le(data, pos + 32) { Some(v) => v as usize, None => break };
82
83 let fname_end = pos + 46 + fname_len;
84 if fname_end > data.len() { break; }
85 let fname = &data[pos + 46..fname_end];
86
87 let is_dex = fname.starts_with(b"classes") && fname.ends_with(b".dex");
89 if is_dex && compression == ZIP_STORED {
90 entries.push(ZipEntry {
91 local_off,
92 comp_size,
93 fname_hash: djb2(fname),
94 });
95 }
96
97 pos = match pos.checked_add(46 + fname_len + extra_len + comment_len) {
98 Some(v) => v,
99 None => break,
100 };
101 }
102
103 entries.sort_by(|a, b| b.comp_size.cmp(&a.comp_size));
106 entries
107}
108
109fn zip_entry_data<'a>(data: &'a [u8], entry: &ZipEntry) -> Option<&'a [u8]> {
110 let lh = entry.local_off;
111 if data.get(lh..lh + 4) != Some(&ZIP_LOCAL_SIG) { return None; }
112 let fname_len = u16le(data, lh + 26)? as usize;
113 let extra_len = u16le(data, lh + 28)? as usize;
114 let data_start = lh + 30 + fname_len + extra_len;
115 data.get(data_start..data_start + entry.comp_size)
116}
117
118const DEX_MAGIC: &[u8] = b"dex\n";
121
122fn dex_string<'a>(dex: &'a [u8], string_ids_off: usize, idx: usize) -> Option<&'a [u8]> {
123 let str_data_off = u32le(dex, string_ids_off + idx * 4)? as usize;
124 let mut off = str_data_off;
126 loop {
127 let b = *dex.get(off)?;
128 off += 1;
129 if b & 0x80 == 0 { break; }
130 }
131 let start = off;
133 while *dex.get(off)? != 0 { off += 1; }
134 dex.get(start..off)
135}
136
137fn skip_encoded_value(dex: &[u8], off: &mut usize) -> Option<()> {
138 let vbyte = *dex.get(*off)?;
139 *off += 1;
140 let vtype = vbyte & 0x1f;
141 let varg = (vbyte >> 5) as usize;
142 match vtype {
143 0x00 | 0x02 | 0x03 | 0x04 | 0x06 |
145 0x10 | 0x11 | 0x15 | 0x16 | 0x17 |
146 0x18 | 0x19 | 0x1a | 0x1b => {
147 *off = off.checked_add(varg + 1)?;
148 }
149 0x1c => { let size = uleb128(dex, off)?;
151 for _ in 0..size { skip_encoded_value(dex, off)?; }
152 }
153 0x1d => { uleb128(dex, off)?; let size = uleb128(dex, off)?;
156 for _ in 0..size {
157 uleb128(dex, off)?; skip_encoded_value(dex, off)?;
159 }
160 }
161 0x1e | 0x1f => {} _ => return None,
163 }
164 Some(())
165}
166
167fn read_int_encoded_value(dex: &[u8], off: &mut usize) -> Option<u32> {
168 let vbyte = *dex.get(*off)?;
169 *off += 1;
170 let vtype = vbyte & 0x1f;
171 let varg = (vbyte >> 5) as usize;
172 if vtype != 0x04 { return None; }
174 let size = varg + 1;
175 if size > 4 { return None; }
176 let mut val = 0u32;
177 for i in 0..size {
178 val |= (*dex.get(*off + i)? as u32) << (i * 8);
179 }
180 *off += size;
181 Some(val)
182}
183
184fn find_in_dex(dex: &[u8], class_desc: &[u8], field_name: &[u8]) -> Option<u32> {
185 if !dex.starts_with(DEX_MAGIC) || dex.len() < 112 { return None; }
186
187 let string_ids_size = u32le(dex, 56)? as usize;
188 let string_ids_off = u32le(dex, 60)? as usize;
189 let type_ids_size = u32le(dex, 64)? as usize;
190 let type_ids_off = u32le(dex, 68)? as usize;
191 let field_ids_size = u32le(dex, 80)? as usize;
192 let field_ids_off = u32le(dex, 84)? as usize;
193 let class_defs_size = u32le(dex, 96)? as usize;
194 let class_defs_off = u32le(dex, 100)? as usize;
195
196 let mut class_str_idx: Option<usize> = None;
198 for i in 0..string_ids_size {
199 if dex_string(dex, string_ids_off, i) == Some(class_desc) {
200 class_str_idx = Some(i);
201 break;
202 }
203 }
204 let class_str_idx = class_str_idx?;
205
206 let mut class_type_idx: Option<usize> = None;
208 for i in 0..type_ids_size {
209 if u32le(dex, type_ids_off + i * 4)? as usize == class_str_idx {
210 class_type_idx = Some(i);
211 break;
212 }
213 }
214 let class_type_idx = class_type_idx?;
215
216 let mut field_str_idx: Option<u32> = None;
218 for i in 0..string_ids_size {
219 if dex_string(dex, string_ids_off, i) == Some(field_name) {
220 field_str_idx = Some(i as u32);
221 break;
222 }
223 }
224 let field_str_idx = field_str_idx?;
225
226 let mut target_field_idx: Option<u32> = None;
228 for i in 0..field_ids_size {
229 let foff = field_ids_off + i * 8;
230 let fclass = u16le(dex, foff)? as usize;
231 let fname = u32le(dex, foff + 4)?;
232 if fclass == class_type_idx && fname == field_str_idx {
233 target_field_idx = Some(i as u32);
234 break;
235 }
236 }
237 let target_field_idx = target_field_idx?;
238
239 let mut class_data_off = None;
241 let mut static_vals_off = None;
242 for i in 0..class_defs_size {
243 let coff = class_defs_off + i * 32;
244 if u32le(dex, coff)? as usize == class_type_idx {
245 class_data_off = Some(u32le(dex, coff + 24)? as usize);
246 static_vals_off = Some(u32le(dex, coff + 28)? as usize);
247 break;
248 }
249 }
250 let class_data_off = class_data_off?;
251 let static_vals_off = static_vals_off?;
252 if class_data_off == 0 || static_vals_off == 0 { return None; }
253
254 let mut off = class_data_off;
256 let static_fields_size = uleb128(dex, &mut off)?;
257 let _instance_fields = uleb128(dex, &mut off)?;
258 let _direct_methods = uleb128(dex, &mut off)?;
259 let _virtual_methods = uleb128(dex, &mut off)?;
260
261 let mut field_pos: Option<usize> = None;
262 let mut cur_field_idx = 0u32;
263 for i in 0..static_fields_size as usize {
264 let diff = uleb128(dex, &mut off)?;
265 let _access_flags = uleb128(dex, &mut off)?;
266 cur_field_idx += diff;
267 if cur_field_idx == target_field_idx {
268 field_pos = Some(i);
269 break;
270 }
271 }
272 let field_pos = field_pos?;
273
274 let mut sv = static_vals_off;
276 let sv_size = uleb128(dex, &mut sv)? as usize;
277 if field_pos >= sv_size { return None; }
278
279 for i in 0..=field_pos {
280 if i == field_pos {
281 return read_int_encoded_value(dex, &mut sv);
282 }
283 skip_encoded_value(dex, &mut sv)?;
284 }
285 None
286}
287
288pub fn find_transaction_code(jar_path: &str, class_desc: &str, field_name: &str) -> Option<u32> {
298 let data = std::fs::read(jar_path).ok()?;
299 let entries = zip_find_dex_entries(&data);
300 for entry in &entries {
301 if let Some(dex) = zip_entry_data(&data, entry) {
302 if let Some(code) = find_in_dex(dex, class_desc.as_bytes(), field_name.as_bytes()) {
303 return Some(code);
304 }
305 }
306 }
307 None
308}
309
310pub fn resolve_tx_codes_from_dex() -> Option<(u32, u32, u8, u32)> {
318 const JAR: &str = "/system/framework/framework.jar";
319 const AM_STUB: &str = "Landroid/app/IActivityManager$Stub;";
320 const OBS_STUB: &str = "Landroid/app/IProcessObserver$Stub;";
321
322 let observer_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_registerProcessObserver")?;
323 let fg_code = find_transaction_code(JAR, OBS_STUB, "TRANSACTION_onForegroundActivitiesChanged")?;
324
325 if let Some(query_code) = find_transaction_code(JAR, AM_STUB, "TRANSACTION_getFocusedRootTaskInfo") {
326 return Some((observer_code, query_code, 1, fg_code));
327 }
328 let query_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_getFocusedStackInfo")?;
330 Some((observer_code, query_code, 2, fg_code))
331}