;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; File Name: CreateHexFile.asm ; ; This program creates a file called asciiasm.bin in the current ; directory and writes 256 bytes into it. It's a simple example ; program written in assembly language for Linux to be compiled ; with nasm. This program is designed so it can be built either ; into an x86 or x86_64 executable without requiring any changes ; to the source code. ; ; To build a 64-bit Linux executable, enter the following ; commands in the terminal: ; ; nasm -f elf64 -o CreateHexFile.o CreateHexFile.asm ; ld -s -no-pie -z noseparate-code CreateHexFile.o -o CreateHexFile ; strip -s CreateHexFile ; ; To build a 32-bit executable, run the following commands: ; ; nasm -f elf32 -o CreateHexFile.o CreateHexFile.asm ; ld -s -no-pie -z noseparate-code -m elf_i386 CreateHexFile.o -o CreateHexFile ; strip -s CreateHexFile ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %if __BITS__ == 64 %define _AX RAX %define _BX RBX %define _CX RCX %define _DX RDX %define _SI RSI %define _DI RDI %define _BP RBP %macro EXIT 1 ; EXIT requires 1 argument MOV RDI,%1 ; Error code will be placed into RDI MOV RAX,60 ; Sys_exit is function #60 in 64-bit kernel SYSCALL %endmacro %else %macro EXIT 1 ; EXIT requires 1 argument MOV EBX,%1 ; Error code will be placed into EBX MOV EAX,1 ; Sys_exit is function #1 in 32-bit kernel INT 0x80 %endmacro %define _AX EAX %define _BX EBX %define _CX ECX %define _DX EDX %define _SI ESI %define _DI EDI %define _BP EBP %endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PROGRAM DATA SECTION ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SECTION .data FileName: DB "asciiasm.bin", 0 Msg_success: DB "File created successfully.", 10, 0 Msg_failure: DB "File could not be created.", 10, 0 Msg_welcome: DB 10, 10, "The following bytes will be written to a file: ", 10, 10 HexCode: DB "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f", 10 DB "10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f", 10 DB "20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f", 10 DB "30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f", 10 DB "40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f", 10 DB "50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f", 10 DB "60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f", 10 DB "70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f", 10 DB "80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f", 10 DB "90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f", 10 DB "a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af", 10 DB "b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf", 10 DB "c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf", 10 DB "d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df", 10 DB "e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef", 10 DB "f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff", 10, 0 SECTION .text GLOBAL _start _start: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PROGRAM BEGINS HERE ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This part of the source code is identical for both x86 and ; x86_64 builds. The '_' in front of the registers stands for ; either an 'E' or an 'R' which will be substituted by nasm ; when it is time to compile the code. MOV _SI,Msg_welcome ; Inform the user about what we are doing CALL PrintText MOV _SI,HexCode MOV _DI,_SI CALL Hex2Str ; Convert hexadecimal digits into binary bytes ; Hex2Str will place the length of string into _DX and the string ; pointer will be left in _DI, which is exactly where ; CreateFile expects them to be: MOV _SI,FileName CALL CreateFile JNC Success_Exit ; CreateFile sets carry flag on error MOV _SI,Msg_failure CALL PrintText ; Print error message EXIT 1 Success_Exit: MOV _SI,Msg_success CALL PrintText ; Print success message EXIT 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CATEGORY: File ; FUNCTION: CreateFile ; CREATED: 2026.1.23 ; PLATFORM: Linux x86 or x86_64 ; DESCR: Creates a binary file ; ; This function creates a new file (if the file already exists, ; then it deletes its contents) and writes some bytes into it. ; ; DS:_SI <= Pointer to ASCIIZ file name ; DS:_DI <= Pointer to data to be written ; _DX <= Number of bytes to write ; _CX => Number of bytes written ; CF => Carry Flag will be set on error ; Modifies: _CX,Flags ; CreateFile: PUSH _AX PUSH _BX PUSH _DX PUSH _SI PUSH _DI %if __BITS__ == 64 PUSH R14 PUSH R13 PUSH R12 PUSH R11 ; I entered the following into Google search: ; Write assembly code that creates a file and writes some bytes into it and results in a linux elf64 executable. ; This gave me a starting point, so then I could change things around a bit... ; Now, whoever designed the x86_64 Linux kernel decided that sys calls will have completely ; different numberings and arguments are to be passed in different registers than x86. ; That person should be put in jail!! ; First of all, we will save the values of RDI and RDX in other registers. MOV R14,RDI MOV R13,RDX ; Create the file. MOV RAX,2 ; The function number for sys_open is 2 in 64-bit mode. ; The kernel expects the following argument(s): ; RDI <= Pointer to the ASCIIZ string that holds the file name. ; RSI <= The file open mode ; RDX <= The permission flags MOV RDI,RSI MOV RSI,65 ; CREATE=1 | TRUNCATE=64 MOV RDX,0o644 ; File permissions: rw-r--r-- SYSCALL ; SYSCALL always modifies RAX, RCX, and R11 MOV R12,RAX ; Save the file descriptor in R12 ; The file descriptor will be a negative number if sys_open failed. SHL RAX,1 ; Shift highest bit of RAX into CF JC CreateFile_Error_x64 ; Jump if RAX was a negative number ; Write bytes to the file. MOV RAX,1 ; The function number for sys_write is 1 in 64-bit mode. ; The 64-bit kernel expects arguments in RDI, RDX, and RSI: MOV RDI,R12 ; Load file descriptor from R12 MOV RDX,R13 ; Load number of bytes to write from R13 MOV RSI,R14 ; Load data pointer from R14 SYSCALL ; SYSCALL always modifies RAX, RCX, and R11 XCHG RDX,RAX ; RDX <= Save number of bytes written ; Close the file. MOV RAX,3 ; The function number for sys_close is 3 in 64-bit mode. MOV RDI,R12 ; Load the file descriptor from R12 SYSCALL ; SYSCALL always modifies RAX, RCX, and R11 MOV RCX,RDX ; RCX <= Load number of bytes written SHL RDX,1 ; Is this a negative number? JNC CreateFile_Exit_x64 CreateFile_Error_x64: XOR _CX,_CX ; Zero bytes written STC CreateFile_Exit_x64: POP R11 POP R12 POP R13 POP R14 %else ; 32-bit version of the code follows next: ; Create and Open File MOV EAX,5 ; The function number for sys_open is 5 in 32-bit mode. ; The 32-bit kernel expects arguments in EBX=filename, ECX=flags, and EDX=mode. MOV EBX,ESI ; EBX <= File name MOV ESI,EDX ; Save number of bytes to write in ESI temporarily MOV ECX,65 ; CREATE=64 | WRITEONLY=1 MOV EDX,0o644 ; File permissions: rw-r--r-- INT 0x80 ; The kernel returns the file descriptor returned in EAX. MOV EBX,EAX ; EBX <= Save file descriptor ; The file descriptor will be a negative number if sys_open failed. SHL EAX,1 ; Shift highest bit of EAX into CF JC CreateFile_Error_x86 ; Jump if EAX was a negative number MOV EAX,4 ; The function number for sys_write is 4 in 32-bit mode. ; The 32-bit kernel expects arguments in EBX=file_descriptor, ECX=buffer, and EDX=length. ; EBX is already set from above MOV ECX,EDI MOV EDX,ESI ; Restore number of bytes to write from ESI INT 0x80 MOV ECX,EAX ; Save number of bytes written in ECX MOV EAX,6 ; The function number for sys_close is 6 in 32-bit mode. INT 0x80 ; Expects file descriptor in EBX which is already set. MOV EAX,ECX SHL EAX,1 ; Shift highest bit of EAX into CF JNC CreateFile_Exit ; Exit normally if it was a positive number. CreateFile_Error_x86: XOR ECX,ECX STC %endif CreateFile_Exit: POP _DI POP _SI POP _DX POP _BX POP _AX RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CATEGORY: Stdio ; FUNCTION: PrintText ; MODIFIED: 2026.1.22 ; PLATFORM: Linux x86 or x86_64 ; DESCR: Prints an ASCIIZ string to stdout ; ; This function prints an ASCIIZ string to stdout. ; ; DS:_SI <= Pointer to ASCIIZ string ; Modifies: None ; PrintText: PUSHF PUSH _SI PUSH _DI PUSH _DX PUSH _AX CALL StrLen %if __BITS__ == 64 MOV RDI,1 ; File descriptor 1 is STDOUT MOV RAX,1 SYSCALL %else PUSH ECX MOV ECX,ESI ; ECX <= Pointer to text MOV EBX,1 ; File descriptor 1 is STDOUT MOV EAX,4 INT 0x80 POP ECX %endif POP _AX POP _DX POP _DI POP _SI POPF RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CATEGORY: String ; FUNCTION: StrLen ; MODIFIED: 2026.1.22 ; PLATFORM: x86 or x86_64, OS independent ; DESCR: Returns the length of an ASCIIZ string ; ; This function finds the terminating NULL character at the end of ; a string and returns the length of string in EDX. ; ; DS:_SI <= Pointer to ASCIIZ string ; _DX => Length of string ; Modifies: _DX,Flags ; StrLen: PUSH _SI PUSH _CX PUSH _AX XOR _DX,_DX ; Reset byte counter. MOV _CX,_SI NEG _CX DEC _CX ; ECX <= maximum allowed length of string CLD ; We will be reading in forward direction StrLen_Loop: LODSB TEST AL,AL JE StrLen_EndOfString INC _DX LOOP StrLen_Loop StrLen_EndOfString: POP _AX POP _CX POP _SI RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CATEGORY: String ; FUNCTION: Hex2Str ; MODIFIED: 2026.1.22 ; PLATFORM: x86 or x86_64, OS independent ; DESCR: Converts hexadecimal digits to binary string ; ; This function converts an ASCIIZ string of hexadecimal numbers ; to binary string. The numbers are grouped into pairs from left to ; right and then each pair is converted to a byte. Non-hexadecimal ; characters are ignored. If there's an odd number of hexadecimal ; digits in the input string, then the function pretends that there ; is a zero at the end completing the last byte. ; ; DS:_SI <= Pointer to ASCIIZ string containing hex digits ; ES:_DI <= Pointer to destination memory area ; _DX => Number of bytes written ; Modifies: _DX,Flags ; Hex2Str: PUSH _SI PUSH _DI PUSH _AX XOR _DX,_DX ; Reset byte counter. CLD ; Read/write operations move in forward direction. ; We will use the high 4 bits of AH to store the upper nibble of ; the byte. AH will be set to 255 when it is empty or uninitialized. Hex2Str_Reset: MOV AH,255 Hex2Str_Loop: LODSB TEST AL,AL JE Hex2Str_EndofString CMP AL,'0' JB Hex2Str_Loop ; Skip non-hex char CMP AL,'f' JA Hex2Str_Loop ; Skip non-hex char CMP AL,'9' JA Hex2Str_NonDigit SUB AL,'0' JMP Hex2Str_SaveValue Hex2Str_NonDigit: OR AL,32 SUB AL,'a' CMP AL,5 JA Hex2Str_Loop ; Skip non-hex char ADD AL,10 Hex2Str_SaveValue: CMP AH,255 JE Hex2Str_StoreHigh ; Got second hex digit. Now, merge them together and save byte. OR AL,AH STOSB INC _DX JMP Hex2Str_Reset Hex2Str_StoreHigh: SHL AX,12 ; Transfer low 4 bits from AL into high 4 bits of AH JMP Hex2Str_Loop ; Stored first hex digit. Hex2Str_EndofString: ; Save last byte if odd number of digits. CMP AH,255 JE Hex2Str_Exit MOV AL,AH STOSB INC _DX Hex2Str_Exit: POP _AX POP _DI POP _SI RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;