fixes. dnload mode should now be usableish

This commit is contained in:
PoroCYon 2019-03-12 15:21:29 +01:00 committed by PoroCYon
parent 26d96fc2c1
commit d9dbaae27a
11 changed files with 65 additions and 42 deletions

View File

@ -18,7 +18,7 @@ CXXOPTFLAGS=$(COPTFLAGS) -fno-exceptions \
-fno-rtti -fno-enforce-eh-specs -fnothrow-opt -fno-use-cxa-get-exception-ptr \
-fno-implicit-templates -fno-threadsafe-statics -fno-use-cxa-atexit
CFLAGS=-Wall -Wextra -Wpedantic -std=gnu11 -nostartfiles -fno-PIC $(COPTFLAGS)
CFLAGS=-Wall -Wextra -Wpedantic -std=gnu11 -nostartfiles -fno-PIC $(COPTFLAGS) #-DUSE_DL_FINI
CXXFLAGS=-Wall -Wextra -Wpedantic -std=c++11 $(CXXOPTFLAGS) -nostartfiles -fno-PIC
ASFLAGS=-I $(SRCDIR)/
@ -40,13 +40,14 @@ CXXFLAGS += -m$(BITS) $(shell pkg-config --cflags sdl2)
LIBS=-lc
SMOLFLAGS += -d
ASFLAGS += -DUSE_INTERP -DALIGN_STACK -DNO_START_ARG #-DUSE_DT_DEBUG
SMOLFLAGS +=
ASFLAGS += -DUSE_INTERP -DALIGN_STACK
#-DUSE_DNLOAD_LOADER #-DUSE_DT_DEBUG #-DUSE_DL_FINI #-DNO_START_ARG #-DUNSAFE_DYNAMIC
NASM ?= nasm
PYTHON3 ?= python3
all: $(BINDIR)/hello-crt $(BINDIR)/sdl-crt $(BINDIR)/flag-crt $(BINDIR)/hello-_start
all: $(BINDIR)/hello-crt $(BINDIR)/sdl-crt $(BINDIR)/flag $(BINDIR)/hello-_start
LIBS += $(filter-out -pthread,$(shell pkg-config --libs sdl2)) -lX11 #-lGL

View File

@ -8,24 +8,32 @@ PoC by Shiz, bugfixing and 64-bit version by PoroCYon.
```sh
./smol.py -lfoo -lbar input.o... smol-output.asm
nasm -I src/ [-DUSE_INTERP] [-DALIGN_STACK] [-DUSE_NX] [-DUSE_DL_FINI] \
[-DUSE_DT_DEBUG] [-DSKIP_ENTRIES] -o nasm-output.o smol-output.asm
ld -T ld/link.ld -o binary nasm-output.o input.o...
nasm -I src/ [-Doption ...] -o nasm-output.o smol-output.asm
ld -T ld/link.ld --oformat=binary -o output.elf nasm-output.o input.o...
# or cc -T ld/link.ld -Wl,--oformat=binary -o output.elf nasm-output.o input.o...
```
* `USE_INTERP`: Include an interp segment in the output ELF file. If not, the
dynamic linker **must** be invoked *explicitely*! (You probably want to
enable this.)
enable this.) Costs the size of a phdr plus the size of the interp string.
* `ALIGN_STACK`: *64-bit only*: realign the stack so that SSE instructions
won't segfault.
* `USE_NX`: Don't use `RWE` segments at all. Not very well tested.
* `USE_DL_FINI`: keep track of the `_dl_fini` function and pass it to `_start`.
won't segfault. Costs 1 byte.
* `USE_NX`: Don't use `RWE` segments at all. Not very well tested. Costs the
size of 1 phdr.
* `USE_DL_FINI`: keep track of the `_dl_fini` function and pass it to your
`_start`. Costs 2 bytes, plus maybe a few more depending on how it's passed
to `__libc_start_main`.
* `USE_DT_DEBUG`: retrieve the `struct link_map` from the `r_debug` linker
data (which is placed at `DT_DEBUG` at startup) instead of exploiting data
leakage from `_dt_start_user`. Might be more compatible, but strictly worse
size-wise on i386, and probably on x86_64 as well.
leakage from `_dt_start_user`. Might be more compatible and compressable, but
strictly worse size-wise by 10 (i386) or 3 (x86_64) bytes.
* `SKIP_ENTRIES`: skip the first two entries of the `struct link_map`, which
represent the main binary and the vDSO.
represent the main binary and the vDSO. Costs around 5 bytes.
* `USE_DNLOAD_LOADER`: *64-bit only*: use the symbol loading mechanism as used
in dnload (i.e. traverse the symtab of the imported libraries). Slightly
larger, but probably better compressable.
* `NO_START_ARG`: *don't* pass the stack pointer to `_start` as the first arg.
Will make it unable to read argc/argv/environ, but gives you 3 bytes.
```
usage: smol.py [-h] [-m TARGET] [-l LIB] [-L DIR] [--nasm NASM] [--cc CC]
@ -58,6 +66,9 @@ imported by a `smol`-ified binary. This can thus be used to detect user mistakes
during dynamic linking. (Think of it as an equivalent of `ldd`, except that it
also checks whether the imported functions are present as well.)
***NOTE***: `smoldd.py` currently doesn't support 64-bit binaries anymore, as
there's currently no (good) way of retrieving the symbol hash table anymore.
## Internal workings
`smol.py` inspects the input object files for needed library files and symbols.

View File

@ -30,8 +30,8 @@ def main():
parser.add_argument('--readelf', default=shutil.which('readelf'), \
help="which readelf binary to use")
parser.add_argument('-d', '--dnload', default=False, action='store_true', \
help="Use dnload's mechanism of importing functions. Slightly larger, but usually better compressable.")
# parser.add_argument('-d', '--dnload', default=False, action='store_true', \
# help="Use dnload's mechanism of importing functions. Slightly larger, but usually better compressable.")
# parser.add_argument('--libsep', default=False, action='store_true', \
# help="Separete import symbols per library, instead of looking at every library when resolving a symbol.")
@ -66,7 +66,7 @@ def main():
symbols.setdefault(library, [])
symbols[library].append((symbol, reloc))
output(arch, symbols, args.dnload, args.output)
output(arch, symbols, args.output)
if __name__ == '__main__':
main()

View File

@ -51,20 +51,26 @@ def output_x86(libraries, outf):
# end output_x86
def output_amd64(libraries, dnload, outf):
def output_amd64(libraries, outf):
outf.write('; vim: set ft=nasm:\n')
outf.write('bits 64\n')
if dnload:
outf.write('%define USE_DNLOAD_LOADER\n')
shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }
outf.write('%include "header64.asm"\n')
outf.write('dynamic.needed:\n')
for library in libraries:
outf.write('dq 1;DT_NEEDED\n')
outf.write('dq (_symbols.{} - _strtab)\n'.format(shorts[library]))
outf.write('dynamic.end:\n')
outf.write(' dq 1;DT_NEEDED\n')
outf.write(' dq (_symbols.{} - _strtab)\n'.format(shorts[library]))
outf.write("""\
dynamic.symtab:
dq DT_SYMTAB ; d_tag
dq 0 ; d_un.d_ptr
dynamic.end:
%ifndef UNSAFE_DYNAMIC
dq DT_NULL
%endif
""")
outf.write('[section .rodata.neededlibs]\n')
@ -110,9 +116,9 @@ global {name}
# end output_amd64
def output(arch, libraries, dnload, outf):
def output(arch, libraries, outf):
if arch == 'i386': output_x86(libraries, outf)
elif arch == 'x86_64': output_amd64(libraries, dnload, outf)
elif arch == 'x86_64': output_amd64(libraries, outf)
else:
eprintf("E: cannot emit for arch '" + str(arch) + "'")
sys.exit(1)

View File

@ -15,8 +15,11 @@ __attribute__((__externally_visible__, __section__(".text.startup._start"),
, __naked__
#endif
))
int _start(void* stack) {
// TODO: _dl_fini etc.
int _start(void* stack
#ifdef USE_DL_FINI
, void (*dl_fini)(void)
#endif
) {
int argc=*(size_t*)stack;
char** argv=(void*)(&((size_t*)stack)[1]);
@ -32,7 +35,13 @@ int _start(void* stack) {
:"S"(argc), "D" (main), "d" (argv)
:);
#else
__libc_start_main(main, argc, argv, NULL, NULL, NULL, (void*)stack);
__libc_start_main(main, argc, argv, NULL, NULL,
#ifdef USE_DL_FINI
dl_fini
#else
NULL
#endif
, (void*)stack);
#endif
__builtin_unreachable();

View File

@ -28,6 +28,7 @@
%define PHDR_W (2)
%define PHDR_X (1)
%define DT_NULL ( 0)
%define DT_STRTAB ( 5)
%define DT_SYMTAB ( 6)
%define DT_DEBUG (21)

View File

@ -100,9 +100,6 @@ dynamic:
dynamic.strtab:
dq DT_STRTAB ; d_tag
dq _strtab ; d_un.d_ptr
dynamic.symtab:
dq DT_SYMTAB ; d_tag
dq 0 ; d_un.d_ptr
%ifdef USE_DT_DEBUG
dynamic.debug:
dq DT_DEBUG ; d_tag

View File

@ -236,14 +236,11 @@ repne scasd ; technically, scasq should be used, but meh. this is 1 byte smaller
mov rdi, rsp
%endif
%ifdef ALIGN_STACK
%ifdef USE_DNLOAD_LOADER
push rax
%else
; apparently not needed?
%endif
%endif
%ifdef USE_DL_FINI
xchg rsi, r13 ; _dl_fini
%endif
; fallthru to _start
;.loopme: jmp short .loopme

View File

@ -10,9 +10,9 @@
#define COLOR(r, g, b) ((r << 16) + (g << 8) + b)
/*__attribute__((__used__,__externally_visible__))
void _start() {*/
int main() {
__attribute__((__used__,__externally_visible__))
void _start() {
/*int main() {*/
#ifdef MAKE_ESC_WORK
Atom wmDeleteMessage;
#endif

View File

@ -1,8 +1,8 @@
#include <stdlib.h>
#include <stdio.h>
__attribute__((__section__(".rodata.hello-_start$f")))
const static char *f = "foo";
/*__attribute__((__section__(".rodata.hello-_start$f")))
static const char *f = "foo";*/
__attribute__((__externally_visible__, __section__(".text.startup._start"),
__noreturn__
@ -11,7 +11,8 @@ __attribute__((__externally_visible__, __section__(".text.startup._start"),
#endif
))
int _start(void) {
puts("Hello World!");//printf("hello world %s\n", f);
//printf("hello world %s\n", f);
puts("Hello World!");
asm volatile("int3");//exit(42);
__builtin_unreachable();
}

View File

@ -5,7 +5,7 @@ int main(void) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *w = SDL_CreateWindow("nice", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_OPENGL);
printf("%s\n", "ohai");
puts("ohai");//printf("%s\n", "ohai");
SDL_Delay(3000);
SDL_DestroyWindow(w);
return 0;