第二次遇到这种题型,想写篇文章记录一下解题过程

题目描述

 

做题思路

反编译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?}


赛后看到有些师傅的设备可以动调,动调速度真的是能快不少,也是呜呜呜了~
如果觉得对你有帮助,可以点个关注喵~

 

 

 

 

 

 

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐