titanium-apk-recover 2.2.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/README.md +252 -0
- package/dist/chunk-ML2DCRT6.js +1063 -0
- package/dist/chunk-ML2DCRT6.js.map +1 -0
- package/dist/cli.cjs +1086 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +90 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1126 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +340 -0
- package/dist/index.d.ts +340 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { DexFile, DexClassDef } from 'libdex-ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public type definitions for ti_recover.
|
|
5
|
+
*/
|
|
6
|
+
/** Configuration accepted by {@link TiRecover}. */
|
|
7
|
+
interface RecoverConfig {
|
|
8
|
+
/** Path to the `.apk` file to recover. Relative paths resolve against `cwd`. */
|
|
9
|
+
apk?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Path to an already-extracted APK directory. When provided, the unzip step
|
|
12
|
+
* is skipped: `classes*.dex` and `AndroidManifest.xml` are read from here.
|
|
13
|
+
*/
|
|
14
|
+
apkDir?: string;
|
|
15
|
+
/** Directory where recovered source and assets are written. */
|
|
16
|
+
outDir?: string;
|
|
17
|
+
/** Temporary directory used while unpacking the APK. Defaults to `_tmp`. */
|
|
18
|
+
tmpDir?: string;
|
|
19
|
+
/** Emit progress logging to the console. Defaults to `false`. */
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** A single recovered source file held in memory. */
|
|
23
|
+
interface RecoveredFile {
|
|
24
|
+
/** Byte offset of the file inside the encrypted asset blob (0 for dev mode). */
|
|
25
|
+
offset: number;
|
|
26
|
+
/** Length in bytes of the recovered content. */
|
|
27
|
+
bytes: number;
|
|
28
|
+
/** Recovered file contents. */
|
|
29
|
+
content: string | Buffer;
|
|
30
|
+
}
|
|
31
|
+
/** Map of relative file path -> recovered file. */
|
|
32
|
+
type MemorySource = Record<string, RecoveredFile>;
|
|
33
|
+
/** Raw values parsed from a decoded `AndroidManifest.xml`. */
|
|
34
|
+
interface ManifestInfo {
|
|
35
|
+
package?: string;
|
|
36
|
+
versionCode?: string;
|
|
37
|
+
versionName?: string;
|
|
38
|
+
appName?: string;
|
|
39
|
+
/** Directory the manifest was read from. */
|
|
40
|
+
dir?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Detected Titanium metadata, returned by {@link TiRecover.info}. */
|
|
43
|
+
interface TitaniumInfo extends ManifestInfo {
|
|
44
|
+
/** Whether the APK was built in Titanium "development" mode (plain sources). */
|
|
45
|
+
developmentMode: boolean;
|
|
46
|
+
/** Coarse Titanium engine version bucket: `"<5"`, `"5.x"` or `"unknown"`. */
|
|
47
|
+
titaniumVersion: TitaniumVersion;
|
|
48
|
+
/** Whether the project appears to be built with Alloy. */
|
|
49
|
+
alloy: boolean;
|
|
50
|
+
/** Recovered files and their sizes. */
|
|
51
|
+
files: {
|
|
52
|
+
name: string;
|
|
53
|
+
bytes: number;
|
|
54
|
+
}[];
|
|
55
|
+
/** Total number of recovered bytes across all files. */
|
|
56
|
+
totalBytes: number;
|
|
57
|
+
}
|
|
58
|
+
type TitaniumVersion = "<5" | "5.x" | "unknown";
|
|
59
|
+
/** Metadata collected while decrypting the asset blob. */
|
|
60
|
+
interface DecryptMeta {
|
|
61
|
+
totalBytes: number;
|
|
62
|
+
titaniumVersion: TitaniumVersion;
|
|
63
|
+
alloy: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Result of a decryption pass. */
|
|
66
|
+
interface DecryptResult {
|
|
67
|
+
files: MemorySource;
|
|
68
|
+
meta: DecryptMeta;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface WrittenFile {
|
|
72
|
+
name: string;
|
|
73
|
+
bytes: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Parses a binary `AndroidManifest.xml` buffer, returning both structured info
|
|
78
|
+
* and a readable XML serialisation.
|
|
79
|
+
*/
|
|
80
|
+
declare function parseBinaryManifest(bytes: Buffer, dir?: string): {
|
|
81
|
+
info: ManifestInfo;
|
|
82
|
+
xml: string;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Text manifest parser (cheerio) for pre-decoded, human-readable manifests.
|
|
86
|
+
*/
|
|
87
|
+
declare function parseManifest(xml: string, dir?: string): ManifestInfo;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Extract Titanium's encrypted asset data directly from `classes*.dex`,
|
|
91
|
+
* replacing the apktool (smali) + jadx (java) decompile step.
|
|
92
|
+
*
|
|
93
|
+
* The generated `AssetCryptImpl` class has two private static methods:
|
|
94
|
+
* - `initAssetsBytes()` builds the encrypted blob by appending a sequence of
|
|
95
|
+
* `const-string` literals into a CharBuffer.
|
|
96
|
+
* - `initAssets()` fills a HashMap with `put("file", new Range(offset, length))`
|
|
97
|
+
* entries.
|
|
98
|
+
*
|
|
99
|
+
* Both are ordinary DEX bytecode. We locate the class, then walk each method's
|
|
100
|
+
* instruction stream: for the blob we collect `const-string`/`const-string/jumbo`
|
|
101
|
+
* operands in order; for the ranges we do a tiny register trace to recover the
|
|
102
|
+
* (file, offset, length) triples. A DEX instruction width table lets us step
|
|
103
|
+
* over every opcode correctly. Since DEX stores real (already-unescaped)
|
|
104
|
+
* strings, the old Java string-unescape step is gone entirely.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/** A single file's location inside the decrypted blob, from `initAssets`. */
|
|
108
|
+
interface DexRange {
|
|
109
|
+
file: string;
|
|
110
|
+
offset: number;
|
|
111
|
+
bytes: number;
|
|
112
|
+
}
|
|
113
|
+
interface AssetCrypt {
|
|
114
|
+
/** The raw encrypted asset blob (last 16 bytes are the AES key). */
|
|
115
|
+
blob: Buffer;
|
|
116
|
+
ranges: DexRange[];
|
|
117
|
+
titaniumVersion: TitaniumVersion;
|
|
118
|
+
}
|
|
119
|
+
type AssetCryptResult = {
|
|
120
|
+
kind: "classic";
|
|
121
|
+
data: AssetCrypt;
|
|
122
|
+
} | {
|
|
123
|
+
kind: "newscheme";
|
|
124
|
+
salt: Buffer | null;
|
|
125
|
+
} | {
|
|
126
|
+
kind: "none";
|
|
127
|
+
};
|
|
128
|
+
/** Width, in code units, of the instruction at `idx` (handles payloads). */
|
|
129
|
+
declare function instructionWidth(insns: Uint16Array, idx: number): number;
|
|
130
|
+
/** Collects `const-string` operands from a method in execution order. */
|
|
131
|
+
declare function extractStringChunks(dex: DexFile, insns: Uint16Array): string[];
|
|
132
|
+
/**
|
|
133
|
+
* Walks `initAssets` with a small register trace to recover the file ranges.
|
|
134
|
+
* Tracks integer/string constants per register, associates `new Range(off,len)`
|
|
135
|
+
* with the object register at its `<init>`, and emits an entry when that object
|
|
136
|
+
* is stored into the map via `put(key, rangeObj)`.
|
|
137
|
+
*/
|
|
138
|
+
declare function extractRanges(dex: DexFile, insns: Uint16Array): DexRange[];
|
|
139
|
+
/**
|
|
140
|
+
* Extracts static `byte[]` fields (assigned via `fill-array-data` +
|
|
141
|
+
* `sput-object`) from a method's instruction stream, keyed by field name.
|
|
142
|
+
*/
|
|
143
|
+
declare function extractByteArrayFields(dex: DexFile, insns: Uint16Array): Record<string, Buffer>;
|
|
144
|
+
/** Lifts the ti.cloak `salt` (AES-CBC IV) out of `AssetCryptImpl.<clinit>`. */
|
|
145
|
+
declare function extractCloakSalt(dex: DexFile, classDef: DexClassDef): Buffer | null;
|
|
146
|
+
/**
|
|
147
|
+
* Locates `AssetCryptImpl` across the given DEX buffers and extracts the
|
|
148
|
+
* encrypted blob plus file ranges. Returns `newscheme` when the class exists
|
|
149
|
+
* but lacks `initAssetsBytes` (the ti.cloak/.bin variant), or `none` when the
|
|
150
|
+
* class is absent entirely.
|
|
151
|
+
*/
|
|
152
|
+
declare function readAssetCrypt(dexBuffers: Uint8Array[], packageHint?: string): AssetCryptResult;
|
|
153
|
+
|
|
154
|
+
/** A single file's location inside the decrypted asset blob. */
|
|
155
|
+
interface Range {
|
|
156
|
+
offset: number;
|
|
157
|
+
bytes: number;
|
|
158
|
+
}
|
|
159
|
+
/** Result of parsing `AssetCryptImpl.smali`'s `initAssetsBytes()`. */
|
|
160
|
+
interface AssetBufferParse {
|
|
161
|
+
titaniumVersion: TitaniumVersion;
|
|
162
|
+
bufferLen: number;
|
|
163
|
+
escaped: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Decodes an integer literal the way `java.lang.Integer.decode` does:
|
|
167
|
+
* supports optional sign, `0x`/`0X`/`#` hex, leading-zero octal, and decimal.
|
|
168
|
+
*/
|
|
169
|
+
declare function decodeJavaInt(literal: string): number;
|
|
170
|
+
/**
|
|
171
|
+
* Parses the `hashMap.put("file", new Range(offset, length))` entries from an
|
|
172
|
+
* `AssetCryptImpl.java` source into an ordered map of file -> range.
|
|
173
|
+
*/
|
|
174
|
+
declare function parseRanges(javaContent: string): Record<string, Range>;
|
|
175
|
+
/**
|
|
176
|
+
* Parses `AssetCryptImpl.smali`, extracting the declared buffer length, the
|
|
177
|
+
* concatenated escaped string literal and a coarse Titanium version.
|
|
178
|
+
*/
|
|
179
|
+
declare function parseAssetBuffer(smaliContent: string): AssetBufferParse;
|
|
180
|
+
/** Heuristic: does the recovered file set look like an Alloy project? */
|
|
181
|
+
declare function detectAlloy(files: MemorySource): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Decrypts one `[offset, length]` slice of the blob. The key is the last 16
|
|
184
|
+
* bytes of the blob. Titanium varied two conventions across versions, so we try
|
|
185
|
+
* the key derived from both `length` and `length - 1`, and the slice from both
|
|
186
|
+
* `offset` and `offset - 1` (some files have a padded offset).
|
|
187
|
+
*/
|
|
188
|
+
declare function decryptRange(blob: Buffer, offset: number, length: number): string | null;
|
|
189
|
+
interface DecryptRangesResult {
|
|
190
|
+
files: MemorySource;
|
|
191
|
+
totalBytes: number;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Decrypts every range out of the blob into an in-memory source map. Files that
|
|
195
|
+
* fail to decrypt are skipped rather than aborting the whole run.
|
|
196
|
+
*/
|
|
197
|
+
declare function decryptRanges(blob: Buffer, ranges: DexRange[], debug?: boolean): DecryptRangesResult;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Derives the AES key from a `libti.cloak.so` buffer and the `salt`.
|
|
201
|
+
* Returns `null` if the buffer is too small or the salt is not 16 bytes.
|
|
202
|
+
*/
|
|
203
|
+
declare function deriveCloakKey(so: Buffer, salt: Buffer): Buffer | null;
|
|
204
|
+
/**
|
|
205
|
+
* Decrypts one `.bin` asset with AES-128-CBC (IV = salt), transparently
|
|
206
|
+
* gunzipping the result when it is gzip-compressed. Returns `null` on failure.
|
|
207
|
+
*/
|
|
208
|
+
declare function decryptCloakAsset(bin: Buffer, key: Buffer, salt: Buffer): Buffer | null;
|
|
209
|
+
/** Heuristic: does this buffer look like decoded UTF-8 text (e.g. JS source)? */
|
|
210
|
+
declare function isProbablyText(buf: Buffer): boolean;
|
|
211
|
+
/**
|
|
212
|
+
* Tries each `libti.cloak.so` buffer, derives a candidate key and confirms it
|
|
213
|
+
* by decrypting `sampleBin` into readable text. Returns the first working key,
|
|
214
|
+
* or `null` if none produce a valid decryption.
|
|
215
|
+
*/
|
|
216
|
+
declare function pickCloakKey(cloakLibs: Buffer[], salt: Buffer, sampleBin: Buffer): Buffer | null;
|
|
217
|
+
|
|
218
|
+
interface BuildInfoInput {
|
|
219
|
+
manifest: ManifestInfo | null;
|
|
220
|
+
memorySource: MemorySource;
|
|
221
|
+
meta?: DecryptMeta;
|
|
222
|
+
developmentMode: boolean;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Combines manifest fields, decryption metadata and the recovered file list
|
|
226
|
+
* into the public {@link TitaniumInfo} shape returned by `TiRecover.info()`.
|
|
227
|
+
*/
|
|
228
|
+
declare function buildInfo(input: BuildInfoInput): TitaniumInfo;
|
|
229
|
+
|
|
230
|
+
interface ReconstructResult {
|
|
231
|
+
/** Remapped sources, everything nested under `Resources/`, plus `tiapp.xml`. */
|
|
232
|
+
memorySource: MemorySource;
|
|
233
|
+
/** The generated tiapp.xml content. */
|
|
234
|
+
tiappXml: string;
|
|
235
|
+
/** Whether the source appears to be an Alloy project. */
|
|
236
|
+
alloy: boolean;
|
|
237
|
+
/** Always true; signals that assets should be nested under `Resources/`. */
|
|
238
|
+
restructured: true;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Produces a reconstructed, Titanium-openable project from recovered sources.
|
|
242
|
+
*/
|
|
243
|
+
declare function reconstruct(memorySource: MemorySource, info: TitaniumInfo): ReconstructResult;
|
|
244
|
+
/** Builds a minimal but valid classic `tiapp.xml` from recovered metadata. */
|
|
245
|
+
declare function buildTiappXml(info: TitaniumInfo): string;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Thrown when an APK uses Titanium's ti.cloak (.bin) encryption and recovery
|
|
249
|
+
* could not complete — typically because the hardcoded `salt` or the bundled
|
|
250
|
+
* `libti.cloak.so` (needed to derive the AES key) is missing.
|
|
251
|
+
*/
|
|
252
|
+
declare class UnsupportedEncryptionError extends Error {
|
|
253
|
+
constructor(reason?: string);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Orchestrates recovery of a Titanium APK: unpack, detect, decrypt/extract,
|
|
257
|
+
* optionally reconstruct a project, and write everything to disk.
|
|
258
|
+
*/
|
|
259
|
+
declare class TiRecover {
|
|
260
|
+
private readonly cwd;
|
|
261
|
+
private config;
|
|
262
|
+
private apkDir;
|
|
263
|
+
private tmpUsed;
|
|
264
|
+
private manifest;
|
|
265
|
+
private dexBuffers;
|
|
266
|
+
private cloakLibs;
|
|
267
|
+
private assetCrypt;
|
|
268
|
+
private developmentMode;
|
|
269
|
+
private tested;
|
|
270
|
+
private isTitanium;
|
|
271
|
+
private memorySource;
|
|
272
|
+
private meta?;
|
|
273
|
+
private restructured;
|
|
274
|
+
constructor(config?: RecoverConfig);
|
|
275
|
+
/** Absolute output directory (resolved against cwd for relative paths). */
|
|
276
|
+
private get outDir();
|
|
277
|
+
private log;
|
|
278
|
+
/**
|
|
279
|
+
* Prepares the working directory. If `apkDir` is supplied it is used directly
|
|
280
|
+
* (reading any `classes*.dex` and manifest found there); otherwise the
|
|
281
|
+
* configured `apk` is unzipped into `tmpDir`.
|
|
282
|
+
*/
|
|
283
|
+
init(): Promise<void>;
|
|
284
|
+
/**
|
|
285
|
+
* Returns `true` if the prepared APK was built with Titanium (in either
|
|
286
|
+
* distribution or development mode), populating internal detection state.
|
|
287
|
+
*/
|
|
288
|
+
test(): Promise<boolean>;
|
|
289
|
+
/**
|
|
290
|
+
* Extracts recovered sources into memory. For distribution builds this
|
|
291
|
+
* decrypts the AES asset blob (from data lifted out of the DEX); for
|
|
292
|
+
* development builds it reads the plain `assets/Resources` tree.
|
|
293
|
+
*/
|
|
294
|
+
extract(): Promise<MemorySource>;
|
|
295
|
+
/**
|
|
296
|
+
* Attempts ti.cloak (.bin) recovery: derive the AES key from a bundled
|
|
297
|
+
* `libti.cloak.so` + the `salt` lifted from `AssetCryptImpl`, then AES-128-CBC
|
|
298
|
+
* decrypt every `Resources/*.bin` asset. Returns the recovered sources, or
|
|
299
|
+
* `null` when the salt/native lib is missing or no key validates.
|
|
300
|
+
*/
|
|
301
|
+
private recoverCloak;
|
|
302
|
+
/**
|
|
303
|
+
* Returns Titanium metadata for the current APK. Must be called after
|
|
304
|
+
* {@link extract} to include the recovered file list.
|
|
305
|
+
*/
|
|
306
|
+
info(): Promise<TitaniumInfo>;
|
|
307
|
+
/**
|
|
308
|
+
* Rebuilds the in-memory sources into a Titanium-openable project structure
|
|
309
|
+
* (everything under `Resources/`, plus a synthesised `tiapp.xml`).
|
|
310
|
+
*/
|
|
311
|
+
reconstruct(): Promise<MemorySource>;
|
|
312
|
+
/** Writes the in-memory sources to the configured `outDir`. */
|
|
313
|
+
writeToDisk(): Promise<WrittenFile[]>;
|
|
314
|
+
/** Copies the APK's static assets and manifest into `outDir`. */
|
|
315
|
+
copyAssets(): Promise<void>;
|
|
316
|
+
/** Removes the temporary directory created during {@link init}. */
|
|
317
|
+
clean(): Promise<void>;
|
|
318
|
+
}
|
|
319
|
+
interface RecoverOptions extends RecoverConfig {
|
|
320
|
+
/** Reconstruct an openable Titanium project layout. Defaults to `false`. */
|
|
321
|
+
reconstruct?: boolean;
|
|
322
|
+
/** Remove the temporary working directory when finished. Defaults to `true`. */
|
|
323
|
+
clean?: boolean;
|
|
324
|
+
}
|
|
325
|
+
interface RecoverResult {
|
|
326
|
+
/** Whether the APK was recognised as a Titanium app and recovered. */
|
|
327
|
+
recovered: boolean;
|
|
328
|
+
/** Titanium metadata, present when `recovered` is true. */
|
|
329
|
+
info?: TitaniumInfo;
|
|
330
|
+
/** Files written to disk, present when `recovered` is true. */
|
|
331
|
+
files?: WrittenFile[];
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* One-shot recovery: unpack, detect, extract, optionally reconstruct, write to
|
|
335
|
+
* disk, copy assets and clean up. Returns whether recovery succeeded plus the
|
|
336
|
+
* gathered info.
|
|
337
|
+
*/
|
|
338
|
+
declare function recover(options: RecoverOptions): Promise<RecoverResult>;
|
|
339
|
+
|
|
340
|
+
export { type AssetCrypt, type AssetCryptResult, type DecryptMeta, type DecryptResult, type DexRange, type ManifestInfo, type MemorySource, type Range, type RecoverConfig, type RecoverOptions, type RecoverResult, type RecoveredFile, TiRecover, type TitaniumInfo, type TitaniumVersion, UnsupportedEncryptionError, type WrittenFile, buildInfo, reconstruct as buildReconstruct, buildTiappXml, decodeJavaInt, decryptCloakAsset, decryptRange, decryptRanges, deriveCloakKey, detectAlloy, extractByteArrayFields, extractCloakSalt, extractRanges, extractStringChunks, instructionWidth, isProbablyText, parseAssetBuffer, parseBinaryManifest, parseManifest, parseRanges, pickCloakKey, readAssetCrypt, recover };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TiRecover,
|
|
3
|
+
UnsupportedEncryptionError,
|
|
4
|
+
buildInfo,
|
|
5
|
+
buildTiappXml,
|
|
6
|
+
decodeJavaInt,
|
|
7
|
+
decryptCloakAsset,
|
|
8
|
+
decryptRange,
|
|
9
|
+
decryptRanges,
|
|
10
|
+
deriveCloakKey,
|
|
11
|
+
detectAlloy,
|
|
12
|
+
extractByteArrayFields,
|
|
13
|
+
extractCloakSalt,
|
|
14
|
+
extractRanges,
|
|
15
|
+
extractStringChunks,
|
|
16
|
+
instructionWidth,
|
|
17
|
+
isProbablyText,
|
|
18
|
+
parseAssetBuffer,
|
|
19
|
+
parseBinaryManifest,
|
|
20
|
+
parseManifest,
|
|
21
|
+
parseRanges,
|
|
22
|
+
pickCloakKey,
|
|
23
|
+
readAssetCrypt,
|
|
24
|
+
reconstruct,
|
|
25
|
+
recover
|
|
26
|
+
} from "./chunk-ML2DCRT6.js";
|
|
27
|
+
export {
|
|
28
|
+
TiRecover,
|
|
29
|
+
UnsupportedEncryptionError,
|
|
30
|
+
buildInfo,
|
|
31
|
+
reconstruct as buildReconstruct,
|
|
32
|
+
buildTiappXml,
|
|
33
|
+
decodeJavaInt,
|
|
34
|
+
decryptCloakAsset,
|
|
35
|
+
decryptRange,
|
|
36
|
+
decryptRanges,
|
|
37
|
+
deriveCloakKey,
|
|
38
|
+
detectAlloy,
|
|
39
|
+
extractByteArrayFields,
|
|
40
|
+
extractCloakSalt,
|
|
41
|
+
extractRanges,
|
|
42
|
+
extractStringChunks,
|
|
43
|
+
instructionWidth,
|
|
44
|
+
isProbablyText,
|
|
45
|
+
parseAssetBuffer,
|
|
46
|
+
parseBinaryManifest,
|
|
47
|
+
parseManifest,
|
|
48
|
+
parseRanges,
|
|
49
|
+
pickCloakKey,
|
|
50
|
+
readAssetCrypt,
|
|
51
|
+
recover
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "titanium-apk-recover",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "Recover the source code from almost any APK made with Appcelerator Titanium (development, distribution or ti.cloak mode).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"titanium-apk-recover": "./dist/cli.js",
|
|
18
|
+
"ti_recover": "./dist/cli.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"lint": "eslint .",
|
|
34
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
35
|
+
"prepublishOnly": "npm run build"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"appcelerator",
|
|
39
|
+
"titanium",
|
|
40
|
+
"apk",
|
|
41
|
+
"android",
|
|
42
|
+
"recover",
|
|
43
|
+
"source",
|
|
44
|
+
"decompile",
|
|
45
|
+
"decompiler",
|
|
46
|
+
"unpack",
|
|
47
|
+
"extract",
|
|
48
|
+
"ti.cloak",
|
|
49
|
+
"reverse-engineering",
|
|
50
|
+
"cli"
|
|
51
|
+
],
|
|
52
|
+
"author": "Pablo Schaffner",
|
|
53
|
+
"license": "Apache-2.0",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "git+https://github.com/puntorigen/ti_recover.git"
|
|
57
|
+
},
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/puntorigen/ti_recover/issues"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/puntorigen/ti_recover#readme",
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"adbkit-apkreader": "^3.2.0",
|
|
64
|
+
"cheerio": "^1.1.2",
|
|
65
|
+
"commander": "^15.0.0",
|
|
66
|
+
"fflate": "^0.8.3",
|
|
67
|
+
"js-beautify": "^2.0.3",
|
|
68
|
+
"libdex-ts": "^1.0.1",
|
|
69
|
+
"picocolors": "^1.1.1"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@eslint/js": "^10.0.1",
|
|
73
|
+
"@types/js-beautify": "^1.14.3",
|
|
74
|
+
"@types/node": "^26.1.0",
|
|
75
|
+
"eslint": "^10.6.0",
|
|
76
|
+
"prettier": "^3.9.4",
|
|
77
|
+
"tsup": "^8.5.1",
|
|
78
|
+
"typescript": "^6.0.3",
|
|
79
|
+
"typescript-eslint": "^8.63.0",
|
|
80
|
+
"vitest": "^4.1.10"
|
|
81
|
+
}
|
|
82
|
+
}
|