乐于分享
好东西不私藏

一款可分享密码的解压缩 app 分析学习

一款可分享密码的解压缩 app 分析学习

解压专家 (apk)

时间不是很充裕,有一些细节没有完善,有兴趣的可以深入分析、比如后续 GOT 链接这些的...本人也是刚学。

分析一款可以共享密码的解压缩 app,其中用到了 native 的知识点,本文将介绍两种 native 解密方法。

java 层

publicstaticvoidcheckPasswordFromServer(String s, Context context0, FindPasswordCallback zipUtil$FindPasswordCallback0) {newThread(newRunnable() {@Overridepublicvoidrun() {varhashMap0=newHashMap();DocumentFileBeandocumentFileBean0= DocumentFileManager.getFileBean(context0, s);if(documentFileBean0 != null) {Strings= documentFileBean0.getFileName();longv= documentFileBean0.getFileSize();                hashMap0.put("fileName", s);                hashMap0.put("fileSize", v);Strings1= ZMCoreUnZipWrapper.getQPWAT(context0, s + v);try {                    hashMap0.put("md5", FileUtil.getFileMd5(s));                }catch(Exception exception0) {                    exception0.printStackTrace();                }vargson0=newGson();Strings2= gson0.toJson(hashMap0);varokHttpClient0=newOkHttpClient();try {varsecureRandom0=newSecureRandom();byte[] arr_b = newbyte[16];                    secureRandom0.nextBytes(arr_b);varivParameterSpec0=newIvParameterSpec(arr_b);varsecretKeySpec0=newSecretKeySpec("EyR2JvBXJXaUdY9auxetvhpEeQ8DmC6L".getBytes("UTF-8"), "AES");Strings3= gson0.toJson(newZipCheckInfo(Base64.encodeToString(ZipUtil.GeneralEncrypt(s2.getBytes("UTF-8"), secretKeySpec0, ivParameterSpec0), 2), Base64.encodeToString(arr_b, 2)));RequestBodyrequestBody0= RequestBody.create(ZipUtil.JSON, s3);                    okHttpClient0.newCall(newBuilder().url("https://file.unisapps.com/api/v3/search/info").addHeader("User-Client-Access", s1).addHeader("appid""zipa").addHeader("version", CommonUtil.getVersionName(context0)).addHeader("platform""android").post(requestBody0).build()).enqueue(newCallback() {@Override// okhttp3.CallbackpublicvoidonFailure(Call call0, IOException iOException0) {                            Log.d("onFailure""onFailure---" + iOException0.getMessage());                            com.fileunzip.zxwknight.utils.ZipUtil.8.this.val$callback.findPassword("");                        }@Override// okhttp3.CallbackpublicvoidonResponse(Call call0, Response response0)throws IOException {if(response0.isSuccessful()) {if(response0.code() != 200) {                                    com.fileunzip.zxwknight.utils.ZipUtil.8.this.val$callback.findPassword("");return;                                }try {Strings= response0.body().string();Classclass0=newHashMap().getClass();varmap0= (Map)gson0.fromJson(s, class0);byte[] arr_b = Base64.decode(((String)map0.get("data")), 2);varivParameterSpec0=newIvParameterSpec(Base64.decode(((String)map0.get("iv")), 2));vars1=newString(ZipUtil.GeneralDecrypt(arr_b, secretKeySpec0, ivParameterSpec0), "UTF-8");newHashMap();Classclass1= map0.getClass();vars2= (String)((Map)gson0.fromJson(s1, class1)).get("pd");                                    com.fileunzip.zxwknight.utils.ZipUtil.8.this.val$callback.findPassword(s2);                                }catch(Exception unused_ex) {                                    com.fileunzip.zxwknight.utils.ZipUtil.8.this.val$callback.findPassword("");                                }                                Log.d("map""map");return;                            }                            com.fileunzip.zxwknight.utils.ZipUtil.8.this.val$callback.findPassword("");                        }                    });                }catch(Exception exception1) {                    exception1.printStackTrace();                }return;            }            zipUtil$FindPasswordCallback0.findPassword("");        }    }).start();}

getVersionName

publicstaticStringgetVersionName(Context context0) {try {return context0.getApplicationContext().getPackageManager().getPackageInfo("com.fileunzip.zxwknight"0).versionName;    }catch(PackageManager.NameNotFoundException packageManager$NameNotFoundException0) {        packageManager$NameNotFoundException0.printStackTrace();return"";    }}

User-Client-Access

Strings= documentFileBean0.getFileName();longv= documentFileBean0.getFileSize();hashMap0.put("fileName", s);hashMap0.put("fileSize", v);Strings1= ZMCoreUnZipWrapper.getQPWAT(context0, s + v);

其中 getQPWAT 调用 libZMCoreUnZip.so 的 executeGBS native 方法

publicstaticStringgetQPWAT(Context context0, String s) {returnZMCoreUnZipZipApi.executeGBS(s, context0);}publicclassZMCoreUnZipZipApi {static {System.loadLibrary("ZMCoreUnZip");    }publicstatic native int executeConfig(String arg0, ZMCoreUnZipZipExtractCallback arg1, String arg2, Context arg3, int arg4) {    }publicstatic native StringexecuteGBS(String arg0, Context arg1) {    }publicstatic native StringgetVersionInfo() {    }}

native 层

静态分析+vibe

发现能识别 elf,但是 export 符号很奇怪,不是可见字符,且指示条不正常。

发现 JNI_OnLoad export 不存在,所以看 init_array

发现不是很好找,这里根据 elf 结构我们可以直接根据 Elf64_Dyn 类型引用定位到 ELF Dynamic Information 然后找到 DT_INIT_ARRAY。

随后发现 init_array 均为空值 0,说明需要重定向。

查看 DT_RELA 元素为 Elf64_Rela

structElf64_Rela{unsigned __int64 r_offset;unsigned __int64 r_info;  __int64 r_addend;};

注意我们需要 r_info == 0x403 的项目,且 r_offset 需要位于 [DT_INIT_ARRAY, DT_INIT_ARRAY + DT_INIT_ARRAYSZ) 之中,即 [0x55DB00, 0x55DB00+0x20)。

所以 init_array 为

0x549504// init10x5495B4// init20x549D08// init30

可以通过编写以下脚本来应用重定向

from `ELF RELA Relocation Table`base =0table= [    (0x55DB000x549504),    (0x55DB080x5495B4),    (0x55DB100x549D08),    (0x55DB200x549490),    (0x55DB280x54949C),    (0x55DE300x55F5B0),    (0x55DE680x55F5A8),    (0x55DE700x55E008),    (0x55DEA00x553A88),    (0x55DEB00x557268),    (0x55DEC00x55F550),    (0x55DED00x54B288),    (0x55DEE00x55F5C0),    (0x55DF100x55E130),    (0x55DF600x55723C),    (0x55DF780x55FA30),    (0x55DF900x55FB88),    (0x55DFB00x55FB50),    (0x55DFC00x54AFE0),    (0x55DFD00x55F5B8),    (0x55DFD80x54B070),    (0x55DFE00x55F9E0),    (0x55F5B00x63BF68),    (0x55F5B80),]def main():    import utils_idaforoldnewintable:        print(f"patch {hex(old)} -> {hex(base + new)}")        utils_ida.DelItem(old8)        utils_ida.CreateQword(old)        utils_ida.Patch(old, (base +new).to_bytes(8'little'))        utils_ida.SetType(old, "void (*)(void)")if __name__ == "__main__":    main()

借助 ai 分析,能够找到关键全局变量并创建结构体。

#pragma pack(push, 2)structloader_meta_t{unsigned __int64 image_header_off;unsigned __int64 program_headers_off;unsigned __int64 dynsym_off;unsigned __int64 dynstr_off;unsigned __int64 unknown_table_20_off;unsigned __int64 rela_off;unsigned __int64 jmprel_off;unsigned __int64 optional_table_off;unsigned __int64 dynamic_off;unsigned __int64 aux_src_off;unsigned __int64 callback_or_code_off;unsigned __int64 loader_entry_off;unsigned __int64 android_reloc_off;unsignedint phnum;unsignedint unknown_6C;unsignedint rela_count;unsignedint jmprel_count;unsignedint android_reloc_size;unsignedint payload_seed0;unsignedint flags;unsignedint packed_size;unsignedint aux_size;unsignedint init_stage;unsignedint dynsym_seed0;unsignedint sym_index_begin;unsignedint sym_index_end;unsignedint dynsym_seed3;unsignedint resource_flags;unsignedint loader_word_A4;int gate_beg;int gate_end;unsignedint split_begin;unsignedint split_end;};#pragma pack(pop)
.data:000000000063BF68 ; loader_meta_t loader_meta.data:000000000063BF68 loader_meta     DCQ 0                   ; image_header_off.data:000000000063BF70                 DCQ 0                   ; program_headers_off.data:000000000063BF78                 DCQ 0x150               ; dynsym_off.data:000000000063BF80                 DCQ 0x5BA5E8            ; dynstr_off.data:000000000063BF88                 DCQ 0xE70               ; unknown_table_20_off.data:000000000063BF90                 DCQ 0xE70               ; rela_off.data:000000000063BF98                 DCQ 0x6CB58             ; jmprel_off.data:000000000063BFA0                 DCQ 0                   ; optional_table_off.data:000000000063BFA8                 DCQ 0x6CB58             ; dynamic_off.data:000000000063BFB0                 DCQ 0xB8                ; aux_src_off.data:000000000063BFB8                 DCQ 0x4B8290            ; callback_or_code_off.data:000000000063BFC0                 DCQ 0x5A68F8            ; loader_entry_off.data:000000000063BFC8                 DCQ 0                   ; android_reloc_off.data:000000000063BFD0                 DCD 6                   ; phnum.data:000000000063BFD4                 DCD 0                   ; unknown_6C.data:000000000063BFD8                 DCD 0x47DF              ; rela_count.data:000000000063BFDC                 DCD 0                   ; jmprel_count.data:000000000063BFE0                 DCD 0                   ; android_reloc_size.data:000000000063BFE4                 DCD 0x80B35FC2          ; payload_seed0.data:000000000063BFE8                 DCD 3                   ; flags.data:000000000063BFEC                 DCD 0x5062E0            ; packed_size.data:000000000063BFF0                 DCD 0x6CC18             ; aux_size.data:000000000063BFF4                 DCD 5                   ; init_stage.data:000000000063BFF8                 DCD 0                   ; dynsym_seed0.data:000000000063BFFC                 DCD 0x3A                ; sym_index_begin.data:000000000063C000                 DCD 0x2F28              ; sym_index_end.data:000000000063C004                 DCD 0x815C0             ; dynsym_seed3.data:000000000063C008                 DCD 0                   ; resource_flags.data:000000000063C00C                 DCD 0                   ; loader_word_A4.data:000000000063C010                 DCD 0xFFFFFFFF          ; gate_beg.data:000000000063C014                 DCD 0xFFFFFFFF          ; gate_end.data:000000000063C018                 DCD 0                   ; split_begin.data:000000000063C01C                 DCD 0                   ; split_end

三个 init 函数分别有以下功能

init1 负责环境探测

init2 负责恢复 dynsym、dynstr

init2-> 解析 DT_SYMTAB / DT_STRTAB / DT_HASH-> init2_run_symbol_decode_context-> gp_decode_D1 = dynstr_dynsym_decode_Impl->DispatchToSymbolDecode(...)-> dynstr_dynsym_decode_Impl

注意到:

dynstr_dynsym_decode_Impl 的解密过程

dynstr_dynsym_decode_Impl @ 0x54D6B8 做两件事:先解 .dynstr,再修 .dynsym

// Decodes runtime .dynstr (dword-XOR stream) and patches Elf64_Sym entries by XOR-adjusting st_value/st_size for a selected symbol range.__int64 __fastcall dynstr_dynsym_decode_Impl(        _BYTE *dynsym_base,unsignedint i_1,unsignedint i_2,        _DWORD *pDynStr,unsignedint dynstrLen,int a6){  __int64 i; // x4int key; // w6int v9; // w5  Elf64_Sym *sym; // x0  __int64 v11; // x4  __int64 v12; // x8  Elf64_Sym *endSym; // x2unsigned __int64 st_value; // x1unsigned __int64 st_value_1; // x3unsigned __int64 st_size; // x1if ( a6 == 0 )  {if ( dynstrLen != 0 )    {      i = 0;      key = 0x56312342;do      {        v9 = pDynStr[i] ^ key;        key += i * 4;        pDynStr[i++] = v9;      }while ( dynstrLen > (unsignedint)(i * 4) );    }    sym = (Elf64_Sym *)&dynsym_base[24 * i_1];if ( i_1 < i_2 )    {      v11 = i_1 + 0x151;      v12 = i_1 + 0x15B;      endSym = &sym[i_2 - 1 - i_1 + 1];do      {        st_value = sym->st_value;        st_value_1 = st_value ^ v11;if ( st_value != 0 )        {          st_size = sym->st_size;          sym->st_value = st_value_1;          sym->st_size = st_size ^ v12;        }        ++sym;      }while ( sym != endSym );    }  }return1;}

给出修复脚本

deffix_dynstr():    key = 0x56312342    start = int('00000000005BA5E8'16)    end = int('000000000063BBBC'16)    utils_ida.DelItem(start, end - start)    b = utils_ida.GetBytesFromEA(start, end - start)print(f"patch {hex(start)} -> {hex(end)}, length: {len(b)}")assertlen(b) % 4 == 0"length must be multiple of 4"    new_b = bytearray()for i inrange(0len(b) // 4):        v = int.from_bytes(b[i*4:i*4+4], 'little')        new_v = v ^ (key & 0xFFFFFFFF)        key = (key + i * 4) & 0xFFFFFFFF        new_b += new_v.to_bytes(4'little')    utils_ida.Patch(start, new_b)deffix_dynsym():    start = int('000000000055FD38'16)    end = int('00000000005A68F8'16# dynsym 结束与 HASH 表之前    b = utils_ida.GetBytesFromEA(start, end - start)assertlen(b) % 0x18 == 0"length must be multiple of 0x18"    new_b = bytearray()    the_start = 0for i inrange(0len(b) // 0x18):        st_name, st_info, st_other, st_shndx, st_value, st_size = struct.unpack('<IBBHQQ', b[i * 0x18: (i + 1) * 0x18])# skip external symbolif st_shndx != 0:if the_start == 0:                the_start = iassert the_start == 58"the_start must be 58"            st_value ^= (the_start + 0x151) & 0xFFFFFFFFFFFFFFFF            st_size ^= (the_start + 0x15B) & 0xFFFFFFFFFFFFFFFF        new_b += struct.pack('<IBBHQQ', st_name, st_info, st_other, st_shndx, st_value, st_size)    utils_ida.Patch(start, new_b)if __name__ == "__main__":    fix_dynstr()    fix_dynsym()

init3 负责还原代码

init3-> decode_dyn_decode_api_table           // 解密 dyn_decode_api_table 函数-> dyn_decode_api_table-> load1_build_runtime_context-> load2_load_packed_segments->DispatchTo(core_decode)       // flags bit1-> core_decode

load2_load_packed_segments

函数概括为六个阶段:

  • 从 ELF header 后定位主 packed payload,并定位 payload 尾部的 trailer
  • 根据 flags 获取可选字符串或构造外部 key
  • 第一次调用 core_decode,恢复 metadata 后方的 ELF 链接信息块
  • 以链接信息块为基址回填 Program Header、.dynsym.dynamic 和 relocation 地址
  • 第二次调用 core_decode,把主 packed payload 还原为带 8 字节长度头的 zlib 包
  • 分配独立输出缓冲,调用 zlib_uncompress2_wrapper,再按 PT_LOAD 解释解压结果
flowchart TD    A["从 image_base 定位 packed_src"]    B["准备 packed_dst 与 ext_key"]    C["解码 `meta+0x48` 数据块"]    D{"flags"}    E["bit5: fixed XOR"]    F["bit1: core_decode"]    G["读取 unpacked_size / compressed_size"]    H["解压为最终 ELF 内存镜像"]    I["回填 elf_parse_ctx_t"]    A --> B --> C --> D    D --> E --> G    D --> F --> G    D -->|无解码标志| G    G --> H --> I

RuntimeMallocRuntimeFreeRuntimeMemcpy 等名称对应 API 表里的 libc 函数;AcquireFlag4String/AcquireFlag8String/AcquireFlag10String 只是按控制它们的 flag 命名,具体业务语义仍未确认。

// load2_load_packed_segments @ 0x54C944uint32_t load2_load_packed_segments(    elf_parse_ctx_t *ctx,    loader_meta_t *meta,    uint8_t *image_base) {    uint32_t flags = meta->flags;    uint32_t packed_size = meta->packed_size;    uint32_t meta_block_size = meta->meta_block_size;    ctx->image_header = image_base + meta->image_header_off;    ctx->phnum = meta->phnum;    ctx->dynsym_seed0 = meta->dynsym_seed0;const uint8_t *meta_block_src = (const uint8_t *)meta + meta->meta_block_off;    uint8_t *meta_block_dst = RuntimeMalloc(meta_block_size);PrepareResourceState(meta->resource_flags);    uint64_t payload_off = sizeof(Elf64_Ehdr) +image_ehdr(image_base)->e_phnum * sizeof(Elf64_Phdr);    uint8_t *packed_src = image_base + payload_off;    uint64_t *trailer = (uint64_t *)align_up(        (uintptr_t)packed_src + packed_size,8    );    uint8_t *packed_dst = packed_src;if ((flags & 0x01) != 0) {        packed_dst = RuntimeMalloc(packed_size);        ctx->owns_packed_buffer = true;    }// X23 = align_up(packed_src + packed_size, 8)。// 当前样本 X23 = 0x506470,且 *(uint64_t *)X23 == 0。    uint64_t descriptor_rel = *(uint64_t *)trailer;constchar *descriptor = descriptor_rel != 0        ? (constchar *)trailer + descriptor_rel        : NULL;ReleaseOldDescriptorCache(ctx);if (descriptor != NULL) {        ctx->descriptor_len = RuntimeStrlen(descriptor);        ctx->descriptor_copy = RuntimeMalloc(ctx->descriptor_len + 1);RuntimeMemcpy(ctx->descriptor_copy, descriptor, ctx->descriptor_len + 1);    }// X21/X26/X28 是三个受 flags bit2/bit3/bit4 控制的可选字符串。// 当前 flags=3,三个分支均不执行,因此三者静态结果都为 NULL。constchar *key_source_bit2 = NULL; // X21constchar *key_source_bit3 = NULL; // X26constchar *key_source_bit4 = NULL; // X28if ((flags & 0x04) != 0) {        key_source_bit2 = AcquireFlag4String();if (!ValidateFlag4String(key_source_bit2)) {            key_source_bit2 = NULL;        }    }if ((flags & 0x08) != 0) {        key_source_bit3 = AcquireFlag8String();    }if ((flags & 0x10) != 0) {        key_source_bit4 = AcquireFlag10String(ctx->descriptor_copy);if (!ValidateFlag10String(key_source_bit4)) {            key_source_bit4 = NULL;        }    }    size_t ext_key_len =SafeStrlen(key_source_bit2) +SafeStrlen(key_source_bit3) +SafeStrlen(key_source_bit4);if (RuntimeFallbackKeyEnabled()) {        ext_key = RuntimeMalloc(11);RuntimeMemset(ext_key, 011);RuntimeMemset(ext_key, 0x5C8);    } elseif (ext_key_len != 0) {        ext_key = RuntimeMalloc(ext_key_len + 1);RuntimeMemset(ext_key, 0, ext_key_len + 1);AppendString(ext_key, key_source_bit2);AppendString(ext_key, key_source_bit3);AppendString(ext_key, key_source_bit4);    }// meta+0x48 指向的数据块始终先通过 core_decode。// 这是 load3 的第一个 decode callsite,不受主 packed payload 的 bit1/bit5 分流控制。if (core_decode_dispatcher(            meta_block_src,            meta_block_size,            meta_block_dst,            meta_block_size,            -1,            -1,            ext_key        ) != meta_block_size) {RuntimeFree(ext_key);return0;    }    ctx->decoded_meta_block = meta_block_dst;    ctx->decoded_meta_block_size = meta_block_size;if ((flags & 0x01) != 0) {// 独立目标缓冲路径:packed_src -> packed_dst。if ((flags & 0x20) != 0) {xor_decode_fixed_key_blocks(                packed_dst,                packed_src,                packed_size            );        } elseif ((flags & 0x02) != 0) {if (NeedsSplitSourceRebuild(meta, payload_off, packed_size)) {                uint8_t *joined_src = RuntimeMalloc(packed_size);                uint32_t joined_len = RebuildSplitSource(                    joined_src,                    image_base,                    meta,                    payload_off,                    packed_size                );core_decode_dispatcher(                    joined_src,                    joined_len,                    packed_dst,                    packed_size,                    -1,                    -1,                    ext_key                );RuntimeFree(joined_src);            } else {core_decode_dispatcher(                    packed_src,                    packed_size,                    packed_dst,                    packed_size,                    meta->gate_beg,                    meta->gate_end,                    ext_key                );            }        } else {// 未设置解码标志,不保留刚分配的目标,直接使用原始 packed 段。RuntimeFree(packed_dst);            packed_dst = packed_src;            ctx->owns_packed_buffer = false;        }// packed payload 前 8 字节是后续解压/加载需要的头字段。        uint32_t unpacked_size = *(uint32_t *)(packed_dst + 0x00);        uint32_t compressed_size = *(uint32_t *)(packed_dst + 0x04);        uint64_t adjusted_size = unpacked_size;        ctx->decompressed_buffer = AllocatePayloadOutput(meta, unpacked_size);CalcPayloadSeed(packed_dst + 8, compressed_size);if (zlib_uncompress2_wrapper(                ctx->decompressed_buffer,                &adjusted_size,                packed_dst + 8,                compressed_size            ) != 0) {RuntimeFree(ctx->decompressed_buffer);RuntimeFree(packed_dst);RuntimeFree(ext_key);return0;        }RuntimeFree(packed_dst);        ctx->loaded_image_size = AdjustOutputSize(meta, adjusted_size);    } else {// 原映射路径:先将覆盖范围改为 RWX,再原地处理 packed_src。RuntimeMprotect(            image_base,PageAlign(payload_off + packed_size),RuntimeExecMapFlags() | PROT_READ | PROT_WRITE        );if ((flags & 0x20) != 0) {xor_decode_fixed_key_blocks(                packed_src,                packed_src,                packed_size            );        } elseif ((flags & 0x02) != 0) {core_decode_dispatcher(                packed_src,                packed_size,                packed_src,                packed_size,                meta->gate_beg,                meta->gate_end,                ext_key            );        }        ctx->parsed_dynstr = (constchar *)image_base;        ctx->loaded_image_size = packed_size;    }// 解码完成后,按恢复出的 image base 回填 ELF 运行时视图。    uint8_t *loaded_base = ctx->loaded_base;    ctx->parsed_dynsym = loaded_base + meta->dynsym_off;    ctx->load_bias = loaded_base + meta->load_bias_off;    ctx->rela = loaded_base + meta->rela_off;    ctx->jmprel = loaded_base + meta->jmprel_off;    ctx->rela_count = meta->rela_size / sizeof(Elf64_Rela);if (meta->android_reloc_off != 0) {        ctx->android_reloc = loaded_base + meta->android_reloc_off;    }if (meta->dynamic_off == 0) {RuntimeFree(ext_key);return0;    }    ctx->dynamic = loaded_base + meta->dynamic_off;elf_compute_load_bias(ctx);    ctx->image_base = ctx->image_end;    ctx->sym_index_begin = meta->sym_index_begin;    ctx->sym_index_end = meta->sym_index_end;    ctx->dynsym_seed3 = meta->dynsym_seed3;    Elf64_Dyn *dynamic = FindFirstDynamicTag(trailer, 1);FinalizeLoadedImage(ctx, trailer, dynamic);RuntimeFree(ext_key);return1;}

该样本将会执行两次 core_decode 而不执行 xor_decode_fixed_key_blocks

core_decode 伪代码

// core_decode @ 0x54B7ECuint32_tcore_decode(uint8_t *src_ptr,       // X0int src_copy_len,       // W1uint8_t *decode_buf,    // X2uint32_t decode_len,    // W3int gate_beg,           // W4int gate_end,           // W5uint8_t *ext_key        // X6){uint8_t head_mask[0x40];uint8_t saved_0x80[0x80];if (src_ptr == NULLreturn0;              // 0x54B808if (src_copy_len <= 0x3Freturn0;         // 0x54B80C..0x54B814memcpy(decode_buf, src_ptr, src_copy_len);  // 0x54B840..0x54B844if (decode_len <= 0x3Freturn0;           // 0x54B848..0x54B850uint32_t seed_input[5];    seed_input[0] = *(uint32_t *)(decode_buf + 0x0C);    seed_input[1] = *(uint32_t *)(decode_buf + 0x04);    seed_input[2] = *(uint32_t *)(decode_buf + 0x30);    seed_input[3] = decode_len;    seed_input[4] = *(uint32_t *)(decode_buf + 0x08);uint32_t seed_main = CalcPayloadSeed(seed_input, 0x14);  // 0x54B8A8memset(head_mask, 0xD70x40);              // 0x54B8AC..0x54B8C0BuildHeadMask(ext_key, head_mask);          // 0x54B8C4..0x54B8CCuint32_t body_len = decode_len - 0x40;if (body_len <= 0x7F) {                     // 0x54B8D4..0x54B8D8Rc4VariantXor(decode_buf + 0x40, body_len, seed_main);for (int i = 0; i < 0x40; ++i) {            decode_buf[i] ^= head_mask[i];        }return decode_len;    }memcpy(saved_0x80, decode_buf + 0x400x80);    // 0x54B960..0x54B978Rc4VariantXor(decode_buf + 0x400x80, seed_main);uint32_t k0 = (seed_main ^ *(uint32_t *)(decode_buf + 0x34)) + 8217;uint32_t k1 = (seed_main ^ *(uint32_t *)(decode_buf + 0x3C)) + 6502;uint32_t k2 = (seed_main ^ decode_len) + 8213;uint32_t k3 = (seed_main ^ *(uint32_t *)(decode_buf + 0x38)) + 6534;uint32_t key4[4] = { k0, k1, k2, k3 };for (int i = 0; i < 0x40; ++i) {            // 0x54B9E4..0x54BA3C        decode_buf[i] ^= head_mask[i];    }Rc4VariantXor(saved_0x80, 0x80, k2);        // 0x54BA40uint32_t sched[9] = {21605, k3, 174771383, k0, k1, 309, k2, 26740    };uint32_t *saved32 = (uint32_t *)saved_0x80;uint32_t block_count = (decode_len - 0xC0) >> 7;if (block_count != 0) {                     // 0x54BA8Cint quarter = block_count >> 2;         // 0x54BA90int range1_beg = quarter;int range1_end = quarter * 2;int range0_beg = quarter * 2;int range0_end = quarter * 3;int range3_beg = quarter * 3;uint8_t *block = decode_buf + 0xC0;int block_pos = 0x120;                  // 注意:gate 比较用 0x120 起步for (uint32_t block_index = 0;             block_index < block_count;             ++block_index, block += 0x80, block_pos += 0x80) {// 0x54BAF4..0x54BB0C:gate 判断直接内联在 core_decode 里if (!(block_pos < gate_beg ||                  gate_beg <= 0x7F ||                  (gate_end != -1 && block_pos >= gate_end))) {continue;            }uint32_t *block32 = (uint32_t *)block;uint32_t mode = sched[block_index % 9] & 3;  // 0x54BB10..0x54BB24switch (mode) {                    // 0x54BB28..0x54BB3Cdefault:// mode 0/default: 0x54BB40 / 0x54BB58..0x54BB80if (block_index >= range0_beg && block_index <= range0_end) {// 0x54BDC4..0x54BE24for (int i = 0; i < 0x80; ++i) {                        block[i] ^= 0xB2;                    }                } else {for (int i = 0; i < 32; ++i) {uint32_t x = saved32[i];                        block32[i] ^= x ^ key4[x & 3];                    }                }break;case1:// mode 1: loc_54BC70 / 0x54BC84..0x54BCCCif (block_index >= range1_beg && block_index <= range1_end) {// 0x54BE28..0x54BE88for (int i = 0; i < 0x80; ++i) {                        block[i] ^= 0xA1;                    }                } else {for (int i = 0; i < 32; ++i) {uint32_t counter = 32 - i;uint32_t rotating = sched[i % 9];                        block32[i] ^= saved32[i] ^ counter ^ rotating;                    }                }break;case2:// mode 2: loc_54BD10 / 0x54BD10..0x54BD5Cfor (int i = 0; i < 32; ++i) {uint32_t rolling = key4[sched[i % 9] & 3];                    block32[i] ^= saved32[i] ^ i ^ rolling;                }break;case3:// mode 3: loc_54BCD0 / 0x54BCDC..0x54BD0Cif (block_index >= range3_beg) {// 0x54BD60..0x54BDC0for (int i = 0; i < 0x80; ++i) {                        block[i] ^= 0xC3;                    }                } else {for (int i = 0; i < 32; ++i) {uint32_t x = saved32[i];                        block32[i] ^= x ^ i ^ key4[x & 3];                    }                }break;            }        }    }uint32_t tail = (decode_len + 0x40) & 0x7F// 0x54BBB4..0x54BBBCif (tail == 0) {return decode_len;    }uint32_t tail_pos = decode_len - tail;for (uint32_t i = 0; i < tail; ++i) {       // 0x54BBD4..0x54BC64uint32_t pos = tail_pos + i;if (gate_beg > (int)pos ||            gate_beg <= 0x7F ||            (gate_end != -1 && gate_end <= (int)pos)) {uint32_t v = sched[key4[i & 3] % 9];            decode_buf[pos] ^= (v + v / 0xFF) ^ saved_0x80[i] ^ i;        }    }return decode_len;}

zlib_uncompress2_wrapper

sub_557170 @ 0x557170 是壳内置 zlib 的一次性解压包装器,参数语义与 uncompress2() 基本一致:

intzlib_uncompress2_wrapper(void *dst,uint64_t *dst_len,constvoid *src,uint64_t src_len);

解压后需要按 PT_LOAD 分段映射

zlib 解压后的 main_payload 输出长度为 0x530268

以下 Elf64_Phdr 来自于第一次 core_decode 解出的 metadata 开头

metadata 开头连续保存 6 个 Elf64_Phdr;筛选 p_type == PT_LOAD 后得到:

p_offsetp_vaddrp_fileszp_memsz
0x00x00x5062E00x5062E0
0x5062E00x50A2E00x29CF00x29D20
0x52FFD00x537FD00x2980xBAE0

完整运行时映射为:

main_payload[0x000000:0x5062E0] -> VA 0x000000main_payload[0x5062E0:0x52FFD0] -> VA 0x50A2E0main_payload[0x52FFD0:0x530268] -> VA 0x537FD0

第二段的 p_memsz - p_filesz = 0x30,第三段为 0xB848;这两块段尾内存必须清零。

当前 IDB 的 VA 0..0x18F 已经保存原始 ELF header 和 Program Header Table,为了避免静态 patch 时覆盖这些结构,可以仅对首段执行:

main_payload[0x190:0x5062E0] -> VA 0x190

解密脚本

"""Decode and patch the packed ELF for the current flags=3 sample."""import structimport zlibimport ida_bytesimport ida_kernwinMAIN_VA = 0x190MAIN_SIZE = 0x5062E0META_VA = 0x63C020META_SIZE = 0x6CC18PHNUM = 6PHENTSIZE = 0x38PT_LOAD = 1def u32(data: bytes | bytearray, offset: int) -> int:returnstruct.unpack_from("<I", data, offset)[0]def put_u32(data: bytearray, offset: int, value: int) ->None:struct.pack_into("<I", data, offset, value & 0xFFFFFFFF)def calc_payload_seed(data: bytes) -> int:    table = []    polynomial = 0x53B20C96forindexinrange(256):        value = indexfor_inrange(8):            value = ((value >> 1) ^ polynomial) if value & 1else value >> 1            value &= 0xFFFFFFFF        table.append(value)    current = 0xFFFFFFFFforvaluein data:        current = table[(value ^ (current & 0xFF)) & 0xFF] ^ (current >> 8)        current = (current + 16) & 0xFFFFFFFFreturn (-current - 516327184) & 0xFFFFFFFFdef rc4_variant_xor(data: bytearray, key_u32: int) ->None:    state = list(range(256))    key = struct.pack("<I", key_u32 & 0xFFFFFFFF)    state_index = 0forindexinrange(256):        old_value = state[index]        state_index = (key[index & 3] + old_value + state_index) & 0xFF        state[index] = state[state_index]        state[state_index] = old_value    index = 0    state_index = 0forpositioninrange(len(data)):        index = (index + 1) & 0xFF        old_value = state[index]        state_index = (old_value + state_index) & 0xFF        state[index] = state[state_index]        state[state_index] = old_value        stream_value = state[(old_value + state[index]) & 0xFF]        mask = (((stream_value >> 2) | (stream_value << 6)) + 0x3A) & 0xFF        data[position] ^= maskdef core_decode(src: bytes) -> bytearray:    decode_len = len(src)    output = bytearray(src)    seed_input = struct.pack("<IIIII",u32(output, 0x0C),u32(output, 0x04),u32(output, 0x30),        decode_len,u32(output, 0x08),    )    seed_main = calc_payload_seed(seed_input)    head_mask = bytes([0xD7]) * 0x40    body_length = decode_len - 0x40if body_length <= 0x7F:        body = bytearray(output[0x40:decode_len])rc4_variant_xor(body, seed_main)        output[0x40:decode_len] = bodyforindexinrange(0x40):            output[index] ^= head_mask[index]return output    saved_block = bytearray(output[0x40:0xC0])    first_block = bytearray(saved_block)rc4_variant_xor(first_block, seed_main)    output[0x40:0xC0] = first_block    key0 = ((seed_main ^ u32(output, 0x34)) + 8217) & 0xFFFFFFFF    key2 = ((seed_main ^ decode_len) + 8213) & 0xFFFFFFFF    key1 = ((seed_main ^ u32(output, 0x3C)) + 6502) & 0xFFFFFFFF    key3 = ((seed_main ^ u32(output, 0x38)) + 6534) & 0xFFFFFFFF    key4 = [key0, key1, key2, key3]forindexinrange(0x40):        output[index] ^= head_mask[index]rc4_variant_xor(saved_block, key2)    block_count = (decode_len - 0xC0) >> 7    range1_begin = (decode_len - 0xC0) >> 9    range1_end = range1_begin << 1    range3_begin = range1_end + range1_begin    schedule = [21605, key3, 174771383, key0, key1, 309, key2, 26740]    saved_words = [u32(saved_block, index * 4forindexinrange(32)]forblock_indexinrange(block_count):        block_offset = 0xC0 + block_index * 0x80        words = [u32(output, block_offset + index * 4forindexinrange(32)]        mode = schedule[block_index % 9] & 3if mode == 0:if range1_end <= block_index <= range3_begin:                words = [word ^ 0xB2forwordin words]else:                words = [word ^ saved_words[i] ^ key4[saved_words[i] & 3fori, word inenumerate(words)]        elif mode == 1:if range1_begin <= block_index <= range1_end:                words = [word ^ 0xA1forwordin words]else:                words = [word ^ saved_words[i] ^ (32 - i) ^ schedule[i % 9fori, word inenumerate(words)]        elif mode == 2:            words = [word ^ saved_words[i] ^ i ^ key4[schedule[i % 9] & 3fori, word inenumerate(words)]else:if block_index >= range3_begin:                words = [word ^ 0xC3forwordin words]else:                words = [word ^ saved_words[i] ^ i ^ key4[saved_words[i] & 3fori, word inenumerate(words)]forindex, word inenumerate(words):put_u32(output, block_offset + index * 4, word)    tail_length = (decode_len + 0x40) & 0x7Fif tail_length:        tail_offset = decode_len - tail_lengthforindexinrange(tail_length):            position = tail_offset + index            schedule_value = schedule[key4[index & 3] % 9]            output[position] ^= ((schedule_value % 0xFF) ^ saved_block[index] ^ index) & 0xFFreturn outputdef parse_load_segments(decoded_meta: bytes):forindexinrange(PHNUM):        phdr = struct.unpack_from("<IIQQQQQQ", decoded_meta, index * PHENTSIZE)        p_type, _, p_offset, p_vaddr, _, p_filesz, p_memsz, _ = phdrif p_type == PT_LOAD:yield p_offset, p_vaddr, p_filesz, p_memszdef patch_load_segments(final_image: bytes, decoded_meta: bytes) ->None:forp_offset, p_vaddr, p_filesz, p_memsz inparse_load_segments(decoded_meta):        skip = 0x190if p_offset == 0else0        source_begin = p_offset + skip        source_end = p_offset + p_filesz        ida_bytes.patch_bytes(            p_vaddr + skip,            final_image[source_begin:source_end],        )if p_memsz > p_filesz:            ida_bytes.patch_bytes(                p_vaddr + p_filesz,bytes(p_memsz - p_filesz),            )print(            f"PT_LOAD final[0x{source_begin:X}:0x{source_end:X}] "            f"-> VA 0x{p_vaddr + skip:X}"        )def main() ->None:    decoded_meta = core_decode(ida_bytes.get_bytes(META_VA, META_SIZE))    decoded_main = core_decode(ida_bytes.get_bytes(MAIN_VA, MAIN_SIZE))    unpacked_size, compressed_size = struct.unpack_from("<II", decoded_main)    final_image = zlib.decompress(decoded_main[8:8 + compressed_size])    assert len(final_image) == unpacked_size    # The first six decoded metadata records are Elf64_Phdr entries.patch_load_segments(final_image, decoded_meta)    ida_kernwin.refresh_idaview_anyway()print(f"unpacked 0x{len(final_image):X} bytes")if __name__ == "__main__":main()

unidbg

编写 test 脚本

package com.github.unidbg.zmcore;import com.github.unidbg.AndroidEmulator;import com.github.unidbg.Module;import com.github.unidbg.Symbol;import com.github.unidbg.arm.backend.Unicorn2Factory;import com.github.unidbg.linux.android.AndroidEmulatorBuilder;import com.github.unidbg.linux.android.AndroidResolver;import com.github.unidbg.linux.android.dvm.DalvikModule;import com.github.unidbg.linux.android.dvm.VM;import com.github.unidbg.memory.Memory;import junit.framework.TestCase;import java.io.File;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;publicclassZMCoreInitDumpTestextendsTestCase {private AndroidEmulator emulator;publicvoidtestInitArrayAndRebuildSo()throws Exception {Pathworkspace= Paths.get(System.getProperty("zmcore.workspace")).toAbsolutePath().normalize();Filelibrary= workspace.resolve("libZMCoreUnZip.so").toFile();Pathoutput= workspace.resolve("unidbg-dump/libZMCoreUnZip.init-rebuilt.so");        Files.createDirectories(output.getParent());        emulator = AndroidEmulatorBuilder.for64Bit()                .setProcessName("com.fileunzip.zxwknight")                .addBackendFactory(newUnicorn2Factory(true))                .build();Memorymemory= emulator.getMemory();        memory.setLibraryResolver(newAndroidResolver(23));VMvm= emulator.createDalvikVM();// forceCallInit=true: execute DT_INIT/DT_INIT_ARRAY before rebuilding the file.DalvikModuledalvikModule= vm.loadLibrary(library, true);Modulemodule= dalvikModule.getModule();        rebuildLoadSegments(library.toPath(), output, module);    }// 以原始 ELF 为文件结构模板,用 init_array 执行后的运行时内存重建各个 PT_LOAD 段privatevoidrebuildLoadSegments(Path input, Path output, Module module)throws IOException {byte[] original = Files.readAllBytes(input);byte[] rebuilt = original.clone();ByteBufferelf= ByteBuffer.wrap(original).order(ByteOrder.LITTLE_ENDIAN);longphoff= elf.getLong(0x20); // Elf64_Ehdr.e_phoffintphentsize= Short.toUnsignedInt(elf.getShort(0x36)); // Elf64_Ehdr.e_phentsizeintphnum= Short.toUnsignedInt(elf.getShort(0x38)); // Elf64_Ehdr.e_phnum// 将解密后的内存段写回原始 ELF 文件的对应位置for (intindex=0; index < phnum; index++) {intphdr= Math.toIntExact(phoff + (long) index * phentsize);if (elf.getInt(phdr) != 1) { // PT_LOADcontinue;            }longfileOffset= elf.getLong(phdr + 0x08); // Elf64_Phdr.p_offsetlongvirtualAddress= elf.getLong(phdr + 0x10); // Elf64_Phdr.p_vaddrlongfileSize= elf.getLong(phdr + 0x20); // Elf64_Phdr.p_fileszbyte[] segment = emulator.getBackend().mem_read(module.base + virtualAddress, fileSize);            System.arraycopy(segment, 0, rebuilt, Math.toIntExact(fileOffset), segment.length);        }// 运行时内存可能覆盖 ELF Header,最终恢复原 SO 的前 0x190 字节        System.arraycopy(original, 0, rebuilt, 00x190);        Files.write(output, rebuilt);        System.out.println("rebuilt SO: " + output);    }@OverrideprotectedvoidtearDown()throws IOException {if (emulator != null) {            emulator.close();        }    }}

将原来的前 0x190 字节复制到 dump 的即可分析

其他

其实还应该解密 elf 最后的 .data 段用于实现 GOT、LPT 定位的,但是比较麻烦就先不做了(分配到动态内存中了),而上述静态分析方法可以直接解密(或者这里结合一下静态的脚本解密一下也可以),下面给出一个工具脚本,用于 patch GOT,当然不使用也可以。

import jsonimport structfrom pathlib import Pathimport ida_bytesimport ida_nameimport ida_naltimport ida_segmentDYNSYM_START = 0x63C170DYNSTR_START = 0x5BA5E8RELA_START = 0x63CE90RELA_END = 0x6A8B78ELF64_SYM_SIZE = 0x18ELF64_RELA_SIZE = 0x18R_AARCH64_ABS64 = 1025R_AARCH64_JUMP_SLOT = 1026R_AARCH64_RELATIVE = 1027VALID_RELOCATION_TYPES = {R_AARCH64_ABS64, R_AARCH64_JUMP_SLOT, R_AARCH64_RELATIVE}MAX_SYMBOL_COUNT = 0x1000MAX_RELOCATION_OFFSET = 0x700000RELOCATION_NAMES = {    R_AARCH64_ABS64: "R_AARCH64_ABS64",    R_AARCH64_JUMP_SLOT: "R_AARCH64_JUMP_SLOT",    R_AARCH64_RELATIVE: "R_AARCH64_RELATIVE",}defread_bytes(address, size):    data = ida_bytes.get_bytes(address, size)if data isNoneorlen(data) != size:raise RuntimeError(f"无法读取 IDA 地址 0x{address:X} 的 0x{size:X} 字节")return datadefread_c_string(address):    data = ida_bytes.get_strlit_contents(address, -10)if data isNone:return""return data.decode("utf-8", errors="replace")defparse_symbol(index):    address = DYNSYM_START + index * ELF64_SYM_SIZE    st_name, st_info, st_other, st_shndx, st_value, st_size = struct.unpack("<IBBHQQ", read_bytes(address, ELF64_SYM_SIZE)    )return {"index": index,"address": address,"name_offset": st_name,"name": read_c_string(DYNSTR_START + st_name) if st_name else"","info": st_info,"other": st_other,"section_index": st_shndx,"value": st_value,"size": st_size,    }defvalidate_decrypted_tables():    symbol_zero = read_bytes(DYNSYM_START, ELF64_SYM_SIZE)if symbol_zero != b"\x00" * ELF64_SYM_SIZE:raise RuntimeError("0x63C170 ?????? Elf64_Sym[0]???????????""???? private relocation"        )    first_relocation = struct.unpack("<QQq", read_bytes(RELA_START, ELF64_RELA_SIZE))    first_type = first_relocation[1] & 0xFFFFFFFF    first_symbol = first_relocation[1] >> 32if (        first_type notin VALID_RELOCATION_TYPESor first_symbol >= MAX_SYMBOL_COUNTor first_relocation[0] >= MAX_RELOCATION_OFFSET    ):raise RuntimeError("0x63CE90 ?????? Elf64_Rela???? IDA ????????""????????? unidbg dump ?????"        )defparse_relocations():    validate_decrypted_tables()    count = (RELA_END - RELA_START) // ELF64_RELA_SIZE    raw_relocations = []    maximum_symbol_index = 0for index inrange(count):        address = RELA_START + index * ELF64_RELA_SIZE        offset, info, addend = struct.unpack("<QQq", read_bytes(address, ELF64_RELA_SIZE)        )        symbol_index = info >> 32        relocation_type = info & 0xFFFFFFFFif relocation_type notin VALID_RELOCATION_TYPES:raise RuntimeError(f"??? #{index} @ 0x{address:X} ????: {relocation_type}"            )if symbol_index >= MAX_SYMBOL_COUNT:raise RuntimeError(f"??? #{index} @ 0x{address:X} ??????: {symbol_index}"            )if offset >= MAX_RELOCATION_OFFSET:raise RuntimeError(f"??? #{index} @ 0x{address:X} ??????: 0x{offset:X}"            )        maximum_symbol_index = max(maximum_symbol_index, symbol_index)        raw_relocations.append(            {"index": index,"address": address,"offset": offset,"info": info,"symbol_index": symbol_index,"type": relocation_type,"type_name": RELOCATION_NAMES.get(                    relocation_type, f"UNKNOWN_{relocation_type}"                ),"addend": addend,            }        )    symbols = [parse_symbol(index) for index inrange(maximum_symbol_index + 1)]for relocation in raw_relocations:        symbol_index = relocation["symbol_index"]        symbol = symbols[symbol_index] if symbol_index < len(symbols) elseNone        relocation["symbol_name"] = symbol["name"if symbol else""        relocation["symbol_value"] = symbol["value"if symbol else0if relocation["type"] == R_AARCH64_RELATIVE:            relocation["resolved_value"] = relocation["addend"]elif relocation["type"] == R_AARCH64_ABS64 and symbol:            relocation["resolved_value"] = symbol["value"] + relocation["addend"]else:            relocation["resolved_value"] = None        resolved_value = relocation["resolved_value"]        relocation["resolved_name"] = (            ida_name.get_name(resolved_value) if resolved_value isnotNoneelse""        )return symbols, raw_relocationsdefsanitize_name(name):    result = []for character in name:if character.isalnum() or character == "_":            result.append(character)else:            result.append("_")return"".join(result).strip("_")defapply_relocations(relocations):    patched = 0    unchanged = 0    named_jump_slots = 0    skipped = 0for relocation in relocations:        target_address = relocation["offset"]        resolved_value = relocation["resolved_value"]        relocation_type = relocation["type"]if relocation_type in (R_AARCH64_RELATIVE, R_AARCH64_ABS64):if resolved_value isNoneor ida_segment.getseg(target_address) isNone:                skipped += 1continue            current_value = struct.unpack("<Q", read_bytes(target_address, 8))[0]            patch_value = resolved_value & 0xFFFFFFFFFFFFFFFFif current_value == patch_value:                unchanged += 1elif ida_bytes.patch_qword(target_address, patch_value):                patched += 1else:                skipped += 1continueif relocation_type == R_AARCH64_JUMP_SLOT:            symbol_name = relocation["symbol_name"]ifnot symbol_name or ida_segment.getseg(target_address) isNone:                skipped += 1continue            got_name = "got_" + sanitize_name(symbol_name)if ida_name.set_name(target_address, got_name, ida_name.SN_FORCE):                named_jump_slots += 1else:                skipped += 1continue        skipped += 1return {"patched": patched,"unchanged": unchanged,"named_jump_slots": named_jump_slots,"skipped": skipped,    }defformat_hex(value):if value isNone:return"-"if value < 0:returnf"-0x{-value:X}"returnf"0x{value:X}"defwrite_outputs(symbols, relocations):    idb_path = Path(ida_nalt.get_input_file_path())    output_directory = idb_path.parent    json_path = output_directory / "private-relocations.json"    text_path = output_directory / "private-relocations.txt"    result = {"dynsym_start": DYNSYM_START,"dynstr_start": DYNSTR_START,"rela_start": RELA_START,"rela_end": RELA_END,"symbol_count"len(symbols),"relocation_count"len(relocations),"symbols": symbols,"relocations": relocations,    }    json_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")    lines = [f"dynsym: 0x{DYNSYM_START:X}",f"dynstr: 0x{DYNSTR_START:X}",f"rela:   [0x{RELA_START:X}, 0x{RELA_END:X})",f"symbols: {len(symbols)}",f"relocations: {len(relocations)}","","index  relocation  offset      type                     symbol                         addend       resolved",    ]for relocation in relocations:        symbol_name = relocation["symbol_name"or"-"        resolved = format_hex(relocation["resolved_value"])if relocation["resolved_name"]:            resolved += f" ({relocation['resolved_name']})"        lines.append(f"{relocation['index']:5d}  "f"0x{relocation['address']:08X}  "f"0x{relocation['offset']:08X}  "f"{relocation['type_name']:<24} "f"{symbol_name:<30} "f"{format_hex(relocation['addend']):<12} "f"{resolved}"        )    text_path.write_text("\n".join(lines) + "\n", encoding="utf-8")return json_path, text_pathdefmain():    symbols, relocations = parse_relocations()    patch_result = apply_relocations(relocations)    json_path, text_path = write_outputs(symbols, relocations)    type_counts = {}for relocation in relocations:        type_name = relocation["type_name"]        type_counts[type_name] = type_counts.get(type_name, 0) + 1print(f"解析符号数量: {len(symbols)}")print(f"解析重定位数量: {len(relocations)}")for type_name, count insorted(type_counts.items()):print(f"{type_name}{count}")print(f"实际写入补丁: {patch_result['patched']}")print(f"原值已经正确: {patch_result['unchanged']}")print(f"命名外部 GOT: {patch_result['named_jump_slots']}")print(f"跳过项目: {patch_result['skipped']}")for relocation in relocations:if relocation["offset"] == 0x533D50:print("0x533D50:", relocation)breakprint(f"JSON: {json_path}")print(f"文本: {text_path}")if __name__ == "__main__":    main()

分析 executeGBS

__int64 __fastcall Java_com_jindk_zmcoreunzipwrapper_ZMCoreUnZipZipApi_executeGBS(        _JNIEnv *a1,        __int64 a2,        void *a3,        void *a4){struct_jmethodID *StaticMethodID_1; // x0constchar *v6; // [xsp+10h] [xbp-250h]char *v7; // [xsp+18h] [xbp-248h]  jbyte *ByteArrayElements; // [xsp+20h] [xbp-240h]  int ArrayLength; // [xsp+2Ch] [xbp-234h]struct_jmethodID *ObjectField; // [xsp+30h] [xbp-230h]struct_jmethodID *v11; // [xsp+38h] [xbp-228h]  jmethodID v12; // [xsp+40h] [xbp-220h]  void *v13; // [xsp+48h] [xbp-218h]  jclass v14; // [xsp+58h] [xbp-208h]  __int64 v15; // [xsp+68h] [xbp-1F8h]  jmethodID v16; // [xsp+70h] [xbp-1F0h]  jclass v17; // [xsp+78h] [xbp-1E8h]struct_jmethodID *v18; // [xsp+90h] [xbp-1D0h]  __int64 v19; // [xsp+98h] [xbp-1C8h]  jclass v20; // [xsp+A0h] [xbp-1C0h]  void *v21; // [xsp+A8h] [xbp-1B8h]  void *v22; // [xsp+B0h] [xbp-1B0h]  int v23; // [xsp+D0h] [xbp-190h]  int v24; // [xsp+D4h] [xbp-18Ch]  int v25; // [xsp+D8h] [xbp-188h]  int v26; // [xsp+DCh] [xbp-184h]constchar *v27; // [xsp+E0h] [xbp-180h]constchar *v28; // [xsp+E8h] [xbp-178h]constchar *v29; // [xsp+F0h] [xbp-170h]constchar *StringUTFChars; // [xsp+F8h] [xbp-168h]  void *v31; // [xsp+100h] [xbp-160h]  void *v32; // [xsp+108h] [xbp-158h]struct_jmethodID *StaticMethodID; // [xsp+110h] [xbp-150h]  jclass a2a; // [xsp+118h] [xbp-148h]  int v35; // [xsp+12Ch] [xbp-134h]  jmethodID v36; // [xsp+130h] [xbp-130h]struct_jmethodID *v37; // [xsp+138h] [xbp-128h]  jmethodID MethodID; // [xsp+140h] [xbp-120h]  jclass Class; // [xsp+148h] [xbp-118h]constchar *v45; // [xsp+218h] [xbp-48h]constchar *v46; // [xsp+238h] [xbp-28h]constchar *v47; // [xsp+258h] [xbp-8h]  Class = _JNIEnv::FindClass(a1, "android/content/Context");if ( Class == nullptr )return0;  MethodID = _JNIEnv::GetMethodID(&a1->functions, Class, "getPackageManager""()Landroid/content/pm/PackageManager;");  v37 = _JNIEnv::GetMethodID(&a1->functions, Class, "getPackageName""()Ljava/lang/String;");if ( a4 == nullptr || MethodID == nullptr || v37 == nullptr )return0;  v36 = _JNIEnv::CallObjectMethod(&a1->functions, a4, v37);if ( v36 == nullptr )return0;  v35 = ZMCoreUnzip_executeCVS(a1, a4);  a2a = _JNIEnv::FindClass(a1, "java/lang/String");  StaticMethodID = _JNIEnv::GetStaticMethodID(&a1->functions, a2a, "valueOf""(I)Ljava/lang/String;");  v32 = (void *)_JNIEnv::CallStaticObjectMethod(                  &a1->functions,                  a2a,                  StaticMethodID,                  (unsigned int)(2 * ((v35 >> 2) - 169) + 127));  v31 = (void *)_JNIEnv::CallStaticObjectMethod(&a1->functions, a2a, StaticMethodID, (unsigned int)v35);if ( v32 == nullptr || v31 == nullptr )return0;  StringUTFChars = _JNIEnv::GetStringUTFChars(&a1->functions, v31, nullptr);  v29 = _JNIEnv::GetStringUTFChars(&a1->functions, v36, nullptr);  v28 = _JNIEnv::GetStringUTFChars(&a1->functions, a3, nullptr);  v27 = _JNIEnv::GetStringUTFChars(&a1->functions, v32, nullptr);if ( StringUTFChars == nullptr || v29 == nullptr || v28 == nullptr || v27 == nullptr )return0;  v26 = j_got_strlen_chk(StringUTFChars, -1);  v25 = j_got_strlen_chk(v29, -1);  v24 = j_got_strlen_chk(v28, -1);  v23 = j_got_strlen_chk(v27, -1);  v47 = (constchar *)j_got_malloc(v26 + v25 + v24 + v23 + 1);j_got_memcpy_chk(v47, StringUTFChars, v26, -1);  v46 = &v47[v26];j_got_memcpy_chk(v46, v29, v25, -1);  v45 = &v46[v25];j_got_memcpy_chk(v45, v28, v24, -1);j_got_memcpy_chk(&v45[v24], v27, v23, -1);  v47[v26 + v25 + v24 + v23] = 0;  v22 = (void *)_JNIEnv::NewStringUTF(a1, v47);j_got_free(v47);  v21 = (void *)_JNIEnv::NewStringUTF(a1, "gwFG#9P!ad+*PMUnisapps$Rs#L!_1G9");  v20 = _JNIEnv::FindClass(a1, "java/lang/String");  v19 = _JNIEnv::NewStringUTF(a1, "utf-8");  v18 = _JNIEnv::GetMethodID(&a1->functions, v20, "getBytes""(Ljava/lang/String;)[B");  _JNIEnv::CallObjectMethod(&a1->functions, v21, v18);  _JNIEnv::CallObjectMethod(&a1->functions, v22, v18);  v17 = _JNIEnv::FindClass(a1, "javax/crypto/spec/SecretKeySpec");if ( v17 == nullptr )return0;  v16 = _JNIEnv::GetMethodID(&a1->functions, v17, "<init>""([BLjava/lang/String;)V");  v15 = _JNIEnv::NewStringUTF(a1, "HmacSHA1");  _JNIEnv::NewObject(&a1->functions, v17, v16);  v14 = _JNIEnv::FindClass(a1, "javax/crypto/Mac");if ( v14 == nullptr )return0;  StaticMethodID_1 = _JNIEnv::GetStaticMethodID(                       &a1->functions,                       v14,"getInstance","(Ljava/lang/String;)Ljavax/crypto/Mac;");  v13 = (void *)_JNIEnv::CallStaticObjectMethod(&a1->functions, v14, StaticMethodID_1, v15);  v12 = _JNIEnv::GetMethodID(&a1->functions, v14, "init""(Ljava/security/Key;)V");  v11 = _JNIEnv::GetMethodID(&a1->functions, v14, "doFinal""([B)[B");  _JNIEnv::CallVoidMethod(&a1->functions, (__int64)v13, (__int64)v12);  ObjectField = _JNIEnv::CallObjectMethod(&a1->functions, v13, v11);  ArrayLength = _JNIEnv::GetArrayLength(&a1->functions, (__int64)ObjectField);if ( ArrayLength == 0 )return0;  ByteArrayElements = _JNIEnv::GetByteArrayElements(&a1->functions, ObjectField, nullptr);  v7 = (char *)j_got_malloc(ArrayLength + 1);j_got_memcpy_chk(v7, ByteArrayElements, ArrayLength, -1);  v7[ArrayLength] = 0;  v6 = ZMCoreUnZip_BXF_encode(v7, ArrayLength);if ( v6 == nullptr )return0;  _JNIEnv::DeleteLocalRef(&a1->functions, (__int64)v22);  _JNIEnv::DeleteLocalRef(&a1->functions, v15);  _JNIEnv::DeleteLocalRef(&a1->functions, (__int64)v21);  _JNIEnv::DeleteLocalRef(&a1->functions, v19);j_got_free(v7);return _JNIEnv::NewStringUTF(a1, v6);}

伪代码

intZMCoreUnzip_executeCVS(Context context) {StringpackageName= context.getPackageName();if (!packageName.equals("com.fileunzip.zxwknight")) {return -300;    }PackageManagerpm= context.getPackageManager();intflags= (SDK_INT >= 28) ? 0x08000000 : 0x40;PackageInfopi= pm.getPackageInfo(packageName, flags);// Android 9+ 走 signingInfo,旧版本走 PackageInfo.signatures    Signature[] signatures = GetApkSignatures(pi);// 当前样本最终取第一个签名对象的 hashCode()return signatures[0].hashCode();}jstring NativeGetQPWATImpl(JNIEnv *env, jclass clazz, jstring input, jobject context) {StringpackageName= context.getPackageName();          // 当前样本 = "com.fileunzip.zxwknight"intctxValue= ZMCoreUnzip_executeCVS(context);         // 当前样本静态复现 = 516622220inttail=2 * ((ctxValue >> 2) - 169) + 127;          // 见 0x4B9008-0x4B9030Stringdata=        String.valueOf(ctxValue) +                          // 前缀1:签名 hashCode        packageName +                                       // 前缀2:包名        input +                                             // 中间:Java 侧传来的 fileName + fileSize        String.valueOf(tail);                               // 后缀:由 ctxValue 推导byte[] key = "gwFG#9P!ad+*PMUnisapps$Rs#L!_1G9".getBytes("utf-8");byte[] body = data.getBytes("utf-8");Macmac= Mac.getInstance("HmacSHA1");                 // 算法固定为 HMAC-SHA1    mac.init(newSecretKeySpec(key, "HmacSHA1"));byte[] digest = mac.doFinal(body);return ZMCoreUnZip_BXF_encode(digest);                 // Base64 编码别名,编码表为 A-Z a-z 0-9 + / =}

POC

#!/usr/bin/env python3"""Minimal fixed-value PoC for:1. submit zip password -> /api/v3/sync/ads2. query zip password  -> /api/v3/search/infoUsage:    python min_zip_password_poc.py submit "a.zip" "123456"    python min_zip_password_poc.py query  "a.zip""""import argparseimport base64import hashlibimport hmacimport jsonimport osfrom pathlib import Pathimport requestsfrom Crypto.Cipher import AESfrom Crypto.Util.Padding import pad, unpadREGION = "CN"PACKAGE_NAME = "com.fileunzip.zxwknight"VERSION_NAME = "3.2.23"APP_ID = "zipa"PLATFORM = "android"CTX_VALUE = 516622220QUERY_URL = "https://file.unisapps.com/api/v3/search/info"SUBMIT_URL = "https://file.unisapps.com/api/v3/sync/ads"ACCESS_KEY = b"gwFG#9P!ad+*PMUnisapps$Rs#L!_1G9"AES_KEY = b"EyR2JvBXJXaUdY9auxetvhpEeQ8DmC6L"def file_md5(path: Path) -> str:    md5 = hashlib.md5()    with path.open("rb"as fp:for chunk in iter(lambda: fp.read(1024 * 1024), b""):            md5.update(chunk)return md5.hexdigest()def build_user_client_access(file_name: str, file_sizeint) -> str:    tail = 2 * ((CTX_VALUE >> 2) - 169) + 127    text = f"{CTX_VALUE}{PACKAGE_NAME}{file_name}{file_size}{tail}".encode("utf-8")    digest = hmac.new(ACCESS_KEY, text, hashlib.sha1).digest()return base64.b64encode(digest).decode("ascii")def encrypt_json(obj: dict) -> str:    plain = json.dumps(obj, ensure_ascii=False, separators=(","":")).encode("utf-8")    iv = os.urandom(16)    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)    data = cipher.encrypt(pad(plain, AES.block_size))    body = {"data": base64.b64encode(data).decode("ascii"),"iv": base64.b64encode(iv).decode("ascii"),    }return json.dumps(body, ensure_ascii=False, separators=(","":"))def try_decrypt_response(text: str) -> str | None:try:        obj = json.loads(text)        data = base64.b64decode(obj["data"])        iv = base64.b64decode(obj["iv"])    except Exception:return None    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)try:        plain = unpad(cipher.decrypt(data), AES.block_size)    except Exception:return Nonereturn plain.decode("utf-8", errors="replace")def submit_password(file_name: str, file_sizeintmd5_value: str, password: str) -> None:    body = encrypt_json(        {"fileName": file_name,"fileSize": file_size,"pd": password,"region": REGION,"md5": md5_value,        }    )    headers = {"Content-Type""application/json; charset=utf-8","appid": APP_ID,"version": VERSION_NAME,"platform": PLATFORM,    }    resp = requests.post(SUBMIT_URL, data=body.encode("utf-8"), headers=headers, timeout=20)print("[submit] status =", resp.status_code)print("[submit] body   =", resp.text)def query_password(file_name: str, file_sizeintmd5_value: str) -> None:    body = encrypt_json(        {"fileName": file_name,"fileSize": file_size,"md5": md5_value,        }    )    headers = {"Content-Type""application/json; charset=utf-8","User-Client-Access"build_user_client_access(file_name, file_size),"appid": APP_ID,"version": VERSION_NAME,"platform": PLATFORM,    }    resp = requests.post(QUERY_URL, data=body.encode("utf-8"), headers=headers, timeout=20)print("[query]  status =", resp.status_code)print("[query]  body   =", resp.text)    plain = try_decrypt_response(resp.text)if plain is not None:print("[query]  decrypted =", plain)def parse_args() -> argparse.Namespace:    parser = argparse.ArgumentParser(description="Minimal zip password submit/query PoC")    parser.add_argument("action", choices=["submit""query"])    parser.add_argument("path", help="local archive path")    parser.add_argument("password", nargs="?", help="required for submit")    args = parser.parse_args()if args.action == "submit"and not args.password:        parser.error("password is required for submit")return argsdef main() -> None:    args = parse_args()    file_path = Path(args.path)if not file_path.is_file():        raise SystemExit(f"file not found: {file_path}")    file_name = file_path.name    file_size = file_path.stat().st_size    md5_value = file_md5(file_path)print("fileName =", file_name)print("fileSize =", file_size)print("md5      =", md5_value)if args.action == "submit":submit_password(file_name, file_size, md5_value, args.password)if args.action == "query":query_password(file_name, file_size, md5_value)if __name__ == "__main__":main()

看雪ID:mb_jepgtozh

https://bbs.kanxue.com/user-home-1022681.htm

*本文为看雪论坛优秀文章,由 mb_jepgtozh原创,转载请注明来自看雪社区
第十届安全开发者峰会【议题征集】-欢迎投稿

# 往期推荐

从时间片轮转到调用级并发:unidbg 单后端多线程架构重构

基于VT EPT的无痕断点无痕hook原理及其应用

基于 GLM-5.2 的 X-Gnarly (332位) 纯算还原分析

ISCC 2026 题目 [house of apple 2] 利用方法

Android 内核无痕 Hook 框架设计思路和避坑经验

球分享

球点赞

球在看

点击阅读原文查看更多

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-11 20:12:37 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/902111.html
  2. 运行时间 : 0.115452s [ 吞吐率:8.66req/s ] 内存消耗:5,037.27kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=f01bba702cd9d149c66435910f79443b
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 4.22 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.11 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000548s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000810s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000456s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.001019s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000487s ]
  6. SELECT * FROM `set` [ RunTime:0.000189s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000587s ]
  8. SELECT * FROM `article` WHERE `id` = 902111 LIMIT 1 [ RunTime:0.004796s ]
  9. UPDATE `article` SET `lasttime` = 1786450357 WHERE `id` = 902111 [ RunTime:0.006176s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000269s ]
  11. SELECT * FROM `article` WHERE `id` < 902111 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000476s ]
  12. SELECT * FROM `article` WHERE `id` > 902111 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000504s ]
  13. SELECT * FROM `article` WHERE `id` < 902111 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001292s ]
  14. SELECT * FROM `article` WHERE `id` < 902111 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.010533s ]
  15. SELECT * FROM `article` WHERE `id` < 902111 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004666s ]
0.117173s