nodejs / postject

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

fix: crash in `postject_find_resource()` on Linux #77

Closed RaisinTen closed 1 year ago

RaisinTen commented 1 year ago

The program headers base address values returned by getauxval(AT_PHDR) and dl_iterate_phdr() are identical only on g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0. However, the values are totally different on clang version 10.0.0-4ubuntu1 and g++ (GCC) 8.5.0 20210514 (Red Hat 8.5.0-16). Since the dl_iterate_phdr() approach seems to work for all the 3 compilers, I think we should proceed with that.

Turns out, this happens because PIE is not enabled by default on the compilers which produce binaries that are crashing.

Fixes: https://github.com/nodejs/postject/issues/70 Refs: https://github.com/nodejs/postject/issues/76 (actually Fixes: but I'll add Fixes: on the postject version upgrade PR in Node.js)

(Node.js PR to test this - https://github.com/nodejs/node/pull/46868)

RaisinTen commented 1 year ago

It is indeed a PIE vs non-PIE issue, good catch!

It changes behavior since it will iterate through all shared objects loaded for the program, and would find notes in shared libraries, which could lead to buggy behavior, and/or open security concerns.

From my testing, it looks like running the callback only on the program headers of the current executable works and I think it addresses your blocking concern. I've pushed a commit for that.

robertgzr commented 1 year ago

unless we just want to avoid the callback mechanism here, using the approach from this PR looks better.

I was playing around with the parsing code and read the elf spec again, specifically how it behaves when PIE come into play.

phdr->p_vaddr will be relative when compiling with -pie which is the default, and absolute when -no-pie, but the original implementation unconditionally adds base_addr to it :P

so we could fix it by checking if (p_vaddr - p_offset) == 0 and only then adding base_addr, but it's nice that dl_iterate_phdr already takes PIE/non-PIE into account

ShenHongFei commented 1 year ago

I verified it with the latest Node.js 19.8.1 version, run postject from the windows system, inject my-app.js into the linux node executable file, generate my-app, and then run it successfully under the linux system, no segfault

@targos Looks like this issue has been successfully fixed https://github.com/nodejs/postject/issues/76

[root@vm ~]# ./my-app
{
  node: '19.8.1',
  acorn: '8.8.2',
  ada: '1.0.4',
  ares: '1.19.0',
  brotli: '1.0.9',
  cldr: '42.0',
  icu: '72.1',
  llhttp: '8.1.0',
  modules: '111',
  napi: '8',
  nghttp2: '1.52.0',
  nghttp3: '0.7.0',
  ngtcp2: '0.8.1',
  openssl: '3.0.8+quic',
  simdutf: '3.2.2',
  tz: '2022g',
  undici: '5.21.0',
  unicode: '15.0',
  uv: '1.44.2',
  uvwasi: '0.0.16',
  v8: '10.8.168.25-node.12',
  zlib: '1.2.13'
}
[ '/root/my-app', './my-app' ]
(node:394) ExperimentalWarning: Single executable application is an experimental feature and might change at any time
(Use `my-app --trace-warnings ...` to show where the warning was created)