kpack: add native Windows and Linux ports (#696)

Windowless in-place compressor (kpackw.exe / kpackl), reusing the
original LZMA + kerpack sources; auto-detects kernel.mnt. Tupfile builds both.

Assisted-by: Claude Opus 5
Reviewed-on: KolibriOS/kolibrios#696
Reviewed-by: Burer <burer@kolibrios.org>
Reviewed-by: IgorA <22+igora@noreply.localhost>
Co-authored-by: leency <lipatov.kiril@gmail.com>
This commit is contained in:
Leency authored and Kiril Lipatov committed 2026-09-23 22:48:55 +00:00
1 parent 298155191d
commit b2f9cc9809
5 files changed
+1697

No files matched your search

+3
View File
@@ -1,2 +1,5 @@
if tup.getconfig("NO_FASM") ~= "" then return end
tup.rule("kpack.asm", "fasm %f %o " .. tup.getconfig("KPACK_CMD"), "kpack")
-- Native host ports (windowless in-place compressor):
tup.rule("kpackw.asm", "fasm %f %o", "kpackw.exe") -- Windows PE
tup.rule("kpackl.asm", "fasm %f %o", "kpackl") -- Linux ELF32
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# Build the native Linux port of kpack (windowless, in-place compressor).
# Self-contained ELF32 - only FASM is needed, no gcc / no linker.
fasm kpackl.asm kpackl
chmod +x kpackl
+4
View File
@@ -0,0 +1,4 @@
@echo off
rem Build the native Windows port of kpack (no window, drag-and-drop compressor).
rem Requires FASM in PATH (or edit the line below to point at fasm.exe).
fasm -m 16384 kpackw.asm kpackw.exe
+795
View File
@@ -0,0 +1,795 @@
; kpackl = Kolibri Packer, native Linux port (no output, in-place compressor)
;
; Usage: kpackl <file>
; The file is compressed in place (overwritten).
; If the file name is "kernel.mnt" it is packed with the "Kernel" option
; (self-extracting KERNPACK format), otherwise the normal KPCK format is used.
; The program prints nothing; on any problem it just leaves the file alone.
;
; Self-contained ELF32 using raw Linux int 0x80 syscalls - no libc, no linker.
; Compression core (LZMA + calltrick) and the self-extractor stub are the
; original KolibriOS kpack sources by diamond; only the OS glue is new.
format ELF executable 3
entry _start
segment readable writeable executable
use_lzma = 1
use_calltrick1 = 40h
use_calltrick2 = 80h
; Linux i386 syscalls
sys_exit = 1
sys_read = 3
sys_write = 4
sys_open = 5
sys_close = 6
sys_lseek = 19
sys_mmap2 = 192
_start:
; ---- get the file argument (argv[1]) ------------------------------
mov eax, [esp] ; argc
cmp eax, 2
jb quit
mov esi, [esp+8] ; argv[1]
mov [infilename], esi
; ---- is the base name "kernel.mnt" ? ------------------------------
mov ebx, esi ; ebx = start of base name
.bn:
mov al, [esi]
test al, al
jz .bn_done
cmp al, '/'
jne .bn_n
inc esi
mov ebx, esi
jmp .bn
.bn_n:
inc esi
jmp .bn
.bn_done:
mov esi, ebx
mov edi, kernel_name
.cmpk:
mov al, [esi]
mov ah, [edi]
test ah, ah
jnz .notend
test al, al ; kernel_name ended: match iff input ended too
jz do_kerpack
jmp do_pack
.notend:
or al, 20h ; tolower (kernel_name is lowercase)
cmp al, ah
jne do_pack
inc esi
inc edi
jmp .cmpk
;=====================================================================
; helper: allocate ecx bytes -> eax = ptr, or eax = 0 on failure
;=====================================================================
alloc:
push ebx
push edx
push esi
push edi
push ebp
mov eax, sys_mmap2
xor ebx, ebx ; addr = NULL
mov edx, 3 ; PROT_READ|PROT_WRITE
mov esi, 22h ; MAP_PRIVATE|MAP_ANONYMOUS
or edi, -1 ; fd = -1
xor ebp, ebp ; offset = 0
int 0x80
pop ebp
pop edi
pop esi
pop edx
pop ebx
cmp eax, 0xFFFFF000 ; -4096..-1 == error
jb .ok
xor eax, eax
.ok:
ret
;=====================================================================
; Normal packing (KPCK)
;=====================================================================
do_pack:
; open input (O_RDONLY)
mov eax, sys_open
mov ebx, [infilename]
xor ecx, ecx
xor edx, edx
int 0x80
test eax, eax
js quit
mov [fd], eax
; size = lseek(fd, 0, SEEK_END)
mov eax, sys_lseek
mov ebx, [fd]
xor ecx, ecx
mov edx, 2
int 0x80
test eax, eax
jle quit
mov [insize], eax
; rewind
mov eax, sys_lseek
mov ebx, [fd]
xor ecx, ecx
xor edx, edx
int 0x80
; allocate infile
mov ecx, [insize]
call alloc
test eax, eax
jz quit
mov [infile], eax
; read
mov eax, sys_read
mov ebx, [fd]
mov ecx, [infile]
mov edx, [insize]
int 0x80
; close
mov eax, sys_close
mov ebx, [fd]
int 0x80
; maximum output size
mov eax, [insize]
shr eax, 3
add eax, [insize]
add eax, 400h
mov esi, eax ; esi = maxout (preserved across alloc)
add eax, eax
mov ecx, eax
call alloc
test eax, eax
jz quit
mov [outfile], eax
mov [outfile1], eax
mov [outfilebest], eax
add eax, esi
mov [outfile2], eax
; KPCK header
mov eax, [outfile1]
mov dword [eax], 'KPCK'
mov ecx, [insize]
mov [eax+4], ecx
; LZMA dictionary size
dec ecx
bsr eax, ecx
inc eax
cmp eax, 28
jb @f
mov eax, 28
@@:
push eax
push eax
call lzma_set_dict_size ; ret 4
pop ecx
mov eax, 1
shl eax, cl
; workmem
imul eax, 19
shr eax, 1
add eax, 509000h
mov ecx, eax
call alloc
test eax, eax
jz quit
mov [workmem], eax
; first pass: plain LZMA
mov eax, [outfile2]
mov [outfile], eax
xchg eax, edi
mov esi, [outfile1]
movsd
movsd
call pk_pack_lzma
mov [outsize], eax
mov eax, [outfile]
mov [outfilebest], eax
mov [method], use_lzma
; calltrick 1
call pk_preprocess_calltrick
test eax, eax
jz .noct1
call pk_set_outfile
call pk_pack_lzma
add eax, 5
cmp eax, [outsize]
jae .noct1
mov [outsize], eax
mov eax, [outfile]
mov [outfilebest], eax
mov [method], use_lzma or use_calltrick1
.noct1:
call pk_set_outfile
push dword [ctn]
movzx eax, byte [cti]
push eax
call pk_preprocess_calltrick2
test eax, eax
jz .noct2
call pk_set_outfile
call pk_pack_lzma
add eax, 5
cmp eax, [outsize]
jae .noct2
mov [outsize], eax
mov eax, [outfile]
mov [outfilebest], eax
mov [method], use_lzma or use_calltrick2
pop ecx
pop ecx
push dword [ctn]
movzx eax, byte [cti]
push eax
.noct2:
pop eax
mov [cti], al
pop eax
mov [ctn], eax
add dword [outsize], 12
mov eax, [outsize]
cmp eax, [insize]
jb .packed_ok
jmp quit ; not smaller: leave original
.packed_ok:
movzx eax, byte [method]
mov edi, [outfilebest]
mov [edi+8], eax
test al, use_calltrick1 or use_calltrick2
jz .save
mov ecx, [outsize]
add ecx, edi
mov eax, [ctn]
mov [ecx-5], eax
mov al, [cti]
mov [ecx-1], al
.save:
; open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644)
mov eax, sys_open
mov ebx, [infilename]
mov ecx, 0x241
mov edx, 0x1A4
int 0x80
test eax, eax
js quit
mov [fd], eax
mov eax, sys_write
mov ebx, [fd]
mov ecx, [outfilebest]
mov edx, [outsize]
int 0x80
mov eax, sys_close
mov ebx, [fd]
int 0x80
quit:
mov eax, sys_exit
xor ebx, ebx
int 0x80
;---------------------------------------------------------------------
pk_set_outfile:
mov eax, [outfilebest]
xor eax, [outfile1]
xor eax, [outfile2]
mov [outfile], eax
ret
;---------------------------------------------------------------------
pk_pack_lzma:
mov eax, [outfile]
add eax, 11
push [workmem]
push [insize]
push eax
push [infile]
call lzma_compress ; ret 16
mov ecx, [outfile]
mov edx, [ecx+12]
bswap edx
mov [ecx+12], edx
dec eax
ret
;---------------------------------------------------------------------
pk_ct_fail:
xor eax, eax
mov [ctn], eax
ret
;---------------------------------------------------------------------
pk_preprocess_calltrick:
mov ecx, [insize]
call alloc
test eax, eax
jz pk_ct_fail
push eax
xor eax, eax
mov edi, ct1
mov ecx, 256/4
push edi
rep stosd
pop edi
mov ecx, [insize]
mov esi, [infile]
xchg eax, edx ; edx = 0
pop eax
xchg eax, ebx ; ebx = temp buffer
push ebx
.pre:
lodsb
sub al, 0E8h
cmp al, 1
ja .cont
cmp ecx, 5
jb .done
lodsd
add eax, esi
sub eax, [infile]
cmp eax, [insize]
jae .xxx
cmp eax, 1000000h
jae .xxx
sub ecx, 4
bswap eax
mov [esi-4], eax
inc edx
mov [ebx], esi
add ebx, 4
jmp .cont
.xxx:
sub esi, 4
movzx eax, byte [esi]
mov byte [eax+edi], 1
.cont:
loop .pre
.done:
mov [ctn], edx
pop edx ; edx = base of temp buffer
xor eax, eax
mov ecx, 256
repnz scasb
jnz pk_ct_fail
not cl
mov [cti], cl
@@:
cmp ebx, edx
jz @f
sub ebx, 4
mov eax, [ebx]
mov [eax-4], cl
jmp @b
@@:
mov al, 1
ret
;---------------------------------------------------------------------
pk_preprocess_calltrick2:
; restore input
mov esi, [infile]
mov ecx, [ctn]
jecxz .pre_init
.restore:
lodsb
sub al, 0E8h
cmp al, 1
ja .restore
mov al, [cti]
cmp [esi], al
jnz .restore
lodsd
mov al, 0
bswap eax
sub eax, esi
add eax, [infile]
mov [esi-4], eax
loop .restore
.pre_init:
mov ecx, [insize]
call alloc
test eax, eax
jz pk_ct_fail
mov edi, ct1
xchg eax, ebx
xor eax, eax
push edi
mov ecx, 256/4
rep stosd
pop edi
mov ecx, [insize]
mov esi, [infile]
xchg eax, edx ; edx = 0
push ebx
.pre:
lodsb
.chk0f:
cmp al, 0Fh
jnz .ip1
dec ecx
jz .done
lodsb
cmp al, 80h
jb .chk0f
cmp al, 90h
jb .take
.ip1:
sub al, 0E8h
cmp al, 1
ja .cont
.take:
cmp ecx, 5
jb .done
lodsd
add eax, esi
sub eax, [infile]
cmp eax, [insize]
jae .xxx
cmp eax, 1000000h
jae .xxx
sub ecx, 4
bswap eax
mov [esi-4], eax
inc edx
mov [ebx], esi
add ebx, 4
jmp .cont
.xxx:
sub esi, 4
movzx eax, byte [esi]
mov byte [eax+edi], 1
.cont:
loop .pre
.done:
mov [ctn], edx
pop edx
xor eax, eax
mov ecx, 256
repnz scasb
jnz pk_ct_fail
not cl
mov [cti], cl
@@:
cmp ebx, edx
jz @f
sub ebx, 4
mov eax, [ebx]
mov [eax-4], cl
jmp @b
@@:
mov al, 1
ret
;=====================================================================
; Kernel packing (self-extracting KERNPACK)
;=====================================================================
do_kerpack:
mov ecx, 300*1024*3 + 6A8000h
call alloc
test eax, eax
jz quit
mov [infile], eax
add eax, 300*1024
mov [inbuftmp], eax
add eax, 300*1024
mov [outfile], eax
add eax, 300*1024
mov [workmem], eax
; open + read
mov eax, sys_open
mov ebx, [infilename]
xor ecx, ecx
xor edx, edx
int 0x80
test eax, eax
js quit
mov [fd], eax
mov eax, sys_lseek
mov ebx, [fd]
xor ecx, ecx
mov edx, 2
int 0x80
test eax, eax
jle quit
cmp eax, 300*1024
jbe @f
mov eax, 300*1024
@@:
mov [insize3], eax
mov eax, sys_lseek
mov ebx, [fd]
xor ecx, ecx
xor edx, edx
int 0x80
mov eax, sys_read
mov ebx, [fd]
mov ecx, [infile]
mov edx, [insize3]
int 0x80
mov eax, sys_close
mov ebx, [fd]
int 0x80
; already packed?
mov ebx, [insize3]
mov edi, [infile]
add edi, ebx
cmp dword [edi-8], 'KERN'
jnz .go
cmp dword [edi-4], 'PACK'
jz quit
.go:
push 18
call lzma_set_dict_size
; find jump to 32-bit code
mov edi, [infile]
mov eax, edi
add eax, [insize3]
dec edi
.find:
cmp eax, edi
je quit ; not a kernel
inc edi
cmp dword [edi], 0xE88EE08E ; mov fs,ax / mov gs,ax
jnz .find
cmp dword [edi+4], 0x00BCD08E ; mov ss,ax / mov esp,00xxxxxx
jnz .find
add edi, 11
mov [inptr], edi
sub edi, [infile]
mov [indelta], edi
lea eax, [ebx+0x10000]
mov dword [loader_patch3+2], eax
sub ebx, edi
mov [insize1], ebx
call ker_preprocess_calltrick3
mov al, [cti]
mov [loader_patch5-1], al
mov eax, [ctn]
mov [loader_patch4+1], eax
mov eax, [inptr]
add eax, [outfile]
sub eax, [infile]
add eax, loader_size - 5
push [workmem]
push [insize1]
push eax
push [inptr]
call lzma_compress
add eax, loader_size - 5
mov [loader_patch1+6], eax
add eax, [indelta]
mov [outsize3], eax
mov eax, [indelta]
mov ecx, eax
add ecx, [outfile]
mov ecx, [ecx + loader_size - 4]
bswap ecx
mov [loader_patch2+4], ecx
add eax, 0x10000
mov [loader_patch1+1], eax
mov esi, [infile]
mov edi, [outfile]
mov ecx, [indelta]
rep movsb
mov esi, loader_start
mov ecx, loader_size
rep movsb
mov eax, [outfile]
add eax, [outsize3]
mov dword [eax], 'KERN'
mov dword [eax+4], 'PACK'
add dword [outsize3], 8
; save
mov eax, sys_open
mov ebx, [infilename]
mov ecx, 0x241
mov edx, 0x1A4
int 0x80
test eax, eax
js quit
mov [fd], eax
mov eax, sys_write
mov ebx, [fd]
mov ecx, [outfile]
mov edx, [outsize3]
int 0x80
mov eax, sys_close
mov ebx, [fd]
int 0x80
jmp quit
;---------------------------------------------------------------------
ker_preprocess_calltrick3:
mov edi, ct1
xor eax, eax
push edi
mov ecx, 256/4
rep stosd
pop edi
mov ecx, ebx ; ecx = insize1
mov esi, [inptr]
mov ebx, [inbuftmp]
xchg eax, edx ; edx = 0
.pre:
lodsb
.chk0f:
cmp al, 0Fh
jnz .ip1
dec ecx
jz .done
lodsb
cmp al, 80h
jb .chk0f
cmp al, 90h
jb .take
.ip1:
sub al, 0E8h
cmp al, 1
ja .cont
.take:
cmp ecx, 5
jb .done
lodsd
add eax, esi
sub eax, [inptr]
cmp eax, [insize1]
jae .xxx
cmp eax, 1000000h
jae .xxx
sub ecx, 4
bswap eax
mov [esi-4], eax
inc edx
mov [ebx], esi
add ebx, 4
jmp .cont
.xxx:
sub esi, 4
movzx eax, byte [esi]
mov byte [eax+edi], 1
.cont:
loop .pre
.done:
mov [ctn], edx
xor eax, eax
mov ecx, 256
repnz scasb
jnz .ret
not cl
mov [cti], cl
@@:
cmp ebx, [inbuftmp]
jz .ret
sub ebx, 4
mov eax, [ebx]
mov [eax-4], cl
jmp @b
.ret:
ret
;---------------------------------------------------------------------
; LZMA compressor and the self-extractor stub (original diamond code)
;---------------------------------------------------------------------
include 'lzma_compress.inc'
include 'lzma_set_dict_size.inc'
include 'loader_lzma.inc'
;---------------------------------------------------------------------
; initialized data
;---------------------------------------------------------------------
kernel_name db 'kernel.mnt', 0
align 4
LiteralNextStates:
db 0,0,0,0,1,2,3,4,5,6,4,5
MatchNextStates:
db 7,7,7,7,7,7,7,10,10,10,10,10
RepNextStates:
db 8,8,8,8,8,8,8,11,11,11,11,11
ShortRepNextStates:
db 9,9,9,9,9,9,9,11,11,11,11,11
align 4
method db 1
;---------------------------------------------------------------------
; uninitialized data (BSS - must stay at the very end of the segment)
;---------------------------------------------------------------------
align 4
infilename dd ?
fd dd ?
infile dd ?
outfile dd ?
outfile1 dd ?
outfile2 dd ?
outfilebest dd ?
inbuftmp dd ?
workmem dd ?
insize dd ?
outsize dd ?
insize3 dd ?
outsize3 dd ?
inptr dd ?
indelta dd ?
insize1 dd ?
ctn dd ?
cti db ?
db 0,0,0
ct1 rb 256
align 4
; LZMA compressor work area (from kpack data.inc) - referenced by name
; from lzma_compress.inc; must keep this exact layout.
_lenEncoder: rd 8451
_prices: rd 4384
rd 17
_finished: rb 1
_writeEndMark: rb 1
_longestMatchWasFound: rb 1
_previousByte: rb 1
_longestMatchLength: rd 1
g_FastPos: rb 1024
_posSlotPrices: rd 256
_isRep0Long: rd 192
distances: rd 274
_optimumCurrentIndex: rd 1
_additionalOffset: rd 1
_isRepG1: rd 12
_isMatch: rd 192
_alignPriceCount: rd 1
_numLiteralContextBits: rd 1
_literalEncoder: rd 114
nowPos64: rd 2
_distancesPrices: rd 512
_repDistances: rd 4
_posSlotEncoder: rd 1028
lastPosSlotFillingPos: rd 2
_numFastBytes: rd 1
_posStateMask: rd 1
_isRepG0: rd 12
_repMatchLenEncoder: rd 8451
rd 4384
rd 17
_isRepG2: rd 12
_dictionarySize: rd 1
_numLiteralPosStateBits:rd 1
_distTableSize: rd 1
_optimumEndIndex: rd 1
state.State: rb 1
state.Prev1IsChar: rb 1
state.Prev2: rb 2
state.PosPrev2: rd 1
state.BackPrev2: rd 1
state.Price: rd 1
state.PosPrev: rd 1
state.BackPrev: rd 1
state.Backs: rd 4
rd 40950
_alignPrices: rd 16
_isRep: rd 12
_posAlignEncoder: rd 256
i_01: rd 1
_state: rb 1
_cache: rb 1
_state.Prev2: rb 2
_posEncoders: rd 1
_numPrevBits: rd 1
_numPosBits: rd 1
_posMask: rd 1
_posStateBits: rd 1
_range: rd 1
_cacheSize: rd 1
_cyclicBufferSize: rd 1
low: rd 2
Models: rd 512
_matchMaxLen: rd 1
pack_pos: rd 1
_cutValue: rd 1
_hash: rd 1
crc_table: rd 256
_buffer: rd 1
_pos: rd 1
_streamPos: rd 1
pack_length: rd 1
+890
View File
@@ -0,0 +1,890 @@
; kpackw = Kolibri Packer, native Windows port (no window, not even a console)
;
; Usage: drag a file onto kpackw.exe, or kpackw.exe <file>
; The file is compressed in place (overwritten).
; If the file name is "kernel.mnt" it is packed with the "Kernel" option
; (self-extracting KERNPACK format), otherwise the normal KPCK format is used.
;
; Compression core (LZMA + calltrick) and the self-extractor stub are the
; original KolibriOS kpack sources by diamond; only the OS glue is new.
format PE GUI 4.0
entry start
;=====================================================================
section '.text' code readable writeable executable
;=====================================================================
use_lzma = 1
use_calltrick1 = 40h
use_calltrick2 = 80h
start:
; ---- get command line, extract the file argument ------------------
call [GetCommandLineA]
mov esi, eax
; skip leading spaces
.skip1:
mov al, [esi]
cmp al, ' '
jne .arg0
inc esi
jmp .skip1
; skip program name (argv[0])
.arg0:
cmp byte [esi], '"'
jne .arg0_plain
inc esi
.arg0_q:
mov al, [esi]
test al, al
jz .no_arg
inc esi
cmp al, '"'
jne .arg0_q
jmp .skip2
.arg0_plain:
mov al, [esi]
test al, al
jz .no_arg
cmp al, ' '
je .skip2
inc esi
jmp .arg0_plain
; skip spaces between argv[0] and argv[1]
.skip2:
mov al, [esi]
cmp al, ' '
jne .arg1
inc esi
jmp .skip2
; read the file name argument into infilename
.arg1:
mov al, [esi]
test al, al
jz .no_arg
mov edi, infilename
cmp al, '"'
jne .arg1_plain
inc esi
.arg1_q:
mov al, [esi]
test al, al
jz .name_done
inc esi
cmp al, '"'
je .name_done
stosb
jmp .arg1_q
.arg1_plain:
mov al, [esi]
test al, al
jz .name_done
cmp al, ' '
je .name_done
inc esi
stosb
jmp .arg1_plain
.name_done:
mov byte [edi], 0
; ---- is the base name "kernel.mnt" ? ------------------------------
; find base name (after last '\' or '/')
mov esi, infilename
mov ebx, esi ; ebx = start of base name
.bn:
mov al, [esi]
test al, al
jz .bn_done
cmp al, '\'
je .bn_sep
cmp al, '/'
je .bn_sep
inc esi
jmp .bn
.bn_sep:
inc esi
mov ebx, esi
jmp .bn
.bn_done:
; case-insensitive compare ebx with "kernel.mnt"
mov esi, ebx
mov edi, kernel_name
.cmpk:
mov al, [esi]
mov ah, [edi]
test ah, ah
jnz .notend
test al, al ; kernel_name ended: match iff input ended too
jz do_kerpack
jmp do_pack
.notend:
or al, 20h ; tolower (kernel_name is lowercase)
cmp al, ah
jne do_pack
inc esi
inc edi
jmp .cmpk
.no_arg:
push 0
call [ExitProcess]
;=====================================================================
; Normal packing (KPCK) - transcribed from the KolibriOS/linux kpack
;=====================================================================
do_pack:
; open input
push 0
push 0
push 3 ; OPEN_EXISTING
push 0
push 1 ; FILE_SHARE_READ
push 80000000h ; GENERIC_READ
push infilename
call [CreateFileA]
cmp eax, -1
je quit
mov esi, eax ; handle
push 0
push esi
call [GetFileSize]
cmp eax, -1
je quit
test eax, eax
jz quit
mov [insize], eax
; allocate infile
push 4
push 3000h
push eax
push 0
call [VirtualAlloc]
test eax, eax
jz quit
mov [infile], eax
; read
push 0
push bytesdone
push [insize]
push eax
push esi
call [ReadFile]
test eax, eax
jz quit
push esi
call [CloseHandle]
; maximum output size
mov eax, [insize]
shr eax, 3
add eax, [insize]
add eax, 400h
mov esi, eax ; esi = maxout
add eax, eax ; two copies
push 4
push 3000h
push eax
push 0
call [VirtualAlloc]
test eax, eax
jz quit
mov [outfile], eax
mov [outfile1], eax
mov [outfilebest], eax
add eax, esi
mov [outfile2], eax
; KPCK header
mov eax, [outfile1]
mov dword [eax], 'KPCK'
mov ecx, [insize]
mov [eax+4], ecx
; LZMA dictionary size
dec ecx
bsr eax, ecx
inc eax
cmp eax, 28
jb @f
mov eax, 28
@@:
push eax
push eax
call lzma_set_dict_size ; ret 4 (pops one)
pop ecx ; ecx = log2(dict)
mov eax, 1
shl eax, cl
; workmem
imul eax, 19
shr eax, 1
add eax, 509000h
push 4
push 3000h
push eax
push 0
call [VirtualAlloc]
test eax, eax
jz quit
mov [workmem], eax
; first pass: plain LZMA
mov eax, [outfile2]
mov [outfile], eax
xchg eax, edi
mov esi, [outfile1]
movsd
movsd
call pk_pack_lzma
mov [outsize], eax
mov eax, [outfile]
mov [outfilebest], eax
mov [method], use_lzma
; calltrick 1
call pk_preprocess_calltrick
test eax, eax
jz .noct1
call pk_set_outfile
call pk_pack_lzma
add eax, 5
cmp eax, [outsize]
jae .noct1
mov [outsize], eax
mov eax, [outfile]
mov [outfilebest], eax
mov [method], use_lzma or use_calltrick1
.noct1:
call pk_set_outfile
push dword [ctn]
movzx eax, byte [cti]
push eax
call pk_preprocess_calltrick2
test eax, eax
jz .noct2
call pk_set_outfile
call pk_pack_lzma
add eax, 5
cmp eax, [outsize]
jae .noct2
mov [outsize], eax
mov eax, [outfile]
mov [outfilebest], eax
mov [method], use_lzma or use_calltrick2
pop ecx
pop ecx
push dword [ctn]
movzx eax, byte [cti]
push eax
.noct2:
pop eax
mov [cti], al
pop eax
mov [ctn], eax
add dword [outsize], 12
mov eax, [outsize]
cmp eax, [insize]
jb .packed_ok
jmp quit ; output not smaller: leave original untouched
.packed_ok:
movzx eax, byte [method]
mov edi, [outfilebest]
mov [edi+8], eax
test al, use_calltrick1 or use_calltrick2
jz .save
mov ecx, [outsize]
add ecx, edi
mov eax, [ctn]
mov [ecx-5], eax
mov al, [cti]
mov [ecx-1], al
.save:
push 0
push 80h ; FILE_ATTRIBUTE_NORMAL
push 2 ; CREATE_ALWAYS
push 0
push 0
push 40000000h ; GENERIC_WRITE
push infilename
call [CreateFileA]
cmp eax, -1
je quit
mov esi, eax
push 0
push bytesdone
push [outsize]
push [outfilebest]
push esi
call [WriteFile]
push esi
call [CloseHandle]
quit:
push 0
call [ExitProcess]
;---------------------------------------------------------------------
pk_set_outfile:
mov eax, [outfilebest]
xor eax, [outfile1]
xor eax, [outfile2]
mov [outfile], eax
ret
;---------------------------------------------------------------------
pk_pack_lzma:
mov eax, [outfile]
add eax, 11
push [workmem]
push [insize]
push eax
push [infile]
call lzma_compress ; ret 16
mov ecx, [outfile]
mov edx, [ecx+12]
bswap edx
mov [ecx+12], edx
dec eax
ret
;---------------------------------------------------------------------
pk_ct_fail:
xor eax, eax
mov [ctn], eax
ret
;---------------------------------------------------------------------
pk_preprocess_calltrick:
push 4
push 3000h
push [insize]
push 0
call [VirtualAlloc]
test eax, eax
jz pk_ct_fail
push eax
xor eax, eax
mov edi, ct1
mov ecx, 256/4
push edi
rep stosd
pop edi
mov ecx, [insize]
mov esi, [infile]
xchg eax, edx ; edx = 0
pop eax
xchg eax, ebx ; ebx = temp buffer
push ebx
.pre:
lodsb
sub al, 0E8h
cmp al, 1
ja .cont
cmp ecx, 5
jb .done
lodsd
add eax, esi
sub eax, [infile]
cmp eax, [insize]
jae .xxx
cmp eax, 1000000h
jae .xxx
sub ecx, 4
bswap eax
mov [esi-4], eax
inc edx
mov [ebx], esi
add ebx, 4
jmp .cont
.xxx:
sub esi, 4
movzx eax, byte [esi]
mov byte [eax+edi], 1
.cont:
loop .pre
.done:
mov [ctn], edx
pop edx ; edx = base of temp buffer
xor eax, eax
mov ecx, 256
repnz scasb
jnz pk_ct_fail
not cl
mov [cti], cl
@@:
cmp ebx, edx
jz @f
sub ebx, 4
mov eax, [ebx]
mov [eax-4], cl
jmp @b
@@:
mov al, 1
ret
;---------------------------------------------------------------------
pk_preprocess_calltrick2:
; restore input
mov esi, [infile]
mov ecx, [ctn]
jecxz .pre_init
.restore:
lodsb
sub al, 0E8h
cmp al, 1
ja .restore
mov al, [cti]
cmp [esi], al
jnz .restore
lodsd
mov al, 0
bswap eax
sub eax, esi
add eax, [infile]
mov [esi-4], eax
loop .restore
.pre_init:
push 4
push 3000h
push [insize]
push 0
call [VirtualAlloc]
test eax, eax
jz pk_ct_fail
mov edi, ct1
xchg eax, ebx
xor eax, eax
push edi
mov ecx, 256/4
rep stosd
pop edi
mov ecx, [insize]
mov esi, [infile]
xchg eax, edx ; edx = 0
push ebx
.pre:
lodsb
.chk0f:
cmp al, 0Fh
jnz .ip1
dec ecx
jz .done
lodsb
cmp al, 80h
jb .chk0f
cmp al, 90h
jb .take
.ip1:
sub al, 0E8h
cmp al, 1
ja .cont
.take:
cmp ecx, 5
jb .done
lodsd
add eax, esi
sub eax, [infile]
cmp eax, [insize]
jae .xxx
cmp eax, 1000000h
jae .xxx
sub ecx, 4
bswap eax
mov [esi-4], eax
inc edx
mov [ebx], esi
add ebx, 4
jmp .cont
.xxx:
sub esi, 4
movzx eax, byte [esi]
mov byte [eax+edi], 1
.cont:
loop .pre
.done:
mov [ctn], edx
pop edx
xor eax, eax
mov ecx, 256
repnz scasb
jnz pk_ct_fail
not cl
mov [cti], cl
@@:
cmp ebx, edx
jz @f
sub ebx, 4
mov eax, [ebx]
mov [eax-4], cl
jmp @b
@@:
mov al, 1
ret
;=====================================================================
; Kernel packing (self-extracting KERNPACK) - from kerpack.inc
;=====================================================================
do_kerpack:
push 4
push 3000h
push 300*1024*3 + 6A8000h
push 0
call [VirtualAlloc]
test eax, eax
jz quit
mov [infile], eax
add eax, 300*1024
mov [inbuftmp], eax
add eax, 300*1024
mov [outfile], eax
add eax, 300*1024
mov [workmem], eax
; open + read
push 0
push 0
push 3
push 0
push 1
push 80000000h
push infilename
call [CreateFileA]
cmp eax, -1
je quit
mov esi, eax
push 0
push esi
call [GetFileSize]
cmp eax, -1
je quit
test eax, eax
jz quit
cmp eax, 300*1024
jbe @f
mov eax, 300*1024
@@:
mov [insize3], eax
push 0
push bytesdone
push [insize3]
push [infile]
push esi
call [ReadFile]
push esi
call [CloseHandle]
; already packed?
mov ebx, [insize3]
mov edi, [infile]
add edi, ebx
cmp dword [edi-8], 'KERN'
jnz .go
cmp dword [edi-4], 'PACK'
jz quit
.go:
push 18
call lzma_set_dict_size
; find jump to 32-bit code
mov edi, [infile]
mov eax, edi
add eax, [insize3]
dec edi
.find:
cmp eax, edi
je quit ; not a kernel
inc edi
cmp dword [edi], 0xE88EE08E ; mov fs,ax / mov gs,ax
jnz .find
cmp dword [edi+4], 0x00BCD08E ; mov ss,ax / mov esp,00xxxxxx
jnz .find
add edi, 11
mov [inptr], edi
sub edi, [infile]
mov [indelta], edi
lea eax, [ebx+0x10000]
mov dword [loader_patch3+2], eax
sub ebx, edi
mov [insize1], ebx
call ker_preprocess_calltrick3
mov al, [cti]
mov [loader_patch5-1], al
mov eax, [ctn]
mov [loader_patch4+1], eax
mov eax, [inptr]
add eax, [outfile]
sub eax, [infile]
add eax, loader_size - 5
push [workmem]
push [insize1]
push eax
push [inptr]
call lzma_compress
add eax, loader_size - 5
mov [loader_patch1+6], eax
add eax, [indelta]
mov [outsize3], eax
mov eax, [indelta]
mov ecx, eax
add ecx, [outfile]
mov ecx, [ecx + loader_size - 4]
bswap ecx
mov [loader_patch2+4], ecx
add eax, 0x10000
mov [loader_patch1+1], eax
mov esi, [infile]
mov edi, [outfile]
mov ecx, [indelta]
rep movsb
mov esi, loader_start
mov ecx, loader_size
rep movsb
mov eax, [outfile]
add eax, [outsize3]
mov dword [eax], 'KERN'
mov dword [eax+4], 'PACK'
add dword [outsize3], 8
; save
push 0
push 80h
push 2
push 0
push 0
push 40000000h
push infilename
call [CreateFileA]
cmp eax, -1
je quit
mov esi, eax
push 0
push bytesdone
push [outsize3]
push [outfile]
push esi
call [WriteFile]
push esi
call [CloseHandle]
jmp quit
;---------------------------------------------------------------------
ker_preprocess_calltrick3:
mov edi, ct1
xor eax, eax
push edi
mov ecx, 256/4
rep stosd
pop edi
mov ecx, ebx ; ecx = insize1
mov esi, [inptr]
mov ebx, [inbuftmp]
xchg eax, edx ; edx = 0
.pre:
lodsb
.chk0f:
cmp al, 0Fh
jnz .ip1
dec ecx
jz .done
lodsb
cmp al, 80h
jb .chk0f
cmp al, 90h
jb .take
.ip1:
sub al, 0E8h
cmp al, 1
ja .cont
.take:
cmp ecx, 5
jb .done
lodsd
add eax, esi
sub eax, [inptr]
cmp eax, [insize1]
jae .xxx
cmp eax, 1000000h
jae .xxx
sub ecx, 4
bswap eax
mov [esi-4], eax
inc edx
mov [ebx], esi
add ebx, 4
jmp .cont
.xxx:
sub esi, 4
movzx eax, byte [esi]
mov byte [eax+edi], 1
.cont:
loop .pre
.done:
mov [ctn], edx
xor eax, eax
mov ecx, 256
repnz scasb
jnz .ret
not cl
mov [cti], cl
@@:
cmp ebx, [inbuftmp]
jz .ret
sub ebx, 4
mov eax, [ebx]
mov [eax-4], cl
jmp @b
.ret:
ret
;---------------------------------------------------------------------
; LZMA compressor and the self-extractor stub (original diamond code)
;---------------------------------------------------------------------
include 'lzma_compress.inc'
include 'lzma_set_dict_size.inc'
include 'loader_lzma.inc'
;=====================================================================
section '.data' data readable writeable
;=====================================================================
kernel_name db 'kernel.mnt', 0
align 4
; LZMA state-transition tables (constants, referenced by lzma_compress)
LiteralNextStates:
db 0,0,0,0,1,2,3,4,5,6,4,5
MatchNextStates:
db 7,7,7,7,7,7,7,10,10,10,10,10
RepNextStates:
db 8,8,8,8,8,8,8,11,11,11,11,11
ShortRepNextStates:
db 9,9,9,9,9,9,9,11,11,11,11,11
align 4
method db 1
db 0,0,0
infile dd ?
outfile dd ?
outfile1 dd ?
outfile2 dd ?
outfilebest dd ?
inbuftmp dd ?
workmem dd ?
insize dd ?
outsize dd ?
insize3 dd ?
outsize3 dd ?
inptr dd ?
indelta dd ?
insize1 dd ?
ctn dd ?
cti db ?
db 0,0,0
bytesdone dd ?
;=====================================================================
section '.bss' data readable writeable
;=====================================================================
infilename rb 1024
ct1 rb 256
align 4
;---------------------------------------------------------------------
; LZMA compressor work area (from kpack data.inc) - referenced by name
; from lzma_compress.inc; must keep this exact layout.
;---------------------------------------------------------------------
_lenEncoder: rd 8451
_prices: rd 4384
rd 17
_finished: rb 1
_writeEndMark: rb 1
_longestMatchWasFound: rb 1
_previousByte: rb 1
_longestMatchLength: rd 1
g_FastPos: rb 1024
_posSlotPrices: rd 256
_isRep0Long: rd 192
distances: rd 274
_optimumCurrentIndex: rd 1
_additionalOffset: rd 1
_isRepG1: rd 12
_isMatch: rd 192
_alignPriceCount: rd 1
_numLiteralContextBits: rd 1
_literalEncoder: rd 114
nowPos64: rd 2
_distancesPrices: rd 512
_repDistances: rd 4
_posSlotEncoder: rd 1028
lastPosSlotFillingPos: rd 2
_numFastBytes: rd 1
_posStateMask: rd 1
_isRepG0: rd 12
_repMatchLenEncoder: rd 8451
rd 4384
rd 17
_isRepG2: rd 12
_dictionarySize: rd 1
_numLiteralPosStateBits:rd 1
_distTableSize: rd 1
_optimumEndIndex: rd 1
state.State: rb 1
state.Prev1IsChar: rb 1
state.Prev2: rb 2
state.PosPrev2: rd 1
state.BackPrev2: rd 1
state.Price: rd 1
state.PosPrev: rd 1
state.BackPrev: rd 1
state.Backs: rd 4
rd 40950
_alignPrices: rd 16
_isRep: rd 12
_posAlignEncoder: rd 256
i_01: rd 1
_state: rb 1
_cache: rb 1
_state.Prev2: rb 2
_posEncoders: rd 1
_numPrevBits: rd 1
_numPosBits: rd 1
_posMask: rd 1
_posStateBits: rd 1
_range: rd 1
_cacheSize: rd 1
_cyclicBufferSize: rd 1
low: rd 2
Models: rd 512
_matchMaxLen: rd 1
pack_pos: rd 1
_cutValue: rd 1
_hash: rd 1
crc_table: rd 256
_buffer: rd 1
_pos: rd 1
_streamPos: rd 1
pack_length: rd 1
;=====================================================================
section '.idata' import data readable writeable
;=====================================================================
dd 0,0,0, RVA kernel_txt, RVA kernel_iat
dd 0,0,0, 0, 0
kernel_iat:
GetCommandLineA dd RVA _GetCommandLineA
CreateFileA dd RVA _CreateFileA
GetFileSize dd RVA _GetFileSize
ReadFile dd RVA _ReadFile
WriteFile dd RVA _WriteFile
CloseHandle dd RVA _CloseHandle
VirtualAlloc dd RVA _VirtualAlloc
VirtualFree dd RVA _VirtualFree
ExitProcess dd RVA _ExitProcess
dd 0
kernel_txt db 'KERNEL32.DLL', 0
align 2
_GetCommandLineA dw 0
db 'GetCommandLineA', 0
_CreateFileA dw 0
db 'CreateFileA', 0
_GetFileSize dw 0
db 'GetFileSize', 0
_ReadFile dw 0
db 'ReadFile', 0
_WriteFile dw 0
db 'WriteFile', 0
_CloseHandle dw 0
db 'CloseHandle', 0
_VirtualAlloc dw 0
db 'VirtualAlloc', 0
_VirtualFree dw 0
db 'VirtualFree', 0
_ExitProcess dw 0
db 'ExitProcess', 0