0,0 → 1,460 |
;freebsd = 1 ;uncomment for FreeBSD-specific changes |
|
format ELF64 |
public _start |
.data? fix section ".bss" writeable align 4 |
.data fix section ".data" writeable align 4 |
.const fix section ".const" align 4 |
.code fix section ".text" executable align 16 |
offset fix |
ptr fix |
struc label type { |
label . type } |
|
extrn lzma_compress |
extrn lzma_set_dict_size |
|
.data? |
infilename dq ? |
outfilename dq ? |
infile dq ? |
outfile dq ? |
inptr dq ? |
workmem dq ? |
insize dd ? |
outsize dd ? |
lzma_dictsize dd ? |
indelta dd ? |
strucstat rq 18 |
|
if defined freebsd |
st_atime_offset = 24 |
st_mtime_offset = 40 |
st_birthtime_offset = 104 |
st_size_offset = 72 |
else |
st_atime_offset = 72 |
st_mtime_offset = 88 |
;st_birthtime_offset not defined |
st_size_offset = 48 |
end if |
|
public environ |
environ dq ? |
public __progname |
__progname dq ? |
ct1 db 256 dup (?) |
ctn dd ? |
cti db ? |
|
.const |
usage_str db 'Written by diamond in 2006, 2007 specially for KolibriOS',13,10 |
db 'LZMA compression library is copyright (c) 1999-2005 by Igor Pavlov',13,10 |
db 13,10 |
db 'Usage: kerpack <infile> [<outfile>]',13,10 |
usage_len = $ - offset usage_str |
errload_str db 'Cannot load input file',13,10 |
errload_len = $ - offset errload_str |
outfileerr_str db 'Cannot save output file',13,10 |
outfileerr_len = $ - offset outfileerr_str |
nomem_str db 'No memory',13,10 |
nomem_len = $ - offset nomem_str |
too_big_str db 'failed, output is greater than input.',13,10 |
too_big_len = $ - too_big_str |
compressing_str db 'Compressing ... ' |
compressing_len = $ - compressing_str |
|
.data |
done_str db 'OK! Compression ratio: ' |
ratio dw '00' |
db '%',13,10,13,10 |
done_len = $ - done_str |
|
use_lzma = 1 |
|
use_no_calltrick = 0 |
use_calltrick1 = 40h |
use_calltrick2 = 80h |
|
method db 1 |
|
.code |
; Write string from [rsi] of rdx bytes. |
write_string: |
; 1. Align stack on 16 bytes. |
push rdi |
; 2. Set rdi to 1 = descriptor for stdout. |
xor edi, edi |
inc edi |
; 3. Do system call. |
call write |
; 4. Restore stack and return. |
pop rdi |
ret |
|
; Write string from [rsi] of rdx bytes and exit. Note that main code jumps (not calls) here, |
; so we should not align the stack. |
write_exit: |
; 1. Call prev func. |
call write_string |
; 2. Do system call for exit. |
; Note that this can be used as independent proc jumped (not called) to. |
doexit: |
xor edi, edi |
inc edi |
call exit |
|
; Main procedure. |
_start: |
; 1. Parse command line. |
; Linux: [rsp] = argc, rsp+8 = argv |
; FreeBSD: [rdi] = argc, rdi+8 = argv |
; 1a. Load argc and argv to registers, |
; skip first argument (which is always program name) |
if defined freebsd |
mov ecx, [rdi] ; ecx = argc |
add rdi, 16 ; rdi = &argv[1] |
else |
mov ecx, [rsp] ; ecx = argc |
lea rdi, [rsp+16] ; rdi = &argv[1] |
end if |
; 1b. Test for first filename parameter. If no, goto step 2. |
call get_file_name |
jz usage |
; 1c. We got input file name, save it. |
; Assume that output file name is the same; if no, we will rewrite it in step 1d. |
mov [infilename], rax |
mov [outfilename], rax |
; 1d. Test for second filename parameter. If yes, rewrite assumption in step 1c and check that there are no 3rd parameter. |
call get_file_name |
jz @f |
mov [outfilename], rax |
call get_file_name |
jnz usage |
@@: |
; 1e. Parsing is done, process to step 3. |
jmp short cont |
; 2. No arguments or too many arguments given; write message and exit. |
usage: |
push usage_len |
pop rdx |
mov rsi, offset usage_str |
jmp write_exit |
cont: |
; 4. Load the input file. |
; 4a. Do system call for stat - get file times and file size. |
mov rdi, [infilename] |
mov rsi, offset strucstat |
mov r13, rsi |
call stat |
; 4b. Test result; if not 0 (0 is OK), goto 4e. |
test rax, rax |
jnz short infileerr |
; 4c. Do system call for open. |
mov rdi, [infilename] |
mov rsi, offset open_mode |
call fopen |
; 4d. Test result; if not NULL, goto 4f. |
test rax, rax |
jnz short inopened |
infileerr: |
; 4e. Say error and abort. |
push errload_len |
pop rdx |
mov rsi, offset errload_str |
jmp write_exit |
inopened: |
mov r12, rax |
; 4f. Check that the size is nonzero and less than 4G. |
mov edi, [r13+st_size_offset] |
test edi, edi |
jz short infileerr |
cmp dword [r13+st_size_offset+4], 0 |
jnz short infileerr |
; 4g. Allocate memory for the input file. |
mov [insize], edi |
call malloc |
test rax, rax |
jz nomem |
mov [infile], rax |
; 4g. Read the input file to the allocated memory. |
mov rdi, rax |
push 1 |
pop rsi |
mov edx, [r13+st_size_offset] |
mov rcx, r12 |
call fread |
; 4h. Test result; must be equal to file size. |
cmp eax, [r13+st_size_offset] |
jnz infileerr |
; 4i. Close the input file. |
mov rdi, r12 |
call fclose |
; 5. Calculate maximum size of the output. |
mov edi, [insize] |
shr edi, 3 |
add edi, [insize] |
add edi, 400h ; should be enough for header |
mov r12d, edi |
; 6. Allocate memory for two copies of maximum output. |
; 6a. Do system call. |
call malloc |
; 6b. Test return value. If ok, goto 6d. |
test rax, rax |
jnz short outmemok |
; 6c. No memory; say error and exit. |
nomem: |
push nomem_len |
pop rdx |
mov rsi, offset nomem_str |
jmp write_exit |
; 6d. Remember allocated memory address. |
outmemok: |
mov [outfile], rax |
; 8. Determine and set lzma_dict_size. |
push 18h |
pop rdi |
call lzma_set_dict_size |
; 9. Allocate lzma_workmem. |
mov edi, (1 shl 18h) * 19 / 2 + 509000h |
call malloc |
test rax, rax |
jz nomem |
mov [workmem], rax |
; 10. Say another 'hi'. |
push compressing_len |
pop rdx |
mov rsi, offset compressing_str |
call write_string |
; 11. Do work. |
; find jump to 32-bit code |
mov rdi, [infile] |
dec rdi |
@@: |
inc rdi |
cmp dword [rdi], 0E88EE08Eh ; mov fs,ax/mov gs,ax |
jnz @b |
cmp dword [rdi+4], 00BCD08Eh ; mov ss,ax/mov esp,00xxxxxx |
jnz @b |
add rdi, 11 |
mov [inptr], rdi |
sub rdi, [infile] |
mov [indelta], edi |
mov eax, [insize] |
mov ebx, eax |
add eax, 0x10000 |
mov [loader_base+..loader_patch3+2], eax |
sub ebx, edi |
mov [insize], ebx |
call preprocess_calltrick2 |
mov al, [cti] |
mov [loader_base+loader_patch5-1], al |
mov eax, [ctn] |
mov [loader_base+loader_patch4+1], eax |
mov esi, [indelta] |
add rsi, [outfile] |
add rsi, loader_size - 5 |
mov rdi, [inptr] |
mov edx, [insize] |
mov rcx, [workmem] |
call lzma_compress |
mov edx, [indelta] |
add eax, loader_size-5 |
mov [loader_base+loader_patch1+6], eax |
add eax, edx |
mov [outsize], eax |
mov eax, edx |
add rax, [outfile] |
mov ecx, [rax + loader_size - 4] |
bswap ecx |
mov [loader_base+loader_patch2+4], ecx |
add edx, 0x10000 |
mov [loader_base+loader_patch1+1], edx |
mov rsi, [infile] |
mov rdi, [outfile] |
mov ecx, [indelta] |
rep movsb |
mov rsi, loader_base |
mov ecx, loader_size |
rep movsb |
mov eax, [outsize] |
cmp eax, [insize] |
jb short packed_ok |
push too_big_len |
pop rdx |
mov rsi, offset too_big_str |
jmp write_exit |
packed_ok: |
; 12. Main work is done. Free lzma_workmem. |
mov rdi, [workmem] |
call free |
; 13. Set header |
mov eax, [outsize] |
mov ecx, 100 |
mul ecx |
div [insize] |
mov cl, 10 |
div cl |
add ax, '00' |
mov [ratio], ax |
push done_len |
pop rdx |
mov rsi, offset done_str |
call write_string |
; 14. Save the output file. |
; 14a. Do system call for open. |
mov rdi, [outfilename] |
mov rsi, create_mode |
call fopen |
; 14b. Test for success; if yes, goto 14d. |
test rax, rax |
jnz short @f |
; 14c. Say error and exit. |
outerr: |
push outfileerr_len |
pop rdx |
mov rsi, offset outfileerr_str |
jmp write_exit |
; 14d. Do system call for write. |
@@: |
mov r12, rax |
mov rdi, [outfile] |
mov esi, [outsize] |
push 1 |
pop rdx |
mov rcx, r12 |
call fwrite |
test eax, eax |
jz short outerr |
; 14e. Close output file. |
mov rdi, r12 |
call fclose |
; 15. Exit. |
xor edi, edi |
call exit |
|
; Scan command line, skipping possible options, and return first non-option |
; ecx is number of arguments left, rdi points to first new argument (updated by func) |
; After the call: ZF set if no arguments left, otherwise rax points to the arg. |
get_file_name: |
; 1. Test whether there are still arguments. If no, goto 5; note ZF is set. |
dec ecx |
jz @@end |
; 2. Get the new arg, advance rdi (ecx was decreased in step 1). |
mov rax, [rdi] |
add rdi, 8 |
; 5. No arguments (ZF set) or normal argument (ZF cleared); return. |
@@end: |
ret |
|
pack_calltrick_fail: |
xor eax, eax |
xor ebx, ebx |
mov [ctn], eax |
ret |
|
preprocess_calltrick2: |
; restore input |
mov rsi, [infile] |
; input preprocessing |
push rax |
mov edi, [insize] |
add edi, edi |
call malloc |
pop rcx |
test rax, rax |
jz pack_calltrick_fail |
mov rdi, offset ct1 |
xchg rax, rbx |
xor eax, eax |
push rdi |
mov ecx, 256/4 |
rep stosd |
pop rdi |
mov ecx, [insize] |
mov rsi, [inptr] |
xchg eax, edx |
push rbx |
input_pre2: |
lodsb |
@@: |
cmp al, 0Fh |
jnz short ip1 |
dec ecx |
jz short input_pre_done2 |
lodsb |
cmp al, 80h |
jb short @b |
cmp al, 90h |
jb short @f |
ip1: |
sub al, 0E8h |
cmp al, 1 |
ja short input_pre_cont2 |
@@: |
cmp ecx, 5 |
jb short input_pre_done2 |
lodsd |
add eax, esi |
sub eax, dword ptr [inptr] |
cmp eax, [insize] |
jae short xxx2 |
cmp eax, 1000000h |
jae short xxx2 |
sub ecx, 4 |
bswap eax |
mov [rsi-4], eax |
inc edx |
mov [rbx], rsi |
add rbx, 8 |
jmp short input_pre_cont2 |
xxx2: sub rsi, 4 |
movzx eax, byte ptr [rsi] |
mov byte ptr [rax+rdi], 1 |
input_pre_cont2: |
loop input_pre2 |
input_pre_done2: |
mov [ctn], edx |
pop rdx |
xor eax, eax |
mov ecx, 256 |
repnz scasb |
jnz pack_calltrick_fail |
not cl |
mov [cti], cl |
@@: |
cmp rbx, rdx |
jz @f |
sub rbx, 8 |
mov rax, [rbx] |
mov [rax-4], cl |
jmp @b |
@@: |
push rax |
mov rdi, rbx |
call free |
pop rax |
ret |
|
extrn exit |
extrn fopen |
extrn fread |
extrn fwrite |
extrn fclose |
extrn fseek |
extrn ftell |
extrn malloc |
extrn free |
extrn write |
extrn utimes |
extrn stat |
|
open_mode db "rb",0 |
create_mode db "wb",0 |
|
.data |
|
loader_base: |
use32 |
org 0 |
include 'loader_lzma.asm' |