1
1
import { memcpy , download } from './transfer' ;
2
2
import { normalize } from './path' ;
3
3
import { freeze , wakeup } from './threads' ;
4
- import c from '../cmod' ;
5
4
6
- type EncryptInfoTuple = [ NativePointer , number , number , number , number ] ;
5
+ const MH_MAGIC_64 = 0xfeedfacf ;
6
+ const LC_ENCRYPTION_INFO = 0x21 ;
7
+ const LC_ENCRYPTION_INFO_64 = 0x2c ;
7
8
8
- interface ISet {
9
- [ key : string ] : boolean ;
10
- }
9
+ type EncryptInfoTuple = [ NativePointer , number , number , number , number ] ;
11
10
12
11
interface Option {
13
12
executableOnly ?: boolean
@@ -27,6 +26,36 @@ export function base() {
27
26
return normalize ( ObjC . classes . NSBundle . mainBundle ( ) . bundlePath ( ) . toString ( ) ) ;
28
27
}
29
28
29
+ function findCryptInfo ( header : NativePointer ) {
30
+ const magic = header . readU32 ( ) ;
31
+ if ( magic !== MH_MAGIC_64 ) {
32
+ throw new Error ( `Unsupported magic ${ magic . toString ( 16 ) } ` ) ;
33
+ }
34
+
35
+ const ncmds = header . add ( 16 ) . readU32 ( ) ;
36
+ const cmds = header . add ( 32 ) ;
37
+
38
+ let offsetOfCmd = 0 ;
39
+ let sizeOfCmd = 0 ;
40
+ let offset = 0 ;
41
+ let size = 0 ;
42
+
43
+ for ( let i = 0 ; i < ncmds ; i ++ ) {
44
+ const cmd = cmds . add ( offsetOfCmd ) . readU32 ( ) ;
45
+ sizeOfCmd = cmds . add ( offsetOfCmd + 4 ) . readU32 ( ) ;
46
+
47
+ if ( cmd === LC_ENCRYPTION_INFO || cmd === LC_ENCRYPTION_INFO_64 ) {
48
+ offset = cmds . add ( offsetOfCmd + 8 ) . readU32 ( ) ;
49
+ size = cmds . add ( offsetOfCmd + 12 ) . readU32 ( ) ;
50
+ return [ cmds . add ( offsetOfCmd ) , offset , size , offsetOfCmd , sizeOfCmd ] as EncryptInfoTuple ;
51
+ }
52
+
53
+ offsetOfCmd += sizeOfCmd ;
54
+ }
55
+
56
+ throw new Error ( 'Cannot find crypt info' ) ;
57
+ }
58
+
30
59
export async function dump ( opt : Option = { } ) {
31
60
// load all frameworks
32
61
warmup ( ) ;
@@ -35,20 +64,20 @@ export async function dump(opt: Option = {}) {
35
64
freeze ( ) ;
36
65
37
66
const bundle = base ( ) ;
38
- const downloaded : ISet = { } ;
67
+ const downloaded = new Set < string > ( ) ;
39
68
for ( let mod of Process . enumerateModules ( ) ) {
40
69
const filename = normalize ( mod . path ) ;
41
70
if ( ! filename . startsWith ( bundle ) )
42
71
continue ;
43
72
44
- const info = findEncyptInfo ! ( mod . base ) as EncryptInfoTuple ;
73
+ const info = findCryptInfo ( mod . base ) as EncryptInfoTuple ;
45
74
const [ ptr , offset , size , offsetOfCmd , sizeOfCmd ] = info ;
46
75
47
76
if ( ptr . isNull ( ) )
48
77
continue ;
49
78
50
79
await download ( filename ) ;
51
- downloaded [ filename ] = true ;
80
+ downloaded . add ( filename ) ;
52
81
53
82
// skip fat header
54
83
const fatOffset = Process . findRangeByAddress ( mod . base ) ! . file ! . offset ;
@@ -71,7 +100,7 @@ export async function dump(opt: Option = {}) {
71
100
72
101
}
73
102
74
- async function pull ( bundle : string , downloaded : ISet ) {
103
+ async function pull ( bundle : string , downloaded : Set < string > ) {
75
104
const manager = ObjC . classes . NSFileManager . defaultManager ( ) ;
76
105
const enumerator = manager . enumeratorAtPath_ ( bundle ) ;
77
106
const pIsDir = Memory . alloc ( Process . pointerSize ) ;
@@ -85,7 +114,7 @@ async function pull(bundle: string, downloaded: ISet) {
85
114
continue ;
86
115
87
116
const fullname = normalize ( base . stringByAppendingPathComponent_ ( path ) ) ;
88
- if ( downloaded [ fullname ] )
117
+ if ( downloaded . has ( fullname ) )
89
118
continue ;
90
119
91
120
pIsDir . writePointer ( NULL ) ;
@@ -96,8 +125,6 @@ async function pull(bundle: string, downloaded: ISet) {
96
125
}
97
126
}
98
127
99
- const cm = new CModule ( c ) ;
100
- const findEncyptInfo = new NativeFunction ( cm [ 'find_encryption_info' ] , [ 'pointer' , 'uint32' , 'uint32' , 'uint32' , 'uint32' ] , [ 'pointer' ] ) ;
101
128
102
129
export function warmup ( ) : void {
103
130
const { NSFileManager, NSBundle } = ObjC . classes
0 commit comments