mirror of
https://github.com/libretro/x86-assembly-cheat.git
synced 2026-11-07 12:47:59 -10:00
387 lines
9.6 KiB
NASM
387 lines
9.6 KiB
NASM
; TODO: this file is being split up into smaller files
|
|
;
|
|
; TODO: split nasm concepts from IA-32 concepts.
|
|
;
|
|
; Main cheat on the x86 architecture and on NASM.
|
|
|
|
%include "lib/asm_io.inc"
|
|
|
|
; # extern
|
|
|
|
; Same as C extern
|
|
|
|
; C compilers automatically link to libc, so all we need to use
|
|
; C stdlib functions when using the c driver is to declare them
|
|
; extern and then follow the c calling convention.
|
|
|
|
; # macro
|
|
|
|
; # Define macros
|
|
|
|
; # Inner macro labels
|
|
|
|
; Labels inside macros prefixed by `%%` are special:
|
|
; for each macro invocation they generate a new unique label,
|
|
; which is not visible on the object files.
|
|
|
|
; This allows macros to behave like functions.
|
|
|
|
; TODO example
|
|
|
|
; # section
|
|
|
|
; Creates ELF sections.
|
|
|
|
; Each section name can be used many times:
|
|
; NASM just concatenates all of those that have the same name on the output.
|
|
|
|
; # segment
|
|
|
|
; Synonym for `section`. Don't use it.
|
|
|
|
; # data section
|
|
|
|
; # .data
|
|
|
|
; Initialized data.
|
|
|
|
; TODO ELF standard section.
|
|
DATA
|
|
|
|
db0 db 0x10
|
|
db0_2 db 0x10, 0x20
|
|
dw0 dw 0x1000
|
|
dd0 dd 0x1000_0000
|
|
dq0 dq 0x1000_0000_2000_0000
|
|
|
|
dd0dd0 dd dd0
|
|
|
|
; ERROR The larger ones do not accept numeric constants. TODO how to use them then?
|
|
|
|
;dt0 dt 0x0
|
|
;do0 do 0x0
|
|
;dy0 dy 0x0
|
|
;dz0 dz 0x0
|
|
|
|
; # Literals
|
|
|
|
; TODO move to main and assert them all.
|
|
; Only leave on eof each data type here.
|
|
|
|
; # integer literals
|
|
|
|
b1 db 110101b ; byte initialized to binary 110101 (53 in decimal)
|
|
b2 db 12h ; byte initialized to hex 12 (18 in decimal)
|
|
b3 db 17o ; byte initialized to octal 17 (15 in decimal)
|
|
b4 db "A" ; byte initialized to ASCII code for A (65)
|
|
|
|
;b5 db A0h ; ERROR: num cannot start with letter
|
|
b5 db 0x0A0
|
|
|
|
d0 dd 1A92h ; double word initialized to hex 1A92
|
|
d1 dd 1A92h ; double word initialized to hex 1A92
|
|
|
|
; # Array literals
|
|
|
|
; Cannot be put directly into the code, as they:
|
|
|
|
; - would take multiple instructions to load
|
|
; - would occupy memory many times if used many times
|
|
|
|
bs4 db 0, 1, 2, 3 ; TODO Unused
|
|
bs4_2 db 0, 0, 0, 0 ; TODO Unused
|
|
bs10 times 10 db 0
|
|
bs20 times 10 db 0
|
|
times 10 db 1
|
|
|
|
; # String literals
|
|
|
|
; Unlike the c stdlib convention, they do not to end in '\0'.
|
|
|
|
; If you want to use them, you need to either:
|
|
|
|
; - add the `0` for libc string methods
|
|
; - pass the size to libc mem methods, or direct system calls
|
|
|
|
;"abcd\n":
|
|
|
|
bs5 db "a", "b", "c", 'd', 0
|
|
|
|
; # strlen(s)
|
|
|
|
; MUST FOLLOW bs5 immediately
|
|
; because `$` is the cur address
|
|
|
|
bs5_len equ $ - bs5
|
|
|
|
; Same
|
|
bs5_2 db 'aaaa', 0
|
|
; Null terminated
|
|
bs4n db 'abc', 0
|
|
|
|
prompt_int db "enter int: ", 0
|
|
filepath db './_out/out.tmp', 0
|
|
|
|
section .rodata
|
|
|
|
; WARNING: uninitialized space declared in non-BSS section `.rodata': zeroing.
|
|
;rodata_resd resd 1
|
|
|
|
rodata_dd dd 1
|
|
|
|
section .mycustomsection
|
|
|
|
mycustomsection_dd dd 1
|
|
|
|
; # bss section
|
|
|
|
; Uninitialized data.
|
|
|
|
; TODO ELF standard section? Guaranteed to be 0 initialized?
|
|
section .bss
|
|
|
|
; # resb
|
|
|
|
; # resb
|
|
|
|
; # resw
|
|
|
|
; # resd
|
|
|
|
; # resq
|
|
|
|
; # rest
|
|
|
|
; # reso
|
|
|
|
; # resy
|
|
|
|
; # resz
|
|
|
|
resb0 resb 1
|
|
resw0 resw 1
|
|
resd0 resd 1
|
|
resd1 resd 1
|
|
resq0 resq 1
|
|
rest0 rest 1
|
|
reso0 reso 1
|
|
reso1 reso 1
|
|
resy0 resy 1
|
|
; NASM 2.11
|
|
;resz0 resz 1
|
|
|
|
ENTRY
|
|
|
|
; # pseudo-instructions
|
|
|
|
; *Not* assembly instructions,
|
|
; but NASM instructions that insert bytes directly into the output file.
|
|
|
|
; Their syntax just happens look like instructions (insane IMHO).
|
|
|
|
; - `db` family
|
|
; - `resb` family
|
|
; - `incbin`
|
|
; - `times`
|
|
; - `equ`
|
|
|
|
; # db
|
|
|
|
; # dw
|
|
|
|
; # dd
|
|
|
|
; Pseudo-instructions that insert bytes directly on the output and give them a label.
|
|
|
|
; This just happens to be exactly how ELF encodes `.data` section data.
|
|
|
|
ASSERT_EQ [db0], 0x10, byte
|
|
ASSERT_EQ [db0_2], 0x2010, word
|
|
ASSERT_EQ [dw0], 0x1000, word
|
|
ASSERT_EQ [dd0], 0x1000_0000, dword
|
|
|
|
; TODO why inverted? Endianess?
|
|
|
|
ASSERT_EQ [dq0], 0x2000_0000, dword
|
|
ASSERT_EQ [dq0 + 4], 0x1000_0000, dword
|
|
|
|
; We could not do a `qword` `cmp` here because we are in 32-bit mode.
|
|
|
|
; # db in .text
|
|
|
|
; Insert two 0x90 bytes in the output:
|
|
; they are both NOP instructions, so it's fine.
|
|
|
|
; We could code everything like that if we wanted :-)
|
|
; But then coding with `printf '\01'` would be more direct.
|
|
|
|
db 0x90
|
|
db 0x90
|
|
|
|
; # section
|
|
|
|
; # rodata
|
|
|
|
ASSERT_EQ [rodata_dd], 1, dword
|
|
|
|
; SEGFAULT
|
|
;mov dword [rodata_dd], 2
|
|
|
|
; # Custom sections
|
|
|
|
ASSERT_EQ [mycustomsection_dd], 1, dword
|
|
|
|
; SEGFAULT
|
|
;mov dword [mycustomsection_dd], 2
|
|
|
|
; # Detect architecture at compile time
|
|
|
|
; Not possible:
|
|
; http://stackoverflow.com/questions/24124484/how-to-detect-architecture-in-nasm-so-that-i-could-have-one-source-code-for-both
|
|
|
|
; But you can use `__OUTPUT_FORMAT__` to get the value of `-f`.
|
|
|
|
; # Functions
|
|
|
|
; A function is an unconditional branch, but in addition must know:
|
|
|
|
; 1. what address to return to
|
|
; 2. how to pass arguments to the func
|
|
; 3. how to get the return value
|
|
|
|
; These are called the `calling conventions`,
|
|
; and they may vary with language and implementation.
|
|
|
|
; # Suboptimal calling conventions
|
|
|
|
;this shows how one could go about designing calling conventions,
|
|
;only to understand that the call ret way is the best
|
|
|
|
; # Simple calling convention
|
|
|
|
; You could use a calling convention like this
|
|
; but this is too inconvenient:
|
|
|
|
; mov ebx, 1
|
|
; mov ecx, $ + 7
|
|
; jmp print_ebx_simple_cc
|
|
|
|
; which would have resd0 1 and would return to the address inside ecx
|
|
; however this is inconvenient because:
|
|
|
|
; 1. the max number of args is limited by the number of registers
|
|
; 2. you have to manually calculate which address to return to
|
|
|
|
; # with stack
|
|
|
|
; Using the stack is the way to go for function calling
|
|
|
|
; You could do:
|
|
|
|
; push 1
|
|
; push $ + 7
|
|
; jmp print_ebx_stack
|
|
|
|
; and expect the function to take arguments from the stack
|
|
; and the address to return to from the stack
|
|
|
|
; However this is still inconvenient because you have to manually calculate
|
|
; the address to come back to.
|
|
|
|
; # call
|
|
|
|
; # ret
|
|
|
|
; Since calling functions is so common,
|
|
; there are two custom instructions just for that: `call` and `ret`
|
|
|
|
; - `call` pushes address of next instruction to stack and jumps to lbl.
|
|
|
|
; As the name suggests, it is used outside of the functions to call them.
|
|
|
|
; - `ret` pops the stack and jumps to the stored address. It therefore undoes `call`.
|
|
|
|
; Using them is the best way to call and return from functions,
|
|
; since you don't have to manually estimate address delta!
|
|
|
|
; Sample calls of simple calling conventions:
|
|
|
|
mov ebx, 19
|
|
call print_ebx_call_ret
|
|
|
|
mov eax, bs4n
|
|
call print_string
|
|
|
|
mov eax, 13
|
|
call print_int
|
|
|
|
;mov eax, prompt_int
|
|
;call print_string
|
|
;call read_int
|
|
;mov [resd0], eax
|
|
;call print_int
|
|
|
|
; # far call
|
|
|
|
; # far ret
|
|
|
|
; Both call and ret have a form that jumps across segments.
|
|
|
|
; It is analogous to far jumps, and can only be done in ring 0.
|
|
|
|
; # Cryptography
|
|
|
|
; https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/x86/crypto?id=refs/tags/v4.0
|
|
|
|
; # AES
|
|
|
|
; https://en.wikipedia.org/wiki/AES_instruction_set
|
|
|
|
; Intel has extended x86 to add instructions to accelerate AES encryption since 2008,
|
|
; AMD followed. For this reason, the Linux kernel implements AES itself, which allows for hardware acceleration,
|
|
; and OpenSSL can use the API for it: https://en.wikipedia.org/wiki/AES_instruction_set.
|
|
; `/proc/cpuinfo` must have the `aes` `CPUID` flag.
|
|
|
|
; TODO example.
|
|
|
|
; # CRC32
|
|
|
|
; # Cyclic redundancy check
|
|
|
|
; http://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
|
|
|
; Pretty specific stuff. Cool. Used by Ethernet for error checking.
|
|
|
|
; # lfence
|
|
|
|
; TODO something to do with out of order
|
|
|
|
EXIT
|
|
|
|
TEXT
|
|
|
|
; Print ebx \n
|
|
;
|
|
; eax is destroyed
|
|
;
|
|
; ecx contains the address to return to
|
|
;
|
|
print_ebx_simple_cc:
|
|
PRINT_INT ebx
|
|
jmp ecx
|
|
|
|
; print ebx \n
|
|
;
|
|
; eax is destroyed
|
|
print_ebx_stack:
|
|
PRINT_INT ebx
|
|
pop eax
|
|
jmp eax
|
|
|
|
; print ebx \n
|
|
;
|
|
; eax is destroyed
|
|
print_ebx_call_ret:
|
|
PRINT_INT ebx
|
|
ret
|