-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathkpack64.asm
More file actions
657 lines (635 loc) · 13.3 KB
/
kpack64.asm
File metadata and controls
657 lines (635 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
; kpack = Kolibri Packer
; Written by diamond in 2006 specially for KolibriOS
; Uses LZMA compression library by Igor Pavlov
; (for more information on LZMA and 7-Zip visit http://www.7-zip.org)
; (plain-C packer and ASM unpacker are ported by diamond)
;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 ?
outfile1 dq ?
outfile2 dq ?
outfile dq ?
outfilebest dq ?
workmem dq ?
insize dd ?
outsize dd ?
lzma_dictsize dd ?
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
timeval rq 2*2
public environ
environ dq ?
public __progname
__progname dq ?
ct1 db 256 dup (?)
ctn dd ?
cti db ?
.const
info_str db 'KPack - Kolibri Packer, version 0.11',13,10
db 'Uses LZMA v4.32 compression library',13,10,13,10
info_len = $ - offset info_str
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: kpack [--nologo] <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
bNoLogo db 0
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
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:
cmp [bNoLogo], 0
jnz doexit
push info_len + usage_len
pop rdx
mov rsi, offset info_str;usage_str
jmp write_exit
; 3. Say hello unless disabled with --nologo.
cont:
cmp [bNoLogo], 0
jnz @f
push info_len
pop rdx
mov rsi, info_str
call write_string
@@:
; 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.
add edi, edi
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
mov [outfile1], rax
mov [outfilebest], rax
add rax, r12
mov [outfile2], rax
sub rax, r12
; 7. Initialize KPCK header.
mov dword ptr [rax], 'KPCK'
mov ecx, [insize]
mov dword ptr [rax+4], ecx
; 8. Determine and set lzma_dict_size.
dec ecx
bsr eax, ecx
inc eax
cmp eax, 28
jb short @f
mov eax, 28
@@:
push rax
mov edi, eax
call lzma_set_dict_size
pop rcx
mov edi, 1
shl edi, cl
mov [lzma_dictsize], edi
; 9. Allocate lzma_workmem.
imul edi, 19
shr edi, 1
add edi, 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.
mov rax, [outfile2]
mov [outfile], rax
xchg rax, rdi
mov rsi, [outfile1]
movsd
movsd
call pack_lzma
mov [outsize], eax
mov rax, [outfile]
mov [outfilebest], rax
mov [method], use_lzma
call preprocess_calltrick
test eax, eax
jz short noct1
call set_outfile
call pack_lzma
add eax, 5
cmp eax, [outsize]
jae short @f
mov [outsize], eax
mov rax, [outfile]
mov [outfilebest], rax
mov [method], use_lzma or use_calltrick1
@@:
noct1:
call set_outfile
push qword ptr [ctn]
push qword ptr [cti]
call preprocess_calltrick2
test eax, eax
jz noct2
call set_outfile
call pack_lzma
add eax, 5
cmp eax, [outsize]
jae short @f
mov [outsize], eax
mov rax, [outfile]
mov [outfilebest], rax
mov [method], use_lzma or use_calltrick2
pop rcx
pop rcx
push qword ptr [ctn]
push qword ptr [cti]
@@:
noct2:
pop rax
mov [cti], al
pop rax
mov [ctn], eax
add [outsize], 12
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
movzx eax, [method]
mov rdi, [outfilebest]
mov [rdi+8], eax
test al, use_calltrick1 or use_calltrick2
jz short @f
mov ecx, [outsize]
add rcx, rdi
mov eax, [ctn]
mov [rcx-5], eax
mov al, [cti]
mov [rcx-1], al
@@:
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
cmp [bNoLogo], 0
jz @f
sub dl, 2
@@:
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, [outfilebest]
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
; 14f. Set output file time from the input file.
; Do two system calls, one for birth time, one for modification time.
mov rdi, [outfilename]
mov rsi, timeval
mov rax, [r13+st_atime_offset]
mov [rsi], rax
mov rax, [r13+st_atime_offset+8]
mov [rsi+8], rax
if defined st_birthtime_offset
mov rax, [r13+st_birthtime_offset]
mov [rsi+16], rax
mov rax, [r13+st_birthtime_offset+8]
mov [rsi+24], rax
call utimes
mov rdi, [outfilename]
mov rsi, timeval
end if
mov rax, [r13+st_mtime_offset]
mov [rsi+16], rax
mov rax, [r13+st_mtime_offset+8]
mov [rsi+24], rax
call utimes
; 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
; 3. Test for --nologo option. If no, goto 5; note ZF is cleared.
cmp dword [rax], '--no'
jnz @@end
cmp dword [rax+4], 'logo'
jnz @@end
; 4. Remember that --nologo was given and continue from the beginning.
mov [bNoLogo], 1
jmp get_file_name
; 5. No arguments (ZF set) or normal argument (ZF cleared); return.
@@end:
ret
set_outfile:
mov rax, [outfilebest]
xor rax, [outfile1]
xor rax, [outfile2]
mov [outfile], rax
ret
pack_calltrick_fail:
xor eax, eax
xor ebx, ebx
mov [ctn], eax
ret
preprocess_calltrick:
; input preprocessing
push rax
mov edi, [insize]
add edi, edi
call malloc
pop rcx
test rax, rax
jz pack_calltrick_fail
push rax
xor eax, eax
mov rdi, offset ct1
mov ecx, 256/4
push rdi
rep stosd
pop rdi
mov ecx, [insize]
mov rsi, [infile]
xchg eax, edx
pop rax
xchg rax, rbx
push rbx
input_pre:
lodsb
sub al, 0E8h
cmp al, 1
ja short input_pre_cont
cmp ecx, 5
jb short input_pre_done
lodsd
add eax, esi
sub eax, dword ptr [infile]
cmp eax, [insize]
jae short xxx
cmp eax, 1000000h
jae short xxx
sub ecx, 4
bswap eax
mov [rsi-4], eax
inc edx
mov [rbx], rsi
add rbx, 8
jmp short input_pre_cont
xxx: sub rsi, 4
movzx eax, byte ptr [rsi]
mov byte ptr [rax+rdi], 1
input_pre_cont:
loop input_pre
input_pre_done:
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
pack_lzma:
push rcx
mov rdi, [infile]
mov rsi, [outfile]
add rsi, 11
mov edx, [insize]
mov rcx, [workmem]
call lzma_compress
pop rcx
mov rcx, [outfile]
mov edx, [rcx+12]
bswap edx
mov [rcx+12], edx
dec eax
ret
preprocess_calltrick2:
; restore input
mov rsi, [infile]
mov ecx, [ctn]
jecxz pc2l2
pc2l1:
lodsb
sub al, 0E8h
cmp al, 1
ja short pc2l1
mov al, [cti]
cmp [rsi], al
jnz short pc2l1
lodsd
mov al, 0
bswap eax
sub eax, esi
add eax, dword ptr [infile]
mov [rsi-4], eax
loop pc2l1
pc2l2:
; 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, [infile]
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 [infile]
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