-
Notifications
You must be signed in to change notification settings - Fork 0
/
patcher.js
115 lines (98 loc) · 3.34 KB
/
patcher.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const ar = require('ar')
const fs = require('fs')
const tarcdmp = require('./tardcmp')
const ldid = require('./ldid')
const JSZip = require('jszip')
const process = require('process')
const patchBinary = require('./patchBinary')
if (process.argv.length <= 2) {
console.log(`Usage: ${process.argv[0]} ${process.argv[1]} [.deb file]`)
process.exit(-1)
}
const ifname = process.argv[2]
if (!ifname.endsWith('.deb')) {
console.log('Not a deb file')
process.exit(-1)
}
const ofname = ifname.substr(0, ifname.length - 4) + '.patched.zip'
ldid().then(async runtime => {
const debContent = fs.readFileSync(ifname)
const debAr = new ar.Archive(debContent)
const files = debAr.getFiles()
files.forEach(f => {
console.log(f.name(), f.fileSize())
})
const dataF = files
.find(f => f.name().startsWith('data.tar'))
const dataFiles = tarcdmp(dataF.name(), dataF.fileData())
// dylib patch
const payloadFiles = []
const executableFiles = []
const dylibPatchedFiles = dataFiles.map(f => {
let { name, fileData, mode: permissions } = f
if (name.startsWith('./')) name = name.substr(2)
if (name.endsWith('.deb')) return // ignore deb file (maybe metafile?)
if (name.startsWith('Library/')) name = 'var/LIB/' + name.substr(8)
if (name.startsWith('Applications/')) {
console.log(' - Apps with /Applications/ content not supported')
throw new Error('Unsupported .deb')
}
// fix for some offending things
console.log(name, fileData.length)
const buf = Buffer.from(fileData.slice(0, 8))
if (buf.readUInt32BE(0) === 0xcafebabe) {
console.log(` - applying ldid & ldid2`)
fileData = patchBinary(runtime, fileData)
executableFiles.push(name)
}
payloadFiles.push(name)
return { name, fileData, permissions }
}).filter(x => x)
let paylodDirectories = []
payloadFiles.forEach(name => {
let lastSlash
while ((lastSlash = name.lastIndexOf('/')) !== -1) {
name = name.substr(0, lastSlash)
paylodDirectories.push(name)
}
})
paylodDirectories = [...new Set(paylodDirectories)]
paylodDirectories.sort()
const zip = new JSZip()
dylibPatchedFiles.forEach(({ name, fileData, permissions }) => {
zip.file('Payload/' + name, fileData, {
unixPermissions: permissions
})
})
// Create .sh
let installerSh = '#!/var/containers/Bundle/iosbinpack64/bin/sh\n'
installerSh += paylodDirectories.map(name => `mkdir -p "/${name}"\n`).join('')
installerSh += payloadFiles.map(name =>
`cp "Payload/${name}" "/${name}"\n` +
`chown mobile "/${name}"\n`
).join('')
installerSh += executableFiles.map(name => `inject "/${name}"\n`).join('')
zip.file('install', installerSh, {
unixPermissions: '755'
})
let uninstallerSh = '#!/var/containers/Bundle/iosbinpack64/bin/sh\n'
uninstallerSh += payloadFiles.map(name => `rm -f "/${name}"\n`).join('')
zip.file('uninstall', uninstallerSh, {
unixPermissions: '755'
})
zip.file('respring', '#!/var/containers/Bundle/iosbinpack64/bin/sh\nkillall SpringBoard\n', {
unixPermissions: '755'
})
zip
.generateNodeStream({
platform: 'UNIX',
type: 'nodebuffer',
compression: 'deflate',
streamFiles: true
})
.pipe(fs.createWriteStream(ofname))
.on('finish', function () {
console.log('done! Quitting after 3sec')
setTimeout(() => { }, 3000)
})
})