Fix #2 and #3 at once

Also make the formatting a bit nicer, remove obsolete comments, and add
a link to autovndh.py in the README
This commit is contained in:
PoroCYon 2019-08-26 04:08:09 +02:00
parent 1e8dd70a46
commit 13f7ed75d8
4 changed files with 133 additions and 135 deletions

View File

@ -1,4 +1,4 @@
Copyright 2018 PoroCYon.
Copyright 2018-2019 PoroCYon and contributors.
This software is provided "as is", without any express or implied warranties,
including but not limited to the implied warranties of merchantability and

View File

@ -1,8 +1,8 @@
default: test
testbin: test.c
$(CC) -static -nostdlib -nostartfiles -O3 -s -o "$@" "$<"
testbin: hello.c #test.c
$(CC) -O3 -s -o "$@" "$<" #-static -nostdlib -nostartfiles
%.gz: %
< "$<" gzip -cnk9 > "$@"
@ -10,11 +10,12 @@ testbin: test.c
< "$<" lzma --format=lzma -9 --extreme --lzma1=preset=9,lc=1,lp=0,pb=0 --keep --stdout > "$@"
vondehi: vondehi.asm
nasm -fbin -o"$@" "$<"
nasm -fbin -DNO_CHEATING -o"$@" "$<"
chmod +x "$@"
test: vondehi testbin.lzma
cat $^ > test && chmod +x test && strace -f ./test; echo $?
-cat $^ > test && chmod +x test && strace -f ./test
wc -c "$<"
.PHONY: default test

View File

@ -10,18 +10,20 @@ but even smaller. It doesn't have a 64-bit version, though.**
## Comparison
| mode etc. | vondehi | trident | Fishypack | sh-based unpacker |
|:------------ | -------:| -------:| ---------:| -----------------:|
| gzip, 32-bit | 161 | 172 | 179? (198?) | 48 to 72 |
| xz, 32-bit | 164 (168*) | 179 | 186 | 48 to 72 |
| gzip, 64-bit | N/A | 208 | 208? | 48 to 72 |
| xz, 64-bit | N/A | 217 | 217 | 48 to 72 |
| Preserves args | Y | N | tries to | can, but often not |
| Min. platform | Linux 3.19 | Linux 2.27 | Linux 2.27 | Most Unices |
| Touches filesystem | N | N | N | Y |
| mode etc. | vondehi | trident | Fishypack | sh-based unpacker |
|:------------------ | ----------:| ----------:| -----------:| ------------------:|
| gzip, 32-bit | 161 | 172 | 179? (198?) | 48 to 72 |
| xz, 32-bit | 164 (168*) | 179 | 186 | 48 to 72 |
| gzip, 64-bit | N/A | 208 | 208? | 48 to 72 |
| xz, 64-bit | N/A | 217 | 217 | 48 to 72 |
| Preserve arg & env | Y/N | N | tries to | can, but often not |
| Min. platform | Linux 3.19 | Linux 2.27 | Linux 2.27 | Most Unices |
| Touches filesystem | N | N | N | Y |
\*: with `NO_UBUNTU_COMPAT` **dis**abled.
All values are with `NO_CHEATING` **dis**abled. If this is enabled, add 5 bytes.
The exact size of a shell-based unpacker depends on the exact impmelentation,
many variations exist. 'xz' means the usage of `xzcat` instead of `zcat`,
the former supports both `xz`- and `lzma`-compressed data.
@ -36,22 +38,31 @@ kernel is 64-bit and supports the 32-bit emulation layer.
```
nasm -fbin -o$out vondehi.asm [-DUSE_GZIP] [-DTAG="j0!"] [-DNO_UBUNTU_COMPAT] \
[-DUSE_VFORK]
[-DUSE_VFORK] [-DNO_CHEATING]
cat $out $intro_compressed > $final
```
See also [autovndh.py](https://pcy.be/tmp/src/autovndh.py), a script that
brute-forces all compression parameters to find the optimal binary.
### Settings
* `USE_GZIP` (default off): use `gzip` (`/bin/zcat`) instead of `xz`
(`/usr/bin/xzcat`).
* `NO_UBUNTU_COMPAT` (default off): assume `/bin` is the same as `/usr/bin`.
Originally named like this because on my machine, `/bin` is linked t
Originally named like this because on my machine, `/bin` is linked to
`/usr/bin`, but on the Revision compomachine (which runs Ubuntu), it isn't.
* `NO_FILE_MANAGER_COMPAT` (default off): save two bytes by putting instructions in the EI_CLASS and EI_DATA fields of the ELF header. Causes executables packed with vondehi to not be recognized as executable in file managers.
* `NO_FILE_MANAGER_COMPAT` (default off): save two bytes by putting
instructions in the EI_CLASS and EI_DATA fields of the ELF header. Causes
executables packed with vondehi to not be recognized as executable in file
managers.
* `USE_VFORK` (default off): use `vfork(2)` instead of `fork(2)`. I hope you
know what you're doing when you enable this.
* `TAG` (default empty): add a vanity tag right before the compressed data.
Only use this when you have bytes to spare, of course.
* `NO_CHEATING` (default off): don't assume file descriptor numbers and
properly pass arguments and environment variable to the payload. You need
this if you're running on Wayland. Costs 5 bytes.
## How to debug it if it doesn't work
@ -71,6 +82,10 @@ cat $out $intro_compressed > $final
* Faemiyah, yx, etc., for small sh-based unpackers (yx: nice trick with
the script partially embedded in the gzip file!)
### Extra thanks to:
* blackle, greg, and others for contributions
## License
[SAL](LICENSE).

View File

@ -1,7 +1,5 @@
; vim: set ft=nasm noet:
;%define USE_GZIP
; vim: set ft=nasm noet ts=4:
%define STDIN_FILENO 0
%define STDOUT_FILENO 1
@ -26,159 +24,143 @@ bits 32
org 0xEBDB0000
ehdr: ;~e_ident
; jg short 0x47 (inc ebp) ; dec esp ; inc esi
db 0x7F,"ELF" ;!E_MAGIC
ehdr: ;~e_ident
; jg short 0x47 (inc ebp) ; dec esp ; inc esi
db 0x7F,"ELF" ;!EI_MAGIC
%ifndef NO_FILE_MANAGER_COMPAT
db 0x01 ;E_CLASS
db 0x01 ;E_DATA
db 0x01 ;!EI_CLASS
db 0x01 ;!EI_DATA
%endif
_parent.0:
%ifdef NO_FILE_MANAGER_COMPAT
xor ebx, ebx
xor ebx, ebx ; EI_CLASS, EI_DATA
%endif
xchg eax, edi ;edi is zero now and will be overwritten eventually
mov al, SYS_waitpid
int 0x80
lea esi, [esp+0x08]
xchg eax, esi ; esi is zero now ; EI_VERSION
mov al, SYS_waitpid ; EI_OSABI, EI_ABIVERSION
int 0x80 ; EI_PAD1, EI_PAD2
db 0x3D ; cmp eax, ...
dw 2 ;!e_type
dw 3 ;!e_machine
%ifdef NO_CHEATING
pop eax ; EI_PAD3
sub esp, 0x18 ; FIXME: HACK (see below) ; EI_PAD4..6
%else
lea esi, [esp + 0x08] ; EI_PAD3..6
%endif
db 0x3D ; cmp eax, ... ; EI_PAD7
dw 2 ;!e_type
dw 3 ;!e_machine
_parent.1:
mov edx, esp
lea ecx, [ebp+__strempty-__self+EBP_bias]
add bl, bl
db 0xEB ; jmp short _parent.2
;dd _start ;!e_entry
dd phdr-ehdr ;!e_phoff ; 0x33
%ifdef NO_CHEATING
push edi ; subs 4 from esp, so we need to take this into account in the
; HACK lines as well ; e_version0
lea esi, [esp+4*eax+0x20] ; FIXME: HACK ; e_version1..3, !e_entry0
; the last byte here^^^^ needs to be 0x20 to have a correct entrypoint
; if you want to optimize this, you probably want to move the entrypt
; somewhere else, but that means you have to change everything else
; with it as well, so good luck.
%else
mov edx, esp ; e_version0..1
lea ecx, [ebp+__strempty-__self+EBP_bias] ; e_version2..3, !e_entry0
%endif
add bl, bl ;!e_entry1..2
db 0xEB ; jmp short _parent.2 ;!e_entry3
dd phdr-ehdr ; 0x33 ;!e_phoff
_start:
mov ax, SYS_memfd_create
mov ebx, esp
jmp short _start.1
;dd 0 ; e_shoff
;dd 0 ; e_flags
dw ehdr.end-ehdr;!e_ehdrsize ; 0x34 0x00
dw phdr.end-phdr;!e_phentsize ; 0x20 0x00
dw 1 ;!e_phnum
mov ax, SYS_memfd_create ; e_shoff
mov ebx, esp ; e_flags0..1
jmp short _start.1 ; e_flags2..3
dw ehdr.end-ehdr ; 0x34 0x00 ;!e_ehdrsize
dw phdr.end-phdr ; 0x20 0x00 ;!e_phentsize
dw 1 ;!e_phnum
_start.1:
int 0x80
pop eax
jmp short _start.2
;dw 0 ; e_shentsize
;dw 0 ; e_shnum
;dw 0 ; e_shstrndx
int 0x80 ; e_shentsize
%ifdef NO_CHEATING
xchg edi, eax ; e_shnum0
%else
pop eax ; e_shnum0
%endif
jmp short _start.2 ; e_shnum1, e_shstrndx0
phdr:
db 1 ;!p_type
db 1 ;! p_type0 ; e_shstrndx1
ehdr.end:
times 3 db 0
dd 0 ;!p_offset
dd ehdr ;!p_vaddr
times 3 db 0 ;! p_type1
dd 0 ;!p_offset
dd ehdr ;!p_vaddr
_start.2:
mov ebp, __self-EBP_bias
;dd ehdr ; p_paddr
jmp short _start.3
db 0,0xEB
;dd filesize ;~p_filesz
jmp short _start.3+4
db 0
;dd filesize ;~p_memsz
db 5 ;~p_flags
mov ebp, __self-EBP_bias;!p_paddr, p_filesz0
jmp short _start.3 ;~p_filesz1..2
db 0 ;~p_filesz3
db 0xEB ;~p_memsz0
jmp short _start.3+4 ;~p_memsz1..2
db 0 ;~p_memsz3
db 5 ;~p_flags0
_start.3:
%ifdef USE_VFORK
mov al, SYS_vfork
mov al, SYS_vfork ;~p_flags1..2
%else
mov al, SYS_fork
mov al, SYS_fork ;~p_flags1..2
%endif
jmp short _start.4
jmp short _start.4 ;~p_flags3, p_align0
;dd 5 ;~p_flags
;db 0 ; p_align
_parent.2:
mov bl, 3
%ifdef NO_CHEATING
pop ebx ; p_align1
phdr.endm2:
phdr.end equ phdr.endm2 + 2
lea ecx, [ebp+__strempty-__self+EBP_bias] ; p_align2..3, ...
xchg edx, esp
%else
mov bl, 3 ; p_align1..2
phdr.endm1:
phdr.end equ phdr.endm1 + 1
;_parent.3:
mov ax, SYS_execveat
mov di, AT_EMPTY_PATH
%endif
mov ax, SYS_execveat ; p_align3, ...
mov di, AT_EMPTY_PATH
;int 0x80 ;; fallthru to _start.4
;_start:
; mov ax, SYS_memfd_create
; mov ebx, esp
; int 0x80
; pop eax
; mov ebp, __self-EBP_bias
; mov al, SYS_fork
_start.4:
int 0x80
int 0x80
%ifndef NO_FILE_MANAGER_COMPAT
xor ebx, ebx
xor ebx, ebx
%endif
test eax, eax
jnz short _parent.0
;jz short _child
;_parent:
; xor ebx, ebx
; mov ax, SYS_waitid
; lea esi, [ebx+P_ALL] ; smaller than mov si, P_ALL
; int 0x80
; mov edx, esp ; use our args in args ; edx == argv
; lea ecx, [ebp+__strempty-__self] ; doesn't like a NULL
; mov bl, 3 ; __memfd
; lea esi, [esp+24]
; mov ax, SYS_execveat
; mov di, AT_EMPTY_PATH ; can be smaller, reg sucks
; int 0x80
jnz short _parent.0
_child:
;mov cl, 0 ; fix argc
;mov dl, 0
; dup stdout->demo
mov al, SYS_dup2
xchg ebx, edi
mov cl, STDOUT_FILENO
int 0x80
lea ebx, [ebp+EBP_bias]
;mov ebx, ebp
mov al, SYS_open
int 0x80
; open self for payload reading
lea ebx, [ebp+EBP_bias]
mov al, SYS_open
dec cl
int 0x80
; fd1
push eax
; seek
mov ebx, eax
mov al, SYS_lseek
mov cl, payload - ehdr
int 0x80
; seek
mov al, SYS_lseek
pop ebx
push ebx
mov cl, payload - ehdr
int 0x80
; dup2 self->stdin
mov al, SYS_dup2
mov cl, STDIN_FILENO
int 0x80
; dup2 demo->stdout
dec ebx
mov al, SYS_dup2
mov cl, STDOUT_FILENO
int 0x80
; execve
mov al, SYS_execve
; dup2 self->stdin
mov al, SYS_dup2
pop ebx
dec ecx ; zero it out -> STDIN_FILENO
int 0x80
; execve
mov al, SYS_execve
lea ebx, [ebp+__zip-__self+EBP_bias]
lea ebx, [ebp+__zip-__self+EBP_bias]
%ifndef USE_GZIP
push ecx
push ebx
%endif
mov ecx, esp
int 0x80
xchg ecx, esp
int 0x80
__self:
db '/proc/self/exe'