nodejs / postject

Easily inject arbitrary read-only resources into executable formats (Mach-O, PE, ELF) and use it at runtime.
Other
180 stars 13 forks source link

Error: Multiple occurences of sentinel found in the binary #92

Open istir opened 9 months ago

istir commented 9 months ago

Hello, I'm trying to build a single executable app and encountered this problem:

➜ echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js
➜ echo '{ "main": "hello.js", "output": "sea-prep.blob" }' > sea-config.json
➜ node --experimental-sea-config sea-config.json
Wrote single executable preparation blob to sea-prep.blob
➜ cp $(command -v node) hello
➜ codesign --remove-signature hello
➜ npx postject hello NODE_SEA_BLOB sea-prep.blob \
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
    --macho-segment-name NODE_SEA
Start injection of NODE_SEA_BLOB in hello...
Error: Multiple occurences of sentinel "NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2" found in the binary

I also tried with pnpx instead of npx. Some information about my environment:

➜ uname -a 
Darwin istirs-Air.lan 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:53:34 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T8103 arm64
➜ node -v 
v21.5.0
➜ npx -v 
10.2.4
bulentv commented 9 months ago

Same on Darwin 23.1.0 with node v21.5.0, injection fails.

I also tested v22.0.0-nightly20240104084d761dfc, which injection appears to be succeeded but the produced binary still doesn't work.

...
➜ npx postject hello NODE_SEA_BLOB sea-prep.blob \
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
    --macho-segment-name NODE_SEA
npm WARN cli npm v10.2.4 does not support Node.js v22.0.0-nightly20240104084d761dfc. This version of npm supports the following node versions: `^18.17.0 || >=20.5.0`. You can find the latest version at https://nodejs.org/.
Start injection of NODE_SEA_BLOB in hello...
💉 Injection done!
➜ ./hello world
zsh: killed     ./hello world
targos commented 9 months ago

@nodejs/single-executable

cceevv commented 4 months ago

.../postject/dist/api.js

...

  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`
    );
  }

...

I tried to comments the Error lines, and everything works.

guymguym commented 2 months ago

Same for me on MacOS 14.5 with M1 cpu, Darwin Kernel Version 23.5.0. Tried both with nodejs 20.16.0 and 22.5.1.

in sea-config.json I have:

{
  "main": "build/sea.js",
  "output": "build/sea.blob",
  "disableExperimentalSEAWarning": true,
  "useSnapshot": false,
  "useCodeCache": false,
  "assets": {}
}
Patrick-Beuks commented 1 month ago

I had the same problem, but found the bug and have a workaround. For me the problem was that I had the sentinel fuse inside of the package.json and this was included in the blob. This in turn confuses if (firstSentinel !== lastSentinel) { as this is indeed not true, even if the that fuse was not used.

I fixed it by base64 encoding the string and in the command decode it before using