2025“湾区杯“网络安全大赛-Reverse-strangeApp-题解
第二次遇到这种题型,想写篇文章记录一下解题过程
题目描述

做题思路
反编译apk

其实看到这里就知道是考dex抽取itemcode还原,因为设备原因,进不去app,本来动调是可以直接把dex完整hook下来的,没环境只能静态打
setData将assets下extract.dat文件读取后传入native,很明显这就是抽出来的dex方法itemcode文件

extract.dat文件

我们只需要去so去看这个文件是怎么被解析回填到原dex的即可

这里可以看出是在加载一个新的so,偏移在0x48DC8,
日志3651 3651 D SWDD : ReadElfHeader -> file_size_ 273008
这里直接把这个加载的elf读下来,接着分析setData的还原逻辑

可以得知回填的结构
struct Payload {
uint32_t code_off; //code offset(需要回填的位置)
uint32_t op_len; //这里注意,so中逻辑是对此值*2作为回填op的长度
uint8_t* opcode; //op的起始位置,截至位置是len*2
};
我们对着extract.dat取第两组数据看看
数据:
18 EF 3B 00 08 00 00 00 70 10 27 AF 00 00 5B 01
22 73 5B 02 23 73 0E 00 38 EF 3B 00 08 00 00 00
54 20 22 73 54 21 23 73 6E 30 63 AD 10 03 0E 00
24 F2 3B 00 04 00 00 00 70 10 27 AF 00 00 0E 00
看第一组
18 EF 3B 00->code_off=0x003BEF18
08 00 00 00->op_len=8*2=16字节
70 10 27 AF 00 00 5B 01 22 73 5B 02 23 73 0E 00->需要回填的opcode 长度16字节
这里就可以看到0E 00在dalvik对应return-void,说明我们是对的
接着看第二组
38 EF 3B 00
08 00 00 00
54 20 22 73 54 21 23 73 6E 30 63 AD 10 03 0E 00
第三组
24 F2 3B 00
04 00 00 00
70 10 27 AF 00 00 0E 00
以此类推
现在只需要用dalvik源码打表,把extract.dat文件完全转为smali即可
dalvik指令集从下面链接取
https://github.com/UchihaL/Indroid/blob/master/libdex/DexOpcodes.h
由于我们只对于extract.dat单个文件进行指令还原,我们还需要取原classes.dex文件,将dex文件里的字符串索引也同样还原过来
下面贴完整的还原脚本
import sys
import struct
from typing import Dict, Tuple, List, Optional
def s1_to_s8(x: int, bits: int) -> int:
sign = 1 << (bits - 1)
mask = (1 << bits) - 1
x &= mask
return (x ^ sign) - sign
def s16(x: int) -> int:
return struct.unpack('<h', struct.pack('<H', x & 0xFFFF))[0]
def s32_from_u2(lo: int, hi: int) -> int:
v = (hi << 16) | lo
return struct.unpack('<i', struct.pack('<I', v))[0]
def s64_from_u2(u1: int, u2_: int, u3: int, u4: int) -> int:
lo = (u2_ << 16) | u1
hi = (u4 << 16) | u3
v = (hi << 32) | lo
return struct.unpack('<q', struct.pack('<Q', v & 0xFFFFFFFFFFFFFFFF))[0]
def hex32(n: int) -> str:
return f"0x{(n & 0xFFFFFFFF):08x}"
def hex16(n: int) -> str:
return f"0x{(n & 0xFFFF):04x}"
def hex8(n: int) -> str:
return f"0x{(n & 0xFF):02x}"
OPCODES: Dict[int, Tuple[str, str, Optional[int], Optional[int]]] = {
0x00: ("nop", "10x", None, None),
0x01: ("move", "12x", None, None),
0x02: ("move/from16", "22x", None, None),
0x03: ("move/16", "32x", None, None),
0x04: ("move-wide", "12x", None, None),
0x05: ("move-wide/from16", "22x", None, None),
0x06: ("move-wide/16", "32x", None, None),
0x07: ("move-object", "12x", None, None),
0x08: ("move-object/from16", "22x", None, None),
0x09: ("move-object/16", "32x", None, None),
0x0a: ("move-result", "11x", None, None),
0x0b: ("move-result-wide", "11x", None, None),
0x0c: ("move-result-object", "11x", None, None),
0x0d: ("move-exception", "11x", None, None),
0x0e: ("return-void", "10x", None, None),
0x0f: ("return", "11x", None, None),
0x10: ("return-wide", "11x", None, None),
0x11: ("return-object", "11x", None, None),
0x12: ("const/4", "11n", None, None),
0x13: ("const/16", "21s", None, None),
0x14: ("const", "31i", None, None),
0x15: ("const/high16", "21ih", None, None),
0x16: ("const-wide/16", "21s", None, None),
0x17: ("const-wide/32", "31i", None, None),
0x18: ("const-wide", "51l", None, None),
0x19: ("const-wide/high16", "21lh", None, None),
0x1a: ("const-string", "21c", 0, None),
0x1b: ("const-string/jumbo", "31c", 0, None),
0x1c: ("const-class", "21c", 1, None),
0x1d: ("monitor-enter", "11x", None, None),
0x1e: ("monitor-exit", "11x", None, None),
0x1f: ("check-cast", "21c", 1, None),
0x20: ("instance-of", "22c", 1, None),
0x21: ("array-length", "12x", None, None),
0x22: ("new-instance", "21c", 1, None),
0x23: ("new-array", "22c", 1, None),
0x24: ("filled-new-array", "35c", 1, None),
0x25: ("filled-new-array/range", "3rc", 1, None),
0x26: ("fill-array-data", "31t", None, None),
0x27: ("throw", "11x", None, None),
0x28: ("goto", "10t", None, None),
0x29: ("goto/16", "20t", None, None),
0x2a: ("goto/32", "30t", None, None),
0x2b: ("packed-switch", "31t", None, None),
0x2c: ("sparse-switch", "31t", None, None),
0x2d: ("cmpl-float", "23x", None, None),
0x2e: ("cmpg-float", "23x", None, None),
0x2f: ("cmpl-double", "23x", None, None),
0x30: ("cmpg-double", "23x", None, None),
0x31: ("cmp-long", "23x", None, None),
0x32: ("if-eq", "22t", None, None),
0x33: ("if-ne", "22t", None, None),
0x34: ("if-lt", "22t", None, None),
0x35: ("if-ge", "22t", None, None),
0x36: ("if-gt", "22t", None, None),
0x37: ("if-le", "22t", None, None),
0x38: ("if-eqz", "21t", None, None),
0x39: ("if-nez", "21t", None, None),
0x3a: ("if-ltz", "21t", None, None),
0x3b: ("if-gez", "21t", None, None),
0x3c: ("if-gtz", "21t", None, None),
0x3d: ("if-lez", "21t", None, None),
0x44: ("aget", "23x", None, None),
0x45: ("aget-wide", "23x", None, None),
0x46: ("aget-object", "23x", None, None),
0x47: ("aget-boolean", "23x", None, None),
0x48: ("aget-byte", "23x", None, None),
0x49: ("aget-char", "23x", None, None),
0x4a: ("aget-short", "23x", None, None),
0x4b: ("aput", "23x", None, None),
0x4c: ("aput-wide", "23x", None, None),
0x4d: ("aput-object", "23x", None, None),
0x4e: ("aput-boolean", "23x", None, None),
0x4f: ("aput-byte", "23x", None, None),
0x50: ("aput-char", "23x", None, None),
0x51: ("aput-short", "23x", None, None),
0x52: ("iget", "22c", 2, None),
0x53: ("iget-wide", "22c", 2, None),
0x54: ("iget-object", "22c", 2, None),
0x55: ("iget-boolean", "22c", 2, None),
0x56: ("iget-byte", "22c", 2, None),
0x57: ("iget-char", "22c", 2, None),
0x58: ("iget-short", "22c", 2, None),
0x59: ("iput", "22c", 2, None),
0x5a: ("iput-wide", "22c", 2, None),
0x5b: ("iput-object", "22c", 2, None),
0x5c: ("iput-boolean", "22c", 2, None),
0x5d: ("iput-byte", "22c", 2, None),
0x5e: ("iput-char", "22c", 2, None),
0x5f: ("iput-short", "22c", 2, None),
0x60: ("sget", "21c", 2, None),
0x61: ("sget-wide", "21c", 2, None),
0x62: ("sget-object", "21c", 2, None),
0x63: ("sget-boolean", "21c", 2, None),
0x64: ("sget-byte", "21c", 2, None),
0x65: ("sget-char", "21c", 2, None),
0x66: ("sget-short", "21c", 2, None),
0x67: ("sput", "21c", 2, None),
0x68: ("sput-wide", "21c", 2, None),
0x69: ("sput-object", "21c", 2, None),
0x6a: ("sput-boolean", "21c", 2, None),
0x6b: ("sput-byte", "21c", 2, None),
0x6c: ("sput-char", "21c", 2, None),
0x6d: ("sput-short", "21c", 2, None),
0x6e: ("invoke-virtual", "35c", 3, None),
0x6f: ("invoke-super", "35c", 3, None),
0x70: ("invoke-direct", "35c", 3, None),
0x71: ("invoke-static", "35c", 3, None),
0x72: ("invoke-interface", "35c", 3, None),
0x74: ("invoke-virtual/range", "3rc", 3, None),
0x75: ("invoke-super/range", "3rc", 3, None),
0x76: ("invoke-direct/range", "3rc", 3, None),
0x77: ("invoke-static/range", "3rc", 3, None),
0x78: ("invoke-interface/range", "3rc", 3, None),
0x7b: ("neg-int", "12x", None, None),
0x7c: ("not-int", "12x", None, None),
0x7d: ("neg-long", "12x", None, None),
0x7e: ("not-long", "12x", None, None),
0x7f: ("neg-float", "12x", None, None),
0x80: ("neg-double", "12x", None, None),
0x81: ("int-to-long", "12x", None, None),
0x82: ("int-to-float", "12x", None, None),
0x83: ("int-to-double", "12x", None, None),
0x84: ("long-to-int", "12x", None, None),
0x85: ("long-to-float", "12x", None, None),
0x86: ("long-to-double", "12x", None, None),
0x87: ("float-to-int", "12x", None, None),
0x88: ("float-to-long", "12x", None, None),
0x89: ("float-to-double", "12x", None, None),
0x8a: ("double-to-int", "12x", None, None),
0x8b: ("double-to-long", "12x", None, None),
0x8c: ("double-to-float", "12x", None, None),
0x8d: ("int-to-byte", "12x", None, None),
0x8e: ("int-to-char", "12x", None, None),
0x8f: ("int-to-short", "12x", None, None),
0x90: ("add-int", "23x", None, None),
0x91: ("sub-int", "23x", None, None),
0x92: ("mul-int", "23x", None, None),
0x93: ("div-int", "23x", None, None),
0x94: ("rem-int", "23x", None, None),
0x95: ("and-int", "23x", None, None),
0x96: ("or-int", "23x", None, None),
0x97: ("xor-int", "23x", None, None),
0x98: ("shl-int", "23x", None, None),
0x99: ("shr-int", "23x", None, None),
0x9a: ("ushr-int", "23x", None, None),
0x9b: ("add-long", "23x", None, None),
0x9c: ("sub-long", "23x", None, None),
0x9d: ("mul-long", "23x", None, None),
0x9e: ("div-long", "23x", None, None),
0x9f: ("rem-long", "23x", None, None),
0xa0: ("and-long", "23x", None, None),
0xa1: ("or-long", "23x", None, None),
0xa2: ("xor-long", "23x", None, None),
0xa3: ("shl-long", "23x", None, None),
0xa4: ("shr-long", "23x", None, None),
0xa5: ("ushr-long", "23x", None, None),
0xa6: ("add-float", "23x", None, None),
0xa7: ("sub-float", "23x", None, None),
0xa8: ("mul-float", "23x", None, None),
0xa9: ("div-float", "23x", None, None),
0xaa: ("rem-float", "23x", None, None),
0xab: ("add-double", "23x", None, None),
0xac: ("sub-double", "23x", None, None),
0xad: ("mul-double", "23x", None, None),
0xae: ("div-double", "23x", None, None),
0xaf: ("rem-double", "23x", None, None),
0xb0: ("add-int/2addr", "12x", None, None),
0xb1: ("sub-int/2addr", "12x", None, None),
0xb2: ("mul-int/2addr", "12x", None, None),
0xb3: ("div-int/2addr", "12x", None, None),
0xb4: ("rem-int/2addr", "12x", None, None),
0xb5: ("and-int/2addr", "12x", None, None),
0xb6: ("or-int/2addr", "12x", None, None),
0xb7: ("xor-int/2addr", "12x", None, None),
0xb8: ("shl-int/2addr", "12x", None, None),
0xb9: ("shr-int/2addr", "12x", None, None),
0xba: ("ushr-int/2addr", "12x", None, None),
0xbb: ("add-long/2addr", "12x", None, None),
0xbc: ("sub-long/2addr", "12x", None, None),
0xbd: ("mul-long/2addr", "12x", None, None),
0xbe: ("div-long/2addr", "12x", None, None),
0xbf: ("rem-long/2addr", "12x", None, None),
0xc0: ("and-long/2addr", "12x", None, None),
0xc1: ("or-long/2addr", "12x", None, None),
0xc2: ("xor-long/2addr", "12x", None, None),
0xc3: ("shl-long/2addr", "12x", None, None),
0xc4: ("shr-long/2addr", "12x", None, None),
0xc5: ("ushr-long/2addr", "12x", None, None),
0xc6: ("add-float/2addr", "12x", None, None),
0xc7: ("sub-float/2addr", "12x", None, None),
0xc8: ("mul-float/2addr", "12x", None, None),
0xc9: ("div-float/2addr", "12x", None, None),
0xca: ("rem-float/2addr", "12x", None, None),
0xcb: ("add-double/2addr", "12x", None, None),
0xcc: ("sub-double/2addr", "12x", None, None),
0xcd: ("mul-double/2addr", "12x", None, None),
0xce: ("div-double/2addr", "12x", None, None),
0xcf: ("rem-double/2addr", "12x", None, None),
0xd0: ("add-int/lit16", "22s", None, None),
0xd1: ("rsub-int", "22s", None, None),
0xd2: ("mul-int/lit16", "22s", None, None),
0xd3: ("div-int/lit16", "22s", None, None),
0xd4: ("rem-int/lit16", "22s", None, None),
0xd5: ("and-int/lit16", "22s", None, None),
0xd6: ("or-int/lit16", "22s", None, None),
0xd7: ("xor-int/lit16", "22s", None, None),
0xd8: ("add-int/lit8", "22b", None, None),
0xd9: ("rsub-int/lit8", "22b", None, None),
0xda: ("mul-int/lit8", "22b", None, None),
0xdb: ("div-int/lit8", "22b", None, None),
0xdc: ("rem-int/lit8", "22b", None, None),
0xdd: ("and-int/lit8", "22b", None, None),
0xde: ("or-int/lit8", "22b", None, None),
0xdf: ("xor-int/lit8", "22b", None, None),
0xe0: ("shl-int/lit8", "22b", None, None),
0xe1: ("shr-int/lit8", "22b", None, None),
0xe2: ("ushr-int/lit8", "22b", None, None),
0x0100: ("packed-switch-payload", "pswitch-payload", None, None),
0x0200: ("sparse-switch-payload", "sswitch-payload", None, None),
0x0300: ("array-payload", "array-payload", None, None),
0xfa: ("invoke-polymorphic", "45cc", 3, 4),
0xfb: ("invoke-polymorphic/range", "4rcc", 3, 4),
0xfc: ("invoke-custom", "35c", 5, None),
0xfd: ("invoke-custom/range", "3rc", 5, None),
0xfe: ("const-method-handle", "21c", 6, None),
0xff: ("const-method-type", "21c", 4, None),
}
def reg_name(r: int, regs_size: int, ins_size: int) -> str:
locals_size = max(0, regs_size - ins_size)
if r >= locals_size:
return f"p{r - locals_size}"
return f"v{r}"
def ref_kind_name(kind: Optional[int]) -> str:
if kind is None:
return "ref"
return {
0: "string",
1: "type",
2: "field",
3: "method",
4: "proto",
5: "call_site",
6: "method_handle",
}.get(kind, "ref")
class DexFile:
def __init__(self, path: str):
with open(path, 'rb') as f:
self.data: bytes = f.read()
self.size = len(self.data)
self._parse_header()
self._parse_primary_tables()
self._parse_map_list()
self._mh_count = 0
self._mh_off = 0
self._cs_count = 0
self._cs_off = 0
if self.map_items.get(0x0008): # TYPE_METHOD_HANDLE_ITEM
self._mh_count, self._mh_off = self.map_items[0x0008]
if self.map_items.get(0x0007): # TYPE_CALL_SITE_ID_ITEM
self._cs_count, self._cs_off = self.map_items[0x0007]
def _u1(self, off: int) -> int:
return self.data[off]
def _u2(self, off: int) -> int:
return struct.unpack_from('<H', self.data, off)[0]
def _u4(self, off: int) -> int:
return struct.unpack_from('<I', self.data, off)[0]
def _bytes(self, off: int, n: int) -> bytes:
return self.data[off:off + n]
def _parse_header(self) -> None:
if self.size < 0x70:
raise ValueError("DEX 文件过小")
self.magic = self._bytes(0, 8)
self.file_size = self._u4(0x20)
self.header_size = self._u4(0x24)
self.endian_tag = self._u4(0x28)
self.map_off = self._u4(0x34)
self.string_ids_size = self._u4(0x38)
self.string_ids_off = self._u4(0x3C)
self.type_ids_size = self._u4(0x40)
self.type_ids_off = self._u4(0x44)
self.proto_ids_size = self._u4(0x48)
self.proto_ids_off = self._u4(0x4C)
self.field_ids_size = self._u4(0x50)
self.field_ids_off = self._u4(0x54)
self.method_ids_size = self._u4(0x58)
self.method_ids_off = self._u4(0x5C)
def _parse_primary_tables(self) -> None:
self._string_offs: List[int] = []
for i in range(self.string_ids_size):
self._string_offs.append(self._u4(self.string_ids_off + i * 4))
self._string_cache: Dict[int, str] = {}
self._type_str_idx: List[int] = []
for i in range(self.type_ids_size):
self._type_str_idx.append(self._u4(self.type_ids_off + i * 4))
self._proto_list: List[Tuple[int, int, int]] = []
for i in range(self.proto_ids_size):
off = self.proto_ids_off + i * 12
shorty_idx = self._u4(off + 0)
return_type_idx = self._u4(off + 4)
parameters_off = self._u4(off + 8)
self._proto_list.append((shorty_idx, return_type_idx, parameters_off))
self._field_list: List[Tuple[int, int, int]] = []
for i in range(self.field_ids_size):
off = self.field_ids_off + i * 8
class_idx = self._u2(off + 0)
type_idx = self._u2(off + 2)
name_idx = self._u4(off + 4)
self._field_list.append((class_idx, type_idx, name_idx))
self._method_list: List[Tuple[int, int, int]] = []
for i in range(self.method_ids_size):
off = self.method_ids_off + i * 8
class_idx = self._u2(off + 0)
proto_idx = self._u2(off + 2)
name_idx = self._u4(off + 4)
self._method_list.append((class_idx, proto_idx, name_idx))
def _parse_map_list(self) -> None:
self.map_items: Dict[int, Tuple[int, int]] = {}
if self.map_off == 0:
return
size = self._u4(self.map_off)
off = self.map_off + 4
for _ in range(size):
if off + 12 > self.size:
break
typ = self._u2(off + 0)
_unused = self._u2(off + 2)
count = self._u4(off + 4)
item_off = self._u4(off + 8)
self.map_items[typ] = (count, item_off)
off += 12
def _uleb128(self, off: int) -> Tuple[int, int]:
result = 0
shift = 0
pos = off
while True:
b = self._u1(pos)
pos += 1
result |= (b & 0x7F) << shift
if (b & 0x80) == 0:
break
shift += 7
return result, pos
def _read_mutf8_at(self, off: int) -> Tuple[str, int]:
_, pos = self._uleb128(off)
chars: List[str] = []
while True:
b0 = self._u1(pos); pos += 1
if b0 == 0x00:
break
if b0 < 0x80:
chars.append(chr(b0))
elif (b0 & 0xE0) == 0xC0:
b1 = self._u1(pos); pos += 1
code = ((b0 & 0x1F) << 6) | (b1 & 0x3F)
if code == 0:
chars.append('\x00')
else:
chars.append(chr(code))
else:
b1 = self._u1(pos); b2 = self._u1(pos + 1); pos += 2
code = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F)
if 0xD800 <= code <= 0xDBFF:
b0n = self._u1(pos); b1n = self._u1(pos + 1); b2n = self._u1(pos + 2); pos += 3
code2 = ((b0n & 0x0F) << 12) | ((b1n & 0x3F) << 6) | (b2n & 0x3F)
if 0xDC00 <= code2 <= 0xDFFF:
cp = 0x10000 + (((code - 0xD800) << 10) | (code2 - 0xDC00))
chars.append(chr(cp))
else:
chars.append(chr(code))
chars.append(chr(code2))
else:
chars.append(chr(code))
return ''.join(chars), pos
def get_string(self, idx: int) -> Optional[str]:
if idx < 0 or idx >= self.string_ids_size:
return None
if idx in self._string_cache:
return self._string_cache[idx]
off = self._string_offs[idx]
if off <= 0 or off >= self.size:
return None
s, _ = self._read_mutf8_at(off)
self._string_cache[idx] = s
return s
def get_type_desc(self, idx: int) -> Optional[str]:
if idx < 0 or idx >= self.type_ids_size:
return None
sidx = self._type_str_idx[idx]
return self.get_string(sidx)
def _get_type_list(self, off: int) -> List[str]:
if off == 0 or off >= self.size:
return []
size = self._u4(off)
types: List[str] = []
p = off + 4
for _ in range(size):
t_idx = self._u2(p)
p += 2
desc = self.get_type_desc(t_idx)
types.append(desc if desc is not None else f"type@{hex16(t_idx)}")
return types
def get_proto_sig(self, idx: int) -> Optional[str]:
if idx < 0 or idx >= self.proto_ids_size:
return None
shorty_idx, ret_type_idx, params_off = self._proto_list[idx]
ret = self.get_type_desc(ret_type_idx) or "V"
params = self._get_type_list(params_off)
return f"({''.join(params)}){ret}"
def get_field_str(self, idx: int) -> Optional[str]:
if idx < 0 or idx >= self.field_ids_size:
return None
class_idx, type_idx, name_idx = self._field_list[idx]
owner = self.get_type_desc(class_idx) or f"type@{hex16(class_idx)}"
ftype = self.get_type_desc(type_idx) or f"type@{hex16(type_idx)}"
name = self.get_string(name_idx) or f"string@{hex32(name_idx)}"
return f"{owner}->{name}:{ftype}"
def get_method_str(self, idx: int) -> Optional[str]:
if idx < 0 or idx >= self.method_ids_size:
return None
class_idx, proto_idx, name_idx = self._method_list[idx]
owner = self.get_type_desc(class_idx) or f"type@{hex16(class_idx)}"
name = self.get_string(name_idx) or f"string@{hex32(name_idx)}"
sig = self.get_proto_sig(proto_idx) or f"proto@{hex16(proto_idx)}"
return f"{owner}->{name}{sig}"
def get_call_site_str(self, idx: int) -> Optional[str]:
if self._cs_off == 0 or idx < 0 or idx >= self._cs_count:
return None
entry_off = self._u4(self._cs_off + idx * 4)
return f"call_site@{hex32(entry_off)}"
def get_method_handle_str(self, idx: int) -> Optional[str]:
if self._mh_off == 0 or idx < 0 or idx >= self._mh_count:
return None
off = self._mh_off + idx * 8
if off + 8 > self.size:
return None
mh_type = self._u2(off + 0)
_unused = self._u2(off + 2)
target_id = self._u2(off + 4)
_unused2 = self._u2(off + 6)
target_field = self.get_field_str(target_id)
target_method = self.get_method_str(target_id)
target = target_method if target_method is not None else target_field
if target is None:
target = f"id@{hex16(target_id)}"
return f"method_handle[kind={mh_type}] {target}"
def format_ref(self, kind: Optional[int], idx: int) -> str:
if kind is None:
return f"ref@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
if kind == 0:
s = self.get_string(idx)
return f"\"{s}\"" if s is not None else f"string@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
if kind == 1:
s = self.get_type_desc(idx)
return s if s is not None else f"type@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
if kind == 2:
s = self.get_field_str(idx)
return s if s is not None else f"field@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
if kind == 3:
s = self.get_method_str(idx)
return s if s is not None else f"method@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
if kind == 4:
s = self.get_proto_sig(idx)
return s if s is not None else f"proto@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
if kind == 5:
s = self.get_call_site_str(idx)
return s if s is not None else f"call_site@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
if kind == 6:
s = self.get_method_handle_str(idx)
return s if s is not None else f"method_handle@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
return f"{ref_kind_name(kind)}@{hex16(idx) if idx <= 0xFFFF else hex32(idx)}"
def decode_block(insns_bytes: bytes, regs_size: int, ins_size: int, dex: Optional[DexFile] = None) -> str:
insns_size = len(insns_bytes) // 2
units = list(struct.unpack('<' + 'H' * insns_size, insns_bytes))
labels_needed: Dict[int, str] = {}
payload_labels: Dict[int, str] = {}
switch_bases: Dict[int, int] = {}
decoded: List[Tuple[int, int, List[str]]] = []
def rn(x: int) -> str:
return reg_name(x, regs_size, ins_size)
def fmt_ref(kind: Optional[int], idx: int, is_32: bool) -> str:
if dex is None:
base = hex32(idx) if is_32 else hex16(idx)
return f"{ref_kind_name(kind)}@{base}"
return dex.format_ref(kind, idx)
def decode_one(pc: int) -> Tuple[int, List[str]]:
u0 = units[pc]
op_lo = u0 & 0xFF
op_hi = (u0 >> 8) & 0xFF
if u0 in (0x0100, 0x0200, 0x0300):
if u0 == 0x0100:
if pc + 4 > insns_size: return (1, ["# truncated packed-switch-payload"])
size = units[pc + 1]
lines = [".packed-switch 0x%08x" % ((units[pc + 3] << 16) | units[pc + 2])]
for _ in range(size):
lines.append(" <tbd>")
lines.append(".end packed-switch")
return (4 + size * 2, lines)
if u0 == 0x0200:
if pc + 2 > insns_size: return (1, ["# truncated sparse-switch-payload"])
size = units[pc + 1]
lines = [".sparse-switch"]
for _ in range(size):
lines.append(" <tbd> -> <tbd>")
lines.append(".end sparse-switch")
return (2 + size * 4, lines)
if u0 == 0x0300:
if pc + 4 > insns_size: return (1, ["# truncated array-payload"])
elem_width = units[pc + 1]
size = (units[pc + 3] << 16) | units[pc + 2]
total_bytes = elem_width * size
cu = (total_bytes + 1) // 2
pad = cu & 1
lines = [f".array-data {elem_width}", ".end array-data"]
return (4 + cu + pad, lines)
info = OPCODES.get(op_lo)
if info is None:
return (1, [f"# unknown-op {hex8(op_lo)}"])
name, fmt, ref1, ref2 = info
a8 = op_hi
out: List[str] = []
size = 1
if fmt == "10x":
out.append(name)
elif fmt == "11x":
out.append(f"{name} {rn(a8)}")
elif fmt == "11n":
A = (a8 >> 4) & 0xF; B = s1_to_s8(a8 & 0xF, 4)
out.append(f"{name} {rn(A)}, {B}")
elif fmt == "12x":
A = (a8 >> 4) & 0xF; B = a8 & 0xF
out.append(f"{name} {rn(A)}, {rn(B)}")
elif fmt == "10t":
off8 = s1_to_s8(a8, 8); tgt = pc + off8
labels_needed.setdefault(tgt, f":L{tgt:04x}")
out.append(f"{name} {labels_needed[tgt]}")
elif fmt == "20t":
size = 2; off16 = s16(units[pc + 1]); tgt = pc + off16
labels_needed.setdefault(tgt, f":L{tgt:04x}")
out.append(f"{name} {labels_needed[tgt]}")
elif fmt == "30t":
size = 3; off32 = s32_from_u2(units[pc + 1], units[pc + 2]); tgt = pc + off32
labels_needed.setdefault(tgt, f":L{tgt:04x}")
out.append(f"{name} {labels_needed[tgt]}")
elif fmt == "21t":
size = 2; A = a8; off16 = s16(units[pc + 1]); tgt = pc + off16
labels_needed.setdefault(tgt, f":L{tgt:04x}")
out.append(f"{name} {rn(A)}, {labels_needed[tgt]}")
elif fmt == "22t":
size = 2; A = (a8 >> 4) & 0xF; B = a8 & 0xF; off16 = s16(units[pc + 1]); tgt = pc + off16
labels_needed.setdefault(tgt, f":L{tgt:04x}")
out.append(f"{name} {rn(A)}, {rn(B)}, {labels_needed[tgt]}")
elif fmt == "21s":
size = 2; A = a8; lit = s16(units[pc + 1])
out.append(f"{name} {rn(A)}, {lit}")
elif fmt == "21ih":
size = 2; A = a8; val = units[pc + 1] << 16
out.append(f"{name} {rn(A)}, {hex(val)}")
elif fmt == "21lh":
size = 2; A = a8; val = units[pc + 1] << 48
out.append(f"{name} {rn(A)}, {hex(val)}")
elif fmt == "31i":
size = 3; A = a8; lit = s32_from_u2(units[pc + 1], units[pc + 2])
out.append(f"{name} {rn(A)}, {hex32(lit)}")
elif fmt == "51l":
size = 5; A = a8; lit = s64_from_u2(units[pc + 1], units[pc + 2], units[pc + 3], units[pc + 4])
out.append(f"{name} {rn(A)}, {hex(lit & 0xFFFFFFFFFFFFFFFF)}")
elif fmt == "21c":
size = 2; A = a8; idx = units[pc + 1]
out.append(f"{name} {rn(A)}, {fmt_ref(ref1, idx, False)}")
elif fmt == "31c":
size = 3; A = a8; idx = (units[pc + 2] << 16) | units[pc + 1]
out.append(f"{name} {rn(A)}, {fmt_ref(ref1, idx, True)}")
elif fmt == "22c":
size = 2; A = (a8 >> 4) & 0xF; B = a8 & 0xF; idx = units[pc + 1]
out.append(f"{name} {rn(A)}, {rn(B)}, {fmt_ref(ref1, idx, False)}")
elif fmt == "22x":
size = 2; A = a8; BBBB = units[pc + 1]
out.append(f"{name} {rn(A)}, {rn(BBBB)}")
elif fmt == "23x":
size = 2; A = a8; B = units[pc + 1] & 0xFF; C = (units[pc + 1] >> 8) & 0xFF
out.append(f"{name} {rn(A)}, {rn(B)}, {rn(C)}")
elif fmt == "22s":
size = 2; A = (a8 >> 4) & 0xF; B = a8 & 0xF; lit = s16(units[pc + 1])
out.append(f"{name} {rn(A)}, {rn(B)}, {lit}")
elif fmt == "22b":
size = 2
A = a8
BC = units[pc + 1]
B = BC & 0xFF
C = s1_to_s8((BC >> 8) & 0xFF, 8)
out.append(f"{name} {rn(A)}, {rn(B)}, {C}")
elif fmt == "32x":
size = 3; AAAA = units[pc + 1]; BBBB = units[pc + 2]
out.append(f"{name} {rn(AAAA)}, {rn(BBBB)}")
elif fmt == "31t":
size = 3; A = a8; off32 = s32_from_u2(units[pc + 1], units[pc + 2]); tgt = pc + off32
payload_labels.setdefault(tgt, f":payload_{tgt:04x}")
switch_bases[tgt] = pc
out.append(f"{name} {rn(A)}, {payload_labels[tgt]}")
elif fmt == "35c":
size = 3; A = (a8 >> 4) & 0xF; G = a8 & 0xF; bbbb = units[pc + 1]; cdef = units[pc + 2]
C = (cdef) & 0xF; D = (cdef >> 4) & 0xF; E = (cdef >> 8) & 0xF; F = (cdef >> 12) & 0xF
regs = [C, D, E, F, G][:A]
out.append(f"{name} {{{', '.join(rn(r) for r in regs)}}}, {fmt_ref(ref1, bbbb, False)}")
elif fmt == "3rc":
size = 3; A = a8; bbbb = units[pc + 1]; cccc = units[pc + 2]
regs = [rn(cccc + i) for i in range(A)]
out.append(f"{name} {{{', '.join(regs)}}}, {fmt_ref(ref1, bbbb, False)}")
elif fmt == "45cc":
size = 4; A = (a8 >> 4) & 0xF; G = a8 & 0xF; bbbb = units[pc + 1]; cdef = units[pc + 2]; hhhh = units[pc + 3]
C = (cdef) & 0xF; D = (cdef >> 4) & 0xF; E = (cdef >> 8) & 0xF; F = (cdef >> 12) & 0xF
regs = [C, D, E, F, G][:A]
out.append(f"{name} {{{', '.join(rn(r) for r in regs)}}}, {fmt_ref(ref1, bbbb, False)}, {fmt_ref(ref2, hhhh, False)}")
elif fmt == "4rcc":
size = 4; A = a8; bbbb = units[pc + 1]; cccc = units[pc + 2]; hhhh = units[pc + 3]
regs = [rn(cccc + i) for i in range(A)]
out.append(f"{name} {{{', '.join(regs)}}}, {fmt_ref(ref1, bbbb, False)}, {fmt_ref(ref2, hhhh, False)}")
else:
out.append(f"# unhandled-format {fmt} ({name})")
return (size, out)
pc = 0
while pc < insns_size:
sz, lines = decode_one(pc)
decoded.append((pc, max(1, sz), lines))
pc += max(1, sz)
for pco in list(payload_labels.keys()):
if 0 <= pco < insns_size:
u0 = units[pco]
if u0 == 0x0100: payload_labels[pco] = f":pswitch_{pco:04x}"
elif u0 == 0x0200: payload_labels[pco] = f":sswitch_{pco:04x}"
elif u0 == 0x0300: payload_labels[pco] = f":array_{pco:04x}"
else: payload_labels[pco] = f":payload_{pco:04x}"
out: List[str] = []
out.append(f".registers {regs_size}")
out.append("")
label_for_pc = {pc: name for pc, name in labels_needed.items()}
label_for_pc.update({pc: name for pc, name in payload_labels.items()})
def render_payload(pc0: int, content_lines: List[str]) -> List[str]:
u0 = units[pc0]
lines: List[str] = []
if u0 == 0x0100:
size = units[pc0 + 1] if pc0 + 1 < insns_size else 0
first_key = s32_from_u2(units[pc0 + 2], units[pc0 + 3]) if pc0 + 3 < insns_size else 0
base_pc = switch_bases.get(pc0, pc0)
lines.append(f".packed-switch {hex32(first_key)}")
for i in range(size):
idx = pc0 + 4 + i * 2
if idx + 1 >= insns_size:
lines.append("# truncated targets"); break
tgt_rel = s32_from_u2(units[idx], units[idx + 1])
tgt_abs = base_pc + tgt_rel
lbl = label_for_pc.get(tgt_abs, f":L{tgt_abs:04x}")
lines.append(f" {lbl}")
lines.append(".end packed-switch")
return lines
if u0 == 0x0200:
size = units[pc0 + 1] if pc0 + 1 < insns_size else 0
base_pc = switch_bases.get(pc0, pc0)
lines.append(".sparse-switch")
for i in range(size):
kp = pc0 + 2 + i * 4
if kp + 3 >= insns_size:
lines.append("# truncated pairs"); break
key = s32_from_u2(units[kp], units[kp + 1])
tgt_rel = s32_from_u2(units[kp + 2], units[kp + 3])
tgt_abs = base_pc + tgt_rel
lbl = label_for_pc.get(tgt_abs, f":L{tgt_abs:04x}")
lines.append(f" {hex32(key)} -> {lbl}")
lines.append(".end sparse-switch")
return lines
if u0 == 0x0300:
elem_width = units[pc0 + 1] if pc0 + 1 < insns_size else 1
size = (units[pc0 + 3] << 16 | units[pc0 + 2]) if pc0 + 3 < insns_size else 0
lines.append(f".array-data {elem_width}")
byte_off = pc0 * 2 + 8
for i in range(size):
start = byte_off + i * elem_width
end = start + elem_width
if end > len(insns_bytes): break
val = int.from_bytes(insns_bytes[start:end], 'little', signed=False)
lines.append(f" {hex(val)}")
lines.append(".end array-data")
return lines
return content_lines
for pc0, sz, content in decoded:
if pc0 in label_for_pc:
out.append(label_for_pc[pc0])
u0 = units[pc0]
if u0 in (0x0100, 0x0200, 0x0300):
out += render_payload(pc0, content)
else:
fixed = []
for ln in content:
if "packed-switch" in ln or "sparse-switch" in ln or "fill-array-data" in ln:
for tgt, lbl in payload_labels.items():
ln = ln.replace(f":payload_{tgt:04x}", lbl)
fixed.append(ln)
out += fixed
return "\n".join(out)
def disasm_pack(path: str, regs_size: int, ins_size: int, dex: Optional[DexFile]) -> str:
with open(path, 'rb') as f:
data = f.read()
pos = 0
idx = 0
out_all: List[str] = []
total = len(data)
while pos + 8 <= total:
off_u32, units = struct.unpack_from('<II', data, pos)
pos += 8
need = units * 2
if units == 0 or pos + need > total:
break
insns = data[pos:pos + need]
pos += need
smali = decode_block(insns, regs_size, ins_size, dex)
out_all.append(f"# record {idx}: file_off={hex(off_u32)} units={units} bytes={need}\n{smali}\n")
idx += 1
if idx == 0:
return "# no records parsed (check file format)"
return "\n".join(out_all)
def main():
path = "extract(1).dat"
dex_path = "classes(1) (10).dex"
regs = 64
ins = 0
dex = DexFile(dex_path)
smali = disasm_pack(path, regs, ins, dex)
lines = [ln for ln in smali.splitlines() if not ln.lstrip().startswith('#')]
with open("out.smali", "w", encoding="utf-8") as f:
f.write("\n".join(lines))
if __name__ == "__main__":
main()
直接可以将extract.dat还原得到out.smali
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
iput-object v0, v1, Lcom/swdd/strangeapp/MainActivity$$ExternalSyntheticLambda0;->f$0:Lcom/swdd/strangeapp/MainActivity;
iput-object v0, v2, Lcom/swdd/strangeapp/MainActivity$$ExternalSyntheticLambda0;->f$1:Landroid/widget/EditText;
return-void
.registers 64
iget-object v2, v0, Lcom/swdd/strangeapp/MainActivity$$ExternalSyntheticLambda0;->f$0:Lcom/swdd/strangeapp/MainActivity;
iget-object v2, v1, Lcom/swdd/strangeapp/MainActivity$$ExternalSyntheticLambda0;->f$1:Landroid/widget/EditText;
invoke-virtual {v0, v1, v3}, Lcom/swdd/strangeapp/MainActivity;->lambda$onCreate$0$com-swdd-strangeapp-MainActivity(Landroid/widget/EditText;Landroid/view/View;)V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-void
.registers 64
const/16 v0, 48
new-array v0, v0, [B
fill-array-data v0, :array_000a
sput-object v0, Lcom/swdd/strangeapp/MainActivity;->TARGET:[B
return-void
:array_000a
.array-data 1
0x76
0x11
0x7
0x7c
0x9d
0x33
0x17
0x85
0xb2
0x17
0xcb
0x1
0x2a
0x6d
0xb3
0x5
0xa9
0xa
0xb3
0x6a
0x4e
0x64
0x7b
0x8a
0xd1
0x1f
0x13
0x38
0x73
0x97
0xf5
0xda
0xee
0xb8
0xc
0x2a
0x11
0x37
0x87
0xd4
0x77
0xd7
0x57
0x76
0x5f
0xb4
0xac
0x45
.end array-data
.registers 64
invoke-direct {v0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V
return-void
.registers 64
if-eqz v4, :L0028
invoke-virtual {v4}, Ljava/lang/String;->isEmpty()Z
move-result v0
if-eqz v0, :L0009
goto :L0028
:L0009
const/4 v0, 0
invoke-virtual {v4, v0}, Ljava/lang/String;->charAt(I)C
move-result v0
xor-int/lit8 v1, v0, 5
int-to-char v1, v1
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
invoke-virtual {v2, v1}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;
move-result-object v2
const/4 v1, 3
invoke-virtual {v4, v3}, Ljava/lang/String;->substring(I)Ljava/lang/String;
move-result-object v3
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v2
return-object v2
:L0028
return-object v4
.registers 64
const-string v0, "1234567891123456"
const-string v1, "1234567891123456"
new-instance v2, Ljavax/crypto/spec/SecretKeySpec;
sget-object v3, Ljava/nio/charset/StandardCharsets;->UTF_8:Ljava/nio/charset/Charset;
invoke-virtual {v0, v3}, Ljava/lang/String;->getBytes(Ljava/nio/charset/Charset;)[B
move-result-object v3
const-string v4, "DES"
invoke-static {v4}, Lcom/swdd/strangeapp/MainActivity;->a(Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
invoke-direct {v2, v3, v4}, Ljavax/crypto/spec/SecretKeySpec;-><init>([BLjava/lang/String;)V
new-instance v3, Ljavax/crypto/spec/IvParameterSpec;
sget-object v4, Ljava/nio/charset/StandardCharsets;->UTF_8:Ljava/nio/charset/Charset;
invoke-virtual {v1, v4}, Ljava/lang/String;->getBytes(Ljava/nio/charset/Charset;)[B
move-result-object v4
invoke-direct {v3, v4}, Ljavax/crypto/spec/IvParameterSpec;-><init>([B)V
const-string v4, "DES/CBC/PKCS5Padding"
invoke-static {v4}, Lcom/swdd/strangeapp/MainActivity;->a(Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
invoke-static {v4}, Ljavax/crypto/Cipher;->getInstance(Ljava/lang/String;)Ljavax/crypto/Cipher;
move-result-object v4
const/4 v1, 5
invoke-virtual {v4, v5, v2, v3}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V
sget-object v5, Ljava/nio/charset/StandardCharsets;->UTF_8:Ljava/nio/charset/Charset;
invoke-virtual {v7, v5}, Ljava/lang/String;->getBytes(Ljava/nio/charset/Charset;)[B
move-result-object v5
invoke-virtual {v4, v5}, Ljavax/crypto/Cipher;->doFinal([B)[B
move-result-object v5
return-object v5
.registers 64
const/4 v0, 0
if-eqz v5, :L001a
if-eqz v6, :L001a
array-length v5, v1
array-length v6, v2
if-eq v2, v1, :L000a
goto :L001a
:L000a
const/4 v0, 1
:L000b
array-length v5, v2
if-ge v2, v1, :L0018
aget-byte v2, v5, v1
aget-byte v3, v6, v1
if-eq v3, v2, :L0015
return v0
:L0015
add-int/lit8 v1, v1, 1
goto :L000b
:L0018
const/4 v1, 0
return v0
:L001a
return v0
.registers 64
new-instance v0, Landroidx/appcompat/app/AlertDialog$Builder;
invoke-direct {v0, v3}, Landroidx/appcompat/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V
invoke-virtual {v0, v4}, Landroidx/appcompat/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroidx/appcompat/app/AlertDialog$Builder;
move-result-object v0
const-string v1, "OK"
const/4 v0, 2
invoke-virtual {v0, v1, v2}, Landroidx/appcompat/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroidx/appcompat/app/AlertDialog$Builder;
move-result-object v0
invoke-virtual {v0}, Landroidx/appcompat/app/AlertDialog$Builder;->show()Landroidx/appcompat/app/AlertDialog;
return-void
.registers 64
invoke-virtual {v5}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;
move-result-object v0
invoke-direct {v4, v0}, Lcom/swdd/strangeapp/MainActivity;->aa(Ljava/lang/String;)[B
move-result-object v1
sget-object v2, Lcom/swdd/strangeapp/MainActivity;->TARGET:[B
invoke-direct {v4, v1, v2}, Lcom/swdd/strangeapp/MainActivity;->compareBytes([B[B)Z
move-result v2
if-eqz v2, :L001a
const-string v2, "Good"
invoke-direct {v4, v2}, Lcom/swdd/strangeapp/MainActivity;->showDialog(Ljava/lang/String;)V
goto :L001f
:L001a
const-string v2, "NO"
invoke-direct {v4, v2}, Lcom/swdd/strangeapp/MainActivity;->showDialog(Ljava/lang/String;)V
:L001f
goto :L003c
move-exception v1
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
const-string/jumbo v3, "加密失败: "
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v1}, Ljava/lang/Exception;->getMessage()Ljava/lang/String;
move-result-object v3
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v2
invoke-direct {v4, v2}, Lcom/swdd/strangeapp/MainActivity;->showDialog(Ljava/lang/String;)V
:L003c
return-void
.registers 64
invoke-super {v3, v4}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
sget v0, Lcom/swdd/strangeapp/R$layout;->activity_main:I
invoke-virtual {v3, v0}, Lcom/swdd/strangeapp/MainActivity;->setContentView(I)V
sget v0, Lcom/swdd/strangeapp/R$id;->inputText:I
invoke-virtual {v3, v0}, Lcom/swdd/strangeapp/MainActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/EditText;
sget v1, Lcom/swdd/strangeapp/R$id;->checkButton:I
invoke-virtual {v3, v1}, Lcom/swdd/strangeapp/MainActivity;->findViewById(I)Landroid/view/View;
move-result-object v1
check-cast v1, Landroid/widget/Button;
new-instance v2, Lcom/swdd/strangeapp/MainActivity$$ExternalSyntheticLambda0;
invoke-direct {v2, v3, v0}, Lcom/swdd/strangeapp/MainActivity$$ExternalSyntheticLambda0;-><init>(Lcom/swdd/strangeapp/MainActivity;Landroid/widget/EditText;)V
invoke-virtual {v1, v2}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
return-void
然后最后一步,smali->java
对着还原来的类
package com.swdd.strangeapp;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MainActivity extends AppCompatActivity {
private static final byte[] TARGET = new byte[]{
(byte) 0x76, 0x11, 0x07, 0x7c, (byte) 0x9d, 0x33, 0x17, (byte) 0x85,
(byte) 0xb2, 0x17, (byte) 0xcb, 0x01, 0x2a, 0x6d, (byte) 0xb3, 0x05,
(byte) 0xa9, 0x0a, (byte) 0xb3, 0x6a, 0x4e, 0x64, 0x7b, (byte) 0x8a,
(byte) 0xd1, 0x1f, 0x13, 0x38, 0x73, (byte) 0x97, (byte) 0xf5, (byte) 0xda,
(byte) 0xee, (byte) 0xb8, 0x0c, 0x2a, 0x11, 0x37, (byte) 0x87, (byte) 0xd4,
0x77, (byte) 0xd7, 0x57, 0x76, 0x5f, (byte) 0xb4, (byte) 0xac, 0x45
};
public static String a(String algo) {
if (algo == null || algo.isEmpty()) {
return algo;
}
char first = algo.charAt(0);
char xored = (char) (first ^ 5);
return new StringBuilder()
.append(xored)
.append(algo.substring(1))
.toString();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText inputText = findViewById(R.id.inputText);
Button checkButton = findViewById(R.id.checkButton);
checkButton.setOnClickListener(v -> m160lambda$onCreate$0$comswddstrangeappMainActivity(inputText, v));
}
void m160lambda$onCreate$0$comswddstrangeappMainActivity(EditText inputText, View v) {
try {
String text = inputText.getText().toString();
byte[] encrypted = aa(text);
boolean ok = compareBytes(encrypted, TARGET);
if (ok) {
showDialog("Good");
} else {
showDialog("NO");
}
} catch (Exception e) {
showDialog("加密失败: " + e.getMessage());
}
}
private byte[] aa(String input) throws Exception {
String keyStr = "1234567891123456";
String ivStr = "1234567891123456";
SecretKeySpec key = new SecretKeySpec(keyStr.getBytes(StandardCharsets.UTF_8), a("DES"));
IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8));
Cipher cipher = Cipher.getInstance(a("DES/CBC/PKCS5Padding"));
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
return cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
}
private boolean compareBytes(byte[] a, byte[] b) {
if (a == null || b == null) return false;
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
private void showDialog(String msg) {
new AlertDialog.Builder(this)
.setMessage(msg)
.setPositiveButton("OK", (DialogInterface dialog, int which) -> {})
.show();
}
}
至此已完全手动静态还原dex抽取壳,有趣的是这里DES的首字符需要xor5,就变成了AES,直接解密得到flag
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
KEY = b"1234567891123456"
IV = b"1234567891123456"
CIPHERTEXT = bytes([
0x76, 0x11, 0x07, 0x7c, 0x9d, 0x33, 0x17, 0x85,
0xb2, 0x17, 0xcb, 0x01, 0x2a, 0x6d, 0xb3, 0x05,
0xa9, 0x0a, 0xb3, 0x6a, 0x4e, 0x64, 0x7b, 0x8a,
0xd1, 0x1f, 0x13, 0x38, 0x73, 0x97, 0xf5, 0xda,
0xee, 0xb8, 0x0c, 0x2a, 0x11, 0x37, 0x87, 0xd4,
0x77, 0xd7, 0x57, 0x76, 0x5f, 0xb4, 0xac, 0x45,
])
cipher = AES.new(KEY, AES.MODE_CBC, IV)
pt = unpad(cipher.decrypt(CIPHERTEXT), AES.block_size)
try:
print(pt.decode("utf-8"))
except UnicodeDecodeError:
print("bytes:", pt)
#flag{just_easy_strange_app_right?}
赛后看到有些师傅的设备可以动调,动调速度真的是能快不少,也是呜呜呜了~
如果觉得对你有帮助,可以点个关注喵~
更多推荐


所有评论(0)