-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.asm
649 lines (577 loc) · 16.5 KB
/
main.asm
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
;;
;; MC404ABCD - 2008s2
;; Projeto 2
;; decompify - COM files disassembler for Linux.
;;
;; 071294 - Jorge Augusto Hongo
;; 072201 - Raphael Kubo da Costa
;;
;; Copyright (C) 2008 Jorge Augusto Hongo
;; Copyright (C) 2008 Raphael Kubo da Costa
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;
%include "instr.h"
%include "opcodes.h"
%include "string.h"
%include "syscalls.h"
%include "util.h"
;; Number of command line arguments
%define ARGC 3
;; Maximum size of a .COM file
%define COMFILEMAXSIZE 0xFFFF
;; GetArrayPosition(void *buffer, int pos, size_t item_size)
;; Destroys: ebx
%macro GetArrayPosition 3
push eax
push ecx
xor ecx, ecx
mov cl, %2
mov ebx, %1
mov eax, %3
mul ecx
add ebx, eax
pop ecx
pop eax
%endmacro
;; In: al
;; Returns a register from ARRAY_8 or 16BITREGS
%macro GetRegister 1
test [%1_reg16bits], byte 1
jz %%8bitreg
mov edx, ARRAY_16BITREGS
jmp %%reg
%%8bitreg:
mov edx, ARRAY_8BITREGS
%%reg:
GetArrayPosition edx, al, 4
%endmacro
;; r/m-related macros
;; ------------------
%macro GetRMmod 1
shr %1, 6
and %1, 0x3
%endmacro
%macro GetRMreg 1
shr %1, 3
and %1, 0x7
%endmacro
%macro GetRMrm 1
and %1, 0x7
%endmacro
;; PrintArguments (argN)
;; Main function to display an instruction.
%macro PrintArguments 1
cmp dword [%1_type], ARGTYPE_REGDS
jbe near %%arg_constant
cmp dword [%1_type], ARGTYPE_MEMORY
je near %%arg_memory
cmp dword [%1_type], ARGTYPE_IMMED
je near %%arg_immed
cmp dword [%1_type], ARGTYPE_RELATIVE
je near %%arg_relative
cmp dword [%1_type], ARGTYPE_FAR
je near %%arg_far
cmp dword [%1_type], ARGTYPE_SIMMED
je near %%arg_simmed
cmp dword [%1_type], ARGTYPE_RM_BOTH
jae near %%arg_rm
%%arg_constant:
mov edi, [%1_displacement]
exec write_string, [asmfile_fd], [edi]
jmp near %%arg_end
%%arg_memory:
exec write_string, [asmfile_fd], memstart
call write_segment
exec write_string, [asmfile_fd], hexstart
exec write_hex, [asmfile_fd], [%1_displacement], 16
exec write_string, [asmfile_fd], memend
jmp near %%arg_end
%%arg_immed:
exec write_string, [asmfile_fd], hexstart
test byte [%1_reg16bits], 1
jz %%arg_immed8
exec write_hex, [asmfile_fd], [%1_displacement], 16
jmp %%arg_end
%%arg_immed8:
exec write_hex, [asmfile_fd], [%1_displacement], 8
jmp near %%arg_end
%%arg_relative:
exec write_string, [asmfile_fd], s_dollar
exec write_string, [asmfile_fd], s_dollar
exec write_string, [asmfile_fd], plus
exec write_string, [asmfile_fd], hexstart
test byte [%1_reg16bits], 1
jz %%arg_relative8
exec write_hex, [asmfile_fd], [%1_displacement], 16
jmp %%arg_end
%%arg_relative8:
exec write_hex, [asmfile_fd], [%1_displacement], 8
jmp near %%arg_end
%%arg_far:
exec write_string, [asmfile_fd], hexstart
exec write_hex, [asmfile_fd], [arg2_displacement], 16
exec write_string, [asmfile_fd], colon
exec write_string, [asmfile_fd], hexstart
exec write_hex, [asmfile_fd], [arg1_displacement], 16
jmp near %%arg_end
%%arg_simmed:
exec write_string, [asmfile_fd], s_byte
exec write_string, [asmfile_fd], space
test byte [%1_displacement], 0x80
jnz %%arg_simmed_neg
exec write_string, [asmfile_fd], plus
jmp %%arg_simmed_writenum
%%arg_simmed_neg:
exec write_string, [asmfile_fd], minus
%%arg_simmed_writenum:
exec write_string, [asmfile_fd], hexstart
exec write_hex, [asmfile_fd], [%1_displacement], 8
jmp near %%arg_end
;; FIXME: this is a behemoth
%%arg_rm:
;; XXX: hack for GRP3 and its annoying operators
;; if we have 'jmp/call far/near', we need to write
;; these words before the operands
cmp dword [group_id], ARRAY_GRP_GRP3
jne near %%not_grp3
cmp dword [arg1_basereg], SARGTYPE_REGDX
je %%grp3_write_near
cmp dword [arg1_basereg], SARGTYPE_REGSP
je %%grp3_write_near
cmp dword [arg1_basereg], SARGTYPE_REGBX
je %%grp3_write_far
cmp dword [arg1_basereg], SARGTYPE_REGBP
je %%grp3_write_far
jmp %%not_grp3
%%grp3_write_near:
exec write_string, [asmfile_fd], s_near
exec write_string, [asmfile_fd], space
jmp %%not_grp3
%%grp3_write_far:
exec write_string, [asmfile_fd], s_far
exec write_string, [asmfile_fd], space
;; Checks if we only have a basereg
%%not_grp3:
cmp dword [%1_type], ARGTYPE_RM_REGISTER
je %%write_register_basereg
cmp dword [%1_type], ARGTYPE_RM_SEGMENT
je %%write_segment_basereg
jmp %%arg_rm_not_rm_register
%%write_register_basereg:
exec write_string, [asmfile_fd], [arg1_basereg]
jmp near %%arg_end
%%write_segment_basereg:
exec write_string, [asmfile_fd], [%1_basereg]
jmp near %%arg_end
%%arg_rm_not_rm_register:
cmp dword [arg1_indexreg], SARGTYPE_REGDI
ja %%arg_rm_normalflow
cmp dword [arg1_indexreg], 0
je %%arg_rm_normalflow
cmp dword [arg1_displacement], 0
jne %%arg_rm_normalflow
exec write_string, [asmfile_fd], [arg1_indexreg]
jmp %%arg_end
%%arg_rm_normalflow:
;; Only write 'word' or 'byte' in the first operand
%ifidn %1,arg1
cmp byte [arg1_reg16bits], 1
jne %%write_byte
exec write_string, [asmfile_fd], s_word
jmp %%write_normalflow
%%write_byte:
exec write_string, [asmfile_fd], s_byte
%%write_normalflow:
exec write_string, [asmfile_fd], space
%endif
exec write_string, [asmfile_fd], memstart
call write_segment
cmp dword [arg1_indexreg], 0
je %%arg_rm_writedisp
exec write_string, [asmfile_fd], [arg1_indexreg]
cmp dword [arg1_displacement], 0
je %%arg_rm_writeend
exec write_string, [asmfile_fd], plus
%%arg_rm_writedisp:
exec write_string, [asmfile_fd], hexstart
exec write_hex, [asmfile_fd], [arg1_displacement], 16
%%arg_rm_writeend:
exec write_string, [asmfile_fd], memend
jmp %%arg_end
%%arg_end:
%endmacro
%macro ProcessArgument 1
; Constant arguments
cmp dword [%1_type], ARGTYPE_NONE
je near %%addr_end
cmp dword [%1_type], ARGTYPE_REGDS ; Last constant argument in the array
jbe near %%addr_const
cmp dword [%1_type], ARGTYPE_RM_BOTH ; First of its kind in the array
jae near %%addr_regmem
cmp dword [%1_type], ARGTYPE_MEMORY ; Last of its kind
jbe near %%addr_memory
cmp dword [%1_type], ARGTYPE_RELATIVE
je near %%addr_relative
cmp dword [%1_type], ARGTYPE_FAR ; FAR can only be used as arg1_type, not arg2!
je near %%addr_far
%%addr_const:
;; %1_displacement = ARRAY_CONSTARGS[4*argN_type]
mov edx, [%1_type]
GetArrayPosition ARRAY_CONSTARGS, [edx], 4
mov [%1_displacement], ebx
jmp %%addr_end
%%addr_regmem:
ProcessRM %1
jmp %%addr_end
%%addr_memory:
xor edx, edx
test [%1_reg16bits], byte 1
jz %%addr_memory8
mov dx, [comfile+esi]
inc esi
inc esi
jmp %%addr_memoryset
%%addr_memory8:
mov dl, [comfile+esi]
inc esi
%%addr_memoryset:
mov [%1_displacement], edx
jmp %%addr_end
%%addr_relative:
xor edx, edx
test [%1_reg16bits], byte 1
jz %%addr_relative8
mov ax, [comfile+esi]
inc esi
inc esi
jmp %%addr_relativeset
%%addr_relative8:
mov al, [comfile+esi]
cbw
mov byte [%1_reg16bits], 1 ;; After the operations, it needs to be a word
inc esi
%%addr_relativeset:
add eax, esi
mov [%1_displacement], eax
jmp %%addr_end
%%addr_far:
xor eax, eax
mov ax, [comfile+esi]
mov [arg1_displacement], eax
inc esi
inc esi
xor eax, eax
mov ax, [comfile+esi]
mov [arg2_displacement], eax
inc esi
inc esi
%%addr_end:
%endmacro
%macro ProcessRM 1
;; This should execute only once per instruction;
;; the arguments for arg2 are set in the first run
%ifidn %1, arg1
mov cl, [comfile+esi]
mov al, cl
;; modREGrm
GetRMreg al
GetRegister arg1
StoreData 32, [ebx], [arg1_basereg]
;; If it's a segment register, overwrite [argN_basereg] with the new pointer
RewriteBaseIfSegment arg1
RewriteBaseIfSegment arg2
;; By default, set displacement and indexreg to zero
;; If they're used, they're set to their values later
StoreData 32, dword 0, [arg1_displacement]
StoreData 32, dword 0, [arg1_indexreg]
;; MODregrm
mov al, cl
GetRMmod al
cmp al, 0
je %%mod00
cmp al, 1
je %%mod01
cmp al, 2
je %%mod10
cmp al, 3
je %%mod11
%%mod00:
StoreData 32, dword 0, [arg1_displacement]
mov al, cl
and al, 7
cmp al, 6
jne %%mod_end
jmp %%mod10
%%mod01:
xor eax, eax
mov al, [comfile+esi+1]
cbw
cwde
StoreData 32, eax, [arg1_displacement]
inc esi
jmp %%mod_end
%%mod10:
xor eax, eax
mov ax, [comfile+esi+1]
StoreData 32, eax, [arg1_displacement]
inc esi
inc esi
jmp %%mod_end
%%mod11:
mov al, cl
GetRMrm al
GetRegister arg1
StoreData 32, [ebx], [arg1_indexreg]
jmp %%end ;; rm is a reg field, skip its parsing
%%mod_end:
;; modregRM
mov al, cl
GetRMmod al
cmp al, 0
jne %%rm_notmod110
mov al, cl
and al, 7
cmp al, 6
je %%end ;; mod==00 && rm==110 has been treated in %%mod00
%%rm_notmod110:
mov al, cl
GetRMrm al
GetArrayPosition ARRAY_RM_MODES, al, 4
StoreData 32, [ebx], [arg1_indexreg]
%%end:
inc esi
%endif
%endmacro
%macro RewriteBaseIfSegment 1
cmp dword [%1_type], ARGTYPE_RM_SEGMENT
jne %%end
GetArrayPosition ARRAY_SEGMENTS, al, 4
StoreData 32, [ebx], [%1_basereg]
%%end:
%endmacro
;; In: register size, src, dest
;; Destroys: edx
;; Handy macro when you have something like mov [dest], [src]
%macro StoreData 3
%push TempContext
%if %1 == 8
%define %$reg dl
%elif %1 == 32
%define %$reg edx
%else
%error "First argument must be 8 or 32"
%endif
mov %$reg, %2
mov %3, %$reg
%pop
%endmacro
section .bss
;; Files (input and output)
asmfile_fd resw 2
asmfile_name resb 255
comfile resb COMFILEMAXSIZE
comfile_fd resw 2
comfile_name resb 255
comfile_size resw 2
;; Opcode and its operands
opcode resb 1
mnemonic resd 1
group_id resd 1
segment_id resd 1
arg1_type resd 1
arg1_basereg resd 1
arg1_indexreg resd 1
arg1_reg16bits resb 1
arg1_displacement resd 1
arg2_type resd 1
arg2_basereg resd 1
arg2_indexreg resd 1
arg2_reg16bits resb 1
arg2_displacement resd 1
section .data
;; Error messages
open_input_file_msg db "Erro ao abrir arquivo executavel", 0x0A, 0
open_output_file_msg db "Erro ao abrir arquivo de saida", 0x0A, 0
usage_msg db "Uso: ./decompify executavel.com output.asm", 0x0A, 0
section .text
extern disasm_write_header, get_file_size, strlen, write_hex, write_string
global _start
load_opcode_data:
;; If it's a segment, load the next byte and preserve the segment reference
cmp dword [ebx + Opcode.segment_id], 0
je .not_segment
StoreData 32, [ebx + Opcode.segment_id], [segment_id]
GetArrayPosition opcodes, [comfile + esi], Opcode_size
mov cl, [comfile+esi]
mov [opcode], cl
inc esi
jmp .load_data
.not_segment:
StoreData 32, [ebx + Opcode.segment_id], [segment_id]
.load_data:
StoreData 32, [ebx + Opcode.mnemonic], [mnemonic]
StoreData 32, [ebx + Opcode.group_id], [group_id]
StoreData 32, [ebx + Opcode.arg1_type], [arg1_type]
StoreData 8, [ebx + Opcode.arg1_reg16bits], [arg1_reg16bits]
StoreData 32, [ebx + Opcode.arg2_type], [arg2_type]
StoreData 8, [ebx + Opcode.arg2_reg16bits], [arg2_reg16bits]
;; If it's a group, move the mnemonic to [mnemonic]
cmp [group_id], dword 0
je .process_arguments
mov al, [comfile+esi]
GetRMreg al
GetArrayPosition [group_id], al, 4 ;; ebx = group_id[4*al]
StoreData 32, [ebx], [mnemonic]
cmp dword [mnemonic], 0
jne .process_arguments
StoreData 32, ARGTYPE_NONE, [arg1_type] ;; If the mnemonic was not found,
StoreData 32, ARGTYPE_NONE, [arg2_type] ;; make the arguments None.
.process_arguments:
ProcessArgument arg1
ProcessArgument arg2
ret
print_instruction:
cmp dword [arg1_type], ARGTYPE_RM_BOTH
jae .check_mnemonic
cmp dword [arg1_type], ARGTYPE_MEMORY
je .check_mnemonic
cmp dword [segment_id], 0
je .check_mnemonic
call write_segment_db ;; Write db 0xSEGCODE in case we need to
.check_mnemonic:
cmp dword [mnemonic], 0
jne .known_byte
exec write_string, [asmfile_fd], s_db
exec write_string, [asmfile_fd], space
exec write_string, [asmfile_fd], hexstart
exec write_hex, [asmfile_fd], [opcode], 8
jmp near .end
.known_byte:
;; Mnemonic
exec write_string, [asmfile_fd], [mnemonic]
;; Argument 1
cmp dword [arg1_type], ARGTYPE_NONE
je near .end
exec write_string, [asmfile_fd], space
PrintArguments arg1
;; Argument 2
cmp dword [arg2_type], ARGTYPE_NONE
je near .end
exec write_string, [asmfile_fd], comma
PrintArguments arg2
.end:
exec write_string, [asmfile_fd], nl
ret
;; Writes the 'segment:', like ds:
write_segment:
cmp dword [segment_id], 0
je .end
exec write_string, [asmfile_fd], [segment_id]
exec write_string, [asmfile_fd], colon
.end:
ret
;; Writes 'db 0xREGCODE', like db 0x26.
write_segment_db:
exec write_string, [asmfile_fd], s_db
exec write_string, [asmfile_fd], space
exec write_string, [asmfile_fd], hexstart
cmp dword [segment_id], SARGTYPE_REGES
je .write_db_es
cmp dword [segment_id], SARGTYPE_REGCS
je .write_db_cs
cmp dword [segment_id], SARGTYPE_REGSS
je .write_db_ss
cmp dword [segment_id], SARGTYPE_REGDS
je .write_db_ds
jmp .end
.write_db_es:
exec write_hex, [asmfile_fd], 0x26, 8
jmp .end
.write_db_cs:
exec write_hex, [asmfile_fd], 0x2E, 8
jmp .end
.write_db_ss:
exec write_hex, [asmfile_fd], 0x36, 8
jmp .end
.write_db_ds:
exec write_hex, [asmfile_fd], 0x3E, 8
.end:
exec write_string, [asmfile_fd], nl
ret
_start:
;; Check arguments and exit if the command line is incorrect
pop eax
cmp eax, ARGC
jne near .exit_usage
;; Skip the first argument (executable name) and get the file names
pop eax
pop dword [comfile_name]
pop dword [asmfile_name]
;; Open the .COM file, exit on error
sys_open [comfile_name]
cmp eax, -1
jle near .exit_open_input_file
mov [comfile_fd], eax
;; Read its whole content and close the file
push word [comfile_fd]
call get_file_size
add esp, 4
mov [comfile_size], eax
sys_lseek [comfile_fd], 0, SEEK_SET
sys_read [comfile_fd], comfile, [comfile_size]
sys_close [comfile_fd]
;; Open the output .ASM file, exit on error
sys_open [asmfile_name], O_WRONLY|O_TRUNC|O_CREAT
cmp eax, -1
jle near .exit_open_output_file
mov [asmfile_fd], eax
;; Write the code header
push dword [asmfile_fd]
call disasm_write_header
add esp, 4
;; Main loop
xor esi, esi
.main_loop:
cmp esi, [comfile_size]
jae near .end_main_loop
;; Get the corresponding opcode position in the table
;; ebx = opcodes[[comfile+esi]]
GetArrayPosition opcodes, [comfile + esi], Opcode_size
mov cl, [comfile+esi]
mov [opcode], cl
;; Increment position counter
inc esi
call load_opcode_data
call print_instruction
jmp .main_loop
.end_main_loop:
;; There may be a segment byte dangling around...
cmp dword [segment_id], 0
je .no_seg_left
call write_segment_db
.no_seg_left:
;; Close the files and exit
sys_close [asmfile_fd]
sys_exit EX_OK
.exit_open_input_file:
print_string(open_input_file_msg)
sys_exit EX_DATAERR
.exit_open_output_file:
print_string(open_output_file_msg)
sys_exit EX_DATAERR
.exit_usage:
print_string(usage_msg)
sys_exit EX_USAGE
; vim:syntax=nasm: