titanium-apk-recover 2.2.2 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -35,14 +35,17 @@ __export(src_exports, {
35
35
  buildInfo: () => buildInfo,
36
36
  buildReconstruct: () => reconstruct,
37
37
  buildTiappXml: () => buildTiappXml,
38
+ cloakKeyCandidates: () => cloakKeyCandidates,
38
39
  decodeJavaInt: () => decodeJavaInt,
39
40
  decryptCloakAsset: () => decryptCloakAsset,
40
41
  decryptRange: () => decryptRange,
41
42
  decryptRanges: () => decryptRanges,
42
43
  deriveCloakKey: () => deriveCloakKey,
44
+ deriveKeyFromBlock: () => deriveKeyFromBlock,
43
45
  detectAlloy: () => detectAlloy,
44
46
  extractByteArrayFields: () => extractByteArrayFields,
45
47
  extractCloakSalt: () => extractCloakSalt,
48
+ extractKeyBlock: () => extractKeyBlock,
46
49
  extractRanges: () => extractRanges,
47
50
  extractStringChunks: () => extractStringChunks,
48
51
  instructionWidth: () => instructionWidth,
@@ -629,26 +632,109 @@ function readAssetCrypt(dexBuffers, packageHint) {
629
632
  var import_node_crypto2 = require("crypto");
630
633
  var import_node_zlib = require("zlib");
631
634
  var KEY_LEN2 = 16;
632
- var BLOCK_BASE = 8200;
633
- function deriveCloakKey(so, salt) {
634
- if (salt.length !== KEY_LEN2) return null;
635
+ var BLOCK_LEN = 64;
636
+ var FALLBACK_BLOCK_OFFSETS = [8200, 8196, 8208, 12292, 16388];
637
+ function extractKeyBlock(so) {
635
638
  try {
636
- if (so.length < BLOCK_BASE + 64) return null;
637
- const randomOffset = so.readUInt8(BLOCK_BASE + 62);
638
- const xor = Buffer.concat([
639
- so.subarray(BLOCK_BASE + 1, BLOCK_BASE + 5),
640
- so.subarray(BLOCK_BASE + randomOffset, BLOCK_BASE + randomOffset + 4),
641
- so.subarray(BLOCK_BASE + 15, BLOCK_BASE + 15 + 4),
642
- so.subarray(BLOCK_BASE + 30, BLOCK_BASE + 30 + 4)
643
- ]);
644
- if (xor.length < KEY_LEN2) return null;
645
- const key = Buffer.alloc(KEY_LEN2);
646
- for (let i = 0; i < KEY_LEN2; i++) key[i] = (salt[i] ?? 0) ^ (xor[i] ?? 0);
647
- return key;
639
+ if (so.length < 64) return null;
640
+ if (so[0] !== 127 || so[1] !== 69 || so[2] !== 76 || so[3] !== 70) return null;
641
+ const is64 = so[4] === 2;
642
+ const le = so[5] !== 2;
643
+ const u16 = (o) => le ? so.readUInt16LE(o) : so.readUInt16BE(o);
644
+ const u32 = (o) => le ? so.readUInt32LE(o) : so.readUInt32BE(o);
645
+ const u64 = (o) => Number(le ? so.readBigUInt64LE(o) : so.readBigUInt64BE(o));
646
+ const shoff = is64 ? u64(40) : u32(32);
647
+ const shentsize = u16(is64 ? 58 : 46);
648
+ const shnum = u16(is64 ? 60 : 48);
649
+ if (shoff === 0 || shnum === 0 || shentsize === 0) return null;
650
+ const secs = [];
651
+ for (let i = 0; i < shnum; i++) {
652
+ const b = shoff + i * shentsize;
653
+ if (b + shentsize > so.length) return null;
654
+ secs.push(
655
+ is64 ? {
656
+ type: u32(b + 4),
657
+ addr: u64(b + 16),
658
+ offset: u64(b + 24),
659
+ size: u64(b + 32),
660
+ link: u32(b + 40),
661
+ entsize: u64(b + 56)
662
+ } : {
663
+ type: u32(b + 4),
664
+ addr: u32(b + 12),
665
+ offset: u32(b + 16),
666
+ size: u32(b + 20),
667
+ link: u32(b + 24),
668
+ entsize: u32(b + 36)
669
+ }
670
+ );
671
+ }
672
+ const SHT_SYMTAB = 2;
673
+ const SHT_DYNSYM = 11;
674
+ const symSec = secs.find((s) => s.type === SHT_DYNSYM) ?? secs.find((s) => s.type === SHT_SYMTAB);
675
+ const strSec = symSec ? secs[symSec.link] : void 0;
676
+ if (!symSec || !strSec) return null;
677
+ const symEnt = is64 ? 24 : 16;
678
+ const count = Math.floor(symSec.size / (symSec.entsize || symEnt));
679
+ for (let i = 0; i < count; i++) {
680
+ const s = symSec.offset + i * symEnt;
681
+ if (s + symEnt > so.length) break;
682
+ const nameOff = strSec.offset + u32(s);
683
+ if (nameOff <= 0 || nameOff >= so.length) continue;
684
+ let end = nameOff;
685
+ while (end < so.length && so[end] !== 0) end++;
686
+ if (so.toString("latin1", nameOff, end) !== "KEY_BLOCK") continue;
687
+ const value = is64 ? u64(s + 8) : u32(s + 4);
688
+ const shndx = u16(s + (is64 ? 6 : 14));
689
+ const host = secs[shndx]?.addr && value >= secs[shndx].addr && value < secs[shndx].addr + secs[shndx].size ? secs[shndx] : secs.find((x) => x.addr !== 0 && x.size > 0 && value >= x.addr && value < x.addr + x.size);
690
+ if (!host) return null;
691
+ const fileOff = value - host.addr + host.offset;
692
+ if (fileOff < 0 || fileOff + BLOCK_LEN > so.length) return null;
693
+ return so.subarray(fileOff, fileOff + BLOCK_LEN);
694
+ }
695
+ return null;
648
696
  } catch {
649
697
  return null;
650
698
  }
651
699
  }
700
+ function deriveKeyFromBlock(block, salt) {
701
+ if (salt.length !== KEY_LEN2 || block.length < BLOCK_LEN) return null;
702
+ const randomOffset = block.readUInt8(62);
703
+ if (randomOffset + 4 > BLOCK_LEN) return null;
704
+ const xor = Buffer.concat([
705
+ block.subarray(1, 5),
706
+ block.subarray(randomOffset, randomOffset + 4),
707
+ block.subarray(15, 15 + 4),
708
+ block.subarray(30, 30 + 4)
709
+ ]);
710
+ if (xor.length < KEY_LEN2) return null;
711
+ const key = Buffer.alloc(KEY_LEN2);
712
+ for (let i = 0; i < KEY_LEN2; i++) key[i] = (salt[i] ?? 0) ^ (xor[i] ?? 0);
713
+ return key;
714
+ }
715
+ function cloakKeyCandidates(so, salt) {
716
+ if (salt.length !== KEY_LEN2) return [];
717
+ const blocks = [];
718
+ const elf = extractKeyBlock(so);
719
+ if (elf) blocks.push(elf);
720
+ for (const off of FALLBACK_BLOCK_OFFSETS) {
721
+ if (off + BLOCK_LEN <= so.length) blocks.push(so.subarray(off, off + BLOCK_LEN));
722
+ }
723
+ const keys = [];
724
+ const seen = /* @__PURE__ */ new Set();
725
+ for (const block of blocks) {
726
+ const key = deriveKeyFromBlock(block, salt);
727
+ if (!key) continue;
728
+ const hex = key.toString("hex");
729
+ if (seen.has(hex)) continue;
730
+ seen.add(hex);
731
+ keys.push(key);
732
+ }
733
+ return keys;
734
+ }
735
+ function deriveCloakKey(so, salt) {
736
+ return cloakKeyCandidates(so, salt)[0] ?? null;
737
+ }
652
738
  function decryptCloakAsset(bin, key, salt) {
653
739
  if (key.length !== KEY_LEN2 || salt.length !== KEY_LEN2) return null;
654
740
  try {
@@ -678,10 +764,10 @@ function isProbablyText(buf) {
678
764
  }
679
765
  function pickCloakKey(cloakLibs, salt, sampleBin) {
680
766
  for (const so of cloakLibs) {
681
- const key = deriveCloakKey(so, salt);
682
- if (!key) continue;
683
- const decrypted = decryptCloakAsset(sampleBin, key, salt);
684
- if (decrypted && isProbablyText(decrypted)) return key;
767
+ for (const key of cloakKeyCandidates(so, salt)) {
768
+ const decrypted = decryptCloakAsset(sampleBin, key, salt);
769
+ if (decrypted && isProbablyText(decrypted)) return key;
770
+ }
685
771
  }
686
772
  return null;
687
773
  }
@@ -1103,14 +1189,17 @@ async function readSourceFiles(base) {
1103
1189
  buildInfo,
1104
1190
  buildReconstruct,
1105
1191
  buildTiappXml,
1192
+ cloakKeyCandidates,
1106
1193
  decodeJavaInt,
1107
1194
  decryptCloakAsset,
1108
1195
  decryptRange,
1109
1196
  decryptRanges,
1110
1197
  deriveCloakKey,
1198
+ deriveKeyFromBlock,
1111
1199
  detectAlloy,
1112
1200
  extractByteArrayFields,
1113
1201
  extractCloakSalt,
1202
+ extractKeyBlock,
1114
1203
  extractRanges,
1115
1204
  extractStringChunks,
1116
1205
  instructionWidth,