-
-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathapi.js
More file actions
163 lines (135 loc) · 4.45 KB
/
api.js
File metadata and controls
163 lines (135 loc) · 4.45 KB
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
const { constants, promises: fs } = require("fs");
const path = require("path");
const loadPostjectModule = require("./postject.js");
async function inject(filename, resourceName, resourceData, options) {
const machoSegmentName = options?.machoSegmentName || "__POSTJECT";
const overwrite = options?.overwrite || false;
let sentinelFuse =
options?.sentinelFuse ||
"POSTJECT_SENTINEL_fce680ab2cc467b6e072b8b5df1996b2";
if (!Buffer.isBuffer(resourceData)) {
throw new TypeError("resourceData must be a buffer");
}
try {
await fs.access(filename, constants.R_OK | constants.W_OK);
} catch {
throw new Error("Can't read and write to target executable");
}
let executable;
const postject = await loadPostjectModule();
try {
executable = await fs.readFile(filename);
} catch {
throw new Error("Couldn't read target executable");
}
const executableFormat = postject.getExecutableFormat(executable);
if (executableFormat === postject.ExecutableFormat.kUnknown) {
throw new Error(
"Executable must be a supported format: ELF, PE, or Mach-O"
);
}
let data;
let result;
switch (executableFormat) {
case postject.ExecutableFormat.kMachO:
{
let sectionName = resourceName;
// Mach-O section names are conventionally of the style __foo
if (!sectionName.startsWith("__")) {
sectionName = `__${sectionName}`;
}
({ result, data } = postject.injectIntoMachO(
executable,
machoSegmentName,
sectionName,
resourceData,
overwrite
));
if (result === postject.InjectResult.kAlreadyExists) {
throw new Error(
`Segment and section with that name already exists: ${machoSegmentName}/${sectionName}\n` +
"Use --overwrite to overwrite the existing content"
);
}
}
break;
case postject.ExecutableFormat.kELF:
{
// ELF sections usually start with a dot ("."), but this is
// technically reserved for the system, so don't transform
let sectionName = resourceName;
({ result, data } = postject.injectIntoELF(
executable,
sectionName,
resourceData,
overwrite
));
if (result === postject.InjectResult.kAlreadyExists) {
throw new Error(
`Section with that name already exists: ${sectionName}` +
"Use --overwrite to overwrite the existing content"
);
}
}
break;
case postject.ExecutableFormat.kPE:
{
// PE resource names appear to only work if uppercase
resourceName = resourceName.toUpperCase();
({ result, data } = postject.injectIntoPE(
executable,
resourceName,
resourceData,
overwrite
));
if (result === postject.InjectResult.kAlreadyExists) {
throw new Error(
`Resource with that name already exists: ${resourceName}\n` +
"Use --overwrite to overwrite the existing content"
);
}
}
break;
}
if (result !== postject.InjectResult.kSuccess) {
throw new Error("Error when injecting resource");
}
const buffer = Buffer.from(data.buffer);
const firstSentinel = buffer.indexOf(sentinelFuse);
if (firstSentinel === -1) {
throw new Error(
`Could not find the sentinel ${sentinelFuse} in the binary`
);
}
const lastSentinel = buffer.lastIndexOf(sentinelFuse);
if (firstSentinel !== lastSentinel) {
throw new Error(
`Multiple occurences of sentinel "${sentinelFuse}" found in the binary`
);
}
const colonIndex = firstSentinel + sentinelFuse.length;
if (buffer[colonIndex] !== ":".charCodeAt(0)) {
throw new Error(
`Value at index ${colonIndex} must be ':' but '${buffer[
colonIndex
].charCodeAt(0)}' was found`
);
}
const hasResourceIndex = firstSentinel + sentinelFuse.length + 1;
const hasResourceValue = buffer[hasResourceIndex];
if (hasResourceValue === "0".charCodeAt(0)) {
buffer[hasResourceIndex] = "1".charCodeAt(0);
} else if (hasResourceValue != "1".charCodeAt(0)) {
throw new Error(
`Value at index ${hasResourceIndex} must be '0' or '1' but '${hasResourceValue.charCodeAt(
0
)}' was found`
);
}
try {
await fs.writeFile(filename, buffer);
} catch {
throw new Error("Couldn't write executable");
}
}
module.exports = { inject };