Skip to content

Commit f6b171c

Browse files
authored
feat: add a fuse to speed up the case where no resource is present (#59)
Fixes: #58 Signed-off-by: Darshan Sen <[email protected]>
1 parent 30989e7 commit f6b171c

File tree

7 files changed

+120
-9
lines changed

7 files changed

+120
-9
lines changed

README.markdown

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ ability to inject the resources into pre-built executables, with the
7575
goal that the end result should be as close as possible to what is
7676
obtained by embedding them at build-time.
7777
78+
Note: Other runtime injection implementers should search the binary
79+
compiled with `postject-api.h` for the
80+
`POSTJECT_SENTINEL_fce680ab2cc467b6e072b8b5df1996b2:0` fuse and
81+
flip the last character to `1` to indicate that a resource has been
82+
injected. A different fuse can also be used by defining the
83+
`POSTJECT_SENTINEL_FUSE` macro before including `postject-api.h` and
84+
passing the same string to postject with
85+
`--sentinel-fuse <sentinel_fuse>`.
86+
7887
### Windows
7988
8089
For PE executables, the resources are added into the `.rsrc` section,

postject-api.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef POSTJECT_API_H_
22
#define POSTJECT_API_H_
33

4+
#include <stdbool.h>
45
#include <stddef.h>
56
#include <stdlib.h>
67
#include <string.h>
@@ -17,6 +18,11 @@
1718
#include <windows.h>
1819
#endif
1920

21+
#ifndef POSTJECT_SENTINEL_FUSE
22+
#define POSTJECT_SENTINEL_FUSE \
23+
"POSTJECT_SENTINEL_fce680ab2cc467b6e072b8b5df1996b2"
24+
#endif
25+
2026
struct postject_options {
2127
const char* elf_section_name;
2228
const char* macho_framework_name;
@@ -33,6 +39,11 @@ inline void postject_options_init(struct postject_options* options) {
3339
options->pe_resource_name = NULL;
3440
}
3541

42+
static inline bool postject_has_resource() {
43+
static const volatile char* sentinel = POSTJECT_SENTINEL_FUSE ":0";
44+
return sentinel[sizeof(POSTJECT_SENTINEL_FUSE)] == '1';
45+
}
46+
3647
static const void* postject_find_resource(
3748
const char* name,
3849
size_t* size,

src/api.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const loadPostjectModule = require("./postject.js");
66
async function inject(filename, resourceName, resourceData, options) {
77
const machoSegmentName = options?.machoSegmentName || "__POSTJECT";
88
const overwrite = options?.overwrite || false;
9+
let sentinelFuse =
10+
options?.sentinelFuse ||
11+
"POSTJECT_SENTINEL_fce680ab2cc467b6e072b8b5df1996b2";
912

1013
if (!Buffer.isBuffer(resourceData)) {
1114
throw new TypeError("resourceData must be a buffer");
@@ -112,8 +115,46 @@ async function inject(filename, resourceName, resourceData, options) {
112115
throw new Error("Error when injecting resource");
113116
}
114117

118+
const buffer = Buffer.from(data.buffer);
119+
const firstSentinel = buffer.indexOf(sentinelFuse);
120+
121+
if (firstSentinel === -1) {
122+
throw new Error(
123+
`Could not find the sentinel ${sentinelFuse} in the binary`
124+
);
125+
}
126+
127+
const lastSentinel = buffer.lastIndexOf(sentinelFuse);
128+
129+
if (firstSentinel !== lastSentinel) {
130+
throw new Error(
131+
`Multiple occurences of sentinel "${sentinelFuse}" found in the binary`
132+
);
133+
}
134+
135+
const colonIndex = firstSentinel + sentinelFuse.length;
136+
if (buffer[colonIndex] !== ":".charCodeAt(0)) {
137+
throw new Error(
138+
`Value at index ${colonIndex} must be ':' but '${buffer[
139+
colonIndex
140+
].charCodeAt(0)}' was found`
141+
);
142+
}
143+
144+
const hasResourceIndex = firstSentinel + sentinelFuse.length + 1;
145+
const hasResourceValue = buffer[hasResourceIndex];
146+
if (hasResourceValue === "0".charCodeAt(0)) {
147+
buffer[hasResourceIndex] = "1".charCodeAt(0);
148+
} else if (hasResourceValue != "1".charCodeAt(0)) {
149+
throw new Error(
150+
`Value at index ${hasResourceIndex} must be '0' or '1' but '${hasResourceValue.charCodeAt(
151+
0
152+
)}' was found`
153+
);
154+
}
155+
115156
try {
116-
await fs.writeFile(filename, data);
157+
await fs.writeFile(filename, buffer);
117158
} catch {
118159
throw new Error("Couldn't write executable");
119160
}

src/cli.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ async function main(filename, resourceName, resource, options) {
2828
await inject(filename, resourceName, resourceData, {
2929
machoSegmentName: options.machoSegmentName,
3030
overwrite: options.overwrite,
31+
sentinelFuse: options.sentinelFuse,
3132
});
3233
} catch (err) {
3334
console.log(err.message);
@@ -52,6 +53,11 @@ if (require.main === module) {
5253
"Name for the Mach-O segment",
5354
"__POSTJECT"
5455
)
56+
.option(
57+
"--sentinel-fuse <sentinel_fuse>",
58+
"Sentinel fuse for resource presence detection",
59+
"POSTJECT_SENTINEL_fce680ab2cc467b6e072b8b5df1996b2"
60+
)
5561
.option("--output-api-header", "Output the API header to stdout")
5662
.option("--overwrite", "Overwrite the resource if it already exists")
5763
.action(main)

test/cli.mjs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,14 @@ describe("postject CLI", () => {
7575
{
7676
const { status, stdout, stderr } = spawnSync(
7777
"node",
78-
["./dist/cli.js", filename, "foobar", resourceFilename],
78+
[
79+
"./dist/cli.js",
80+
filename,
81+
"foobar",
82+
resourceFilename,
83+
"--sentinel-fuse",
84+
"NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2",
85+
],
7986
{ encoding: "utf-8" }
8087
);
8188
// TODO(dsanders11) - Enable this once we squelch LIEF warnings
@@ -153,7 +160,9 @@ describe("postject API", () => {
153160

154161
{
155162
const resourceData = await fs.readFile(resourceFilename);
156-
await inject(filename, "foobar", resourceData);
163+
await inject(filename, "foobar", resourceData, {
164+
sentinelFuse: "NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2",
165+
});
157166
}
158167

159168
// Verifying code signing using a self-signed certificate.

test/test.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44
#include "../dist/postject-api.h"
55

66
int main() {
7-
size_t size;
8-
const void* ptr = postject_find_resource("foobar", &size, NULL);
7+
size_t size = 0;
98

10-
if (ptr && size > 0) {
9+
if (postject_has_resource()) {
10+
const void* ptr = postject_find_resource("foobar", &size, NULL);
11+
if (ptr == NULL) {
12+
fprintf(stderr, "ptr must not be NULL.\n");
13+
exit(1);
14+
}
15+
if (size == 0) {
16+
fprintf(stderr, "size must not be 0.\n");
17+
exit(1);
18+
}
1119
char* str = (char*)malloc(size + 1);
1220
memset(str, 0, size + 1);
1321
#if defined(_WIN32)
@@ -17,6 +25,15 @@ int main() {
1725
#endif
1826
printf("%s\n", str);
1927
} else {
28+
const void* ptr = postject_find_resource("foobar", &size, NULL);
29+
if (ptr != NULL) {
30+
fprintf(stderr, "ptr must be NULL.\n");
31+
exit(1);
32+
}
33+
if (size > 0) {
34+
fprintf(stderr, "size must not be greater than 0.\n");
35+
exit(1);
36+
}
2037
printf("Hello world\n");
2138
}
2239

test/test.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
11
#include <iostream>
22
#include <string>
33

4+
#define POSTJECT_SENTINEL_FUSE "NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2"
45
#include "../dist/postject-api.h"
56

67
int main() {
7-
size_t size;
8-
const void* ptr = postject_find_resource("foobar", &size, nullptr);
8+
size_t size = 0;
99

10-
if (ptr && size > 0) {
10+
if (postject_has_resource()) {
11+
const void* ptr = postject_find_resource("foobar", &size, nullptr);
12+
if (ptr == NULL) {
13+
std::cerr << "ptr must not be NULL." << std::endl;
14+
exit(1);
15+
}
16+
if (size == 0) {
17+
std::cerr << "size must not be 0." << std::endl;
18+
exit(1);
19+
}
1120
std::cout << std::string(static_cast<const char*>(ptr), size) << std::endl;
1221
} else {
22+
const void* ptr = postject_find_resource("foobar", &size, nullptr);
23+
if (ptr != nullptr) {
24+
std::cerr << "ptr must be nullptr." << std::endl;
25+
exit(1);
26+
}
27+
if (size > 0) {
28+
std::cerr << "size must not be greater than 0." << std::endl;
29+
exit(1);
30+
}
1331
std::cout << "Hello world" << std::endl;
1432
}
1533

0 commit comments

Comments
 (0)