Last active
September 18, 2022 18:53
-
-
Save ped7g/c96a7eec86f9b090d0f33ba36af056c1 to your computer and use it in GitHub Desktop.
linux x86_64 asm example of "base64 encoder" (for lecturing purposes)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; (C) [copyleft] 2021 Peter Helcmanovsky | |
; License: CC0 https://creativecommons.org/share-your-work/public-domain/cc0 | |
; | |
; x86_64 linux asm example of base64 encoding | |
; | |
; reads stdin, encodes it to base64 without new-lines, outputs to stdout | |
; | |
; the code is meant to be rather straightforward and simple (for lecturing purpose), | |
; not performance optimal | |
; | |
; to build I'm using nasm and ld: | |
; nasm -f elf64 ped_base64.asm -l out.lst && ld ped_base64.o -o base64en | |
SECTION .data ; Section containing initialised data | |
Base64Table: db "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | |
SECTION .bss ; Section containing uninitialized data | |
ChunkSize: equ 1000 ; size in 3-byte work units (3000 bytes) | |
ReadBufferSize: equ ChunkSize * 3 | |
ReadBuffer: resb ReadBufferSize + 8 ; +8 bytes is padding for mem-access safety | |
OutputBufferSize: equ ChunkSize * 4 | |
OutputBufer: resb OutputBufferSize + 8 ; +8 bytes is padding (mem-access and "=" overwrite zone) | |
SECTION .text ; Section containing code | |
global _start ; Linker needs this to find the entry point! | |
_start: | |
; load some common values into preserved registers (these are "global") | |
xor ebp,ebp ; rbp = 0 | |
mov r12,ReadBuffer | |
mov r13,OutputBufer | |
.processChunkLoop: | |
call readInput | |
test rax,rax | |
jle .err_or_exit ; error or zero length -> exit | |
mov ecx,eax | |
call processChunk ; leaves size of output in rdi | |
call writeOutput | |
jmp .processChunkLoop ; loop until full input was processed | |
.err_or_exit: | |
mov rdi,rax ; error code = 0 when OK, otherwise err_no of sys_read() | |
mov rax,60 ; sys_exit | |
syscall | |
readInput: ; out rax = length or error (<0) | |
xor eax,eax ; rax = 0 = sys_read | |
xor edi,edi ; rdi = 0 = stdin | |
mov rsi,r12 ; rsi = read buffer for data | |
mov edx,ReadBufferSize ; rdx = bytes to read (32b value) | |
syscall | |
ret | |
writeOutput: ; in rdi = length of output data to print | |
mov rdx,rdi ; output size | |
mov rsi,r13 ; OutputBufer | |
lea rax,[rbp+1] ; rax = 1 (sys_write) | |
mov rdi,rax ; rdi = 1 (stdout) | |
syscall | |
ret | |
processChunk: ; rcx = size of data to process (non zero value) | |
mov rsi,r12 ; rsi = ReadBuffer | |
mov rdi,r13 ; rdi = OutputBufer | |
lea r8,[rsi+rcx] ; r8 = first byte after bytes read (address) | |
mov [r8],ebp ; make sure input data are padded with 4+ zeroes | |
; convert input data into base64 encoding | |
.loop: | |
mov eax,[rsi] | |
add rsi,3 | |
bswap eax | |
shr eax,8 ; 24 bits (4x6) of input in big-endian order | |
; => b23..b18 is first 6-bits value to output, b5..b0 is last one | |
mov edx,eax ; copy eax into edx | |
shr eax,6 ; remove 6 from eax because they will already be processed | |
and edx,0x3F ; keep only last 6 bits | |
mov bh,[Base64Table+rdx]; translate into base64 (fourth output char) | |
mov edx,eax ; get copy of next 6 bits | |
shr eax,6 ; remove 6 processed bits | |
and edx,0x3F ; keep only last 6 bits | |
mov bl,[Base64Table+rdx]; translate into base64 (third output char) | |
shl ebx,16 ; shift fourth+third char into upper 16 bits of ebx | |
mov edx,eax ; get next 6 bits | |
shr eax,6 ; remove 6 processed bits | |
and edx,0x3F ; keep only last 6 bits | |
mov bh,[Base64Table+rdx]; convert bits into base64 (second output char) | |
mov bl,[Base64Table+rax]; convert bits into base64 (first output char) | |
mov [rdi],ebx ; write four output bytes | |
add rdi,4 | |
cmp rsi,r8 | |
jb .loop ; loop until all input data were processed (rsi >= r8) | |
; patch the output chunk to end with '=' or '==' if (0 != (size%3)) | |
sub r8,rsi ; r8 = 0, -1 (one char extra in output) or -2 (two chars extra) | |
mov word [rdi+r8],'==' ; overwrite any extra chars in output with '=' | |
sub rdi,r13 ; rdi = size of output data | |
ret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; (C) [copyleft] 2021 Peter Helcmanovsky | |
; License: CC0 https://creativecommons.org/share-your-work/public-domain/cc0 | |
; | |
; x86_64 linux asm example of base64 encoding | |
; | |
; reads stdin, base64 encodes it to stdout, adding new-line after 76 output characters | |
; (works the same way as `base64` in common linux installation) | |
; | |
; the code is meant to be rather straightforward and simple (for lecturing purpose), | |
; not performance optimal | |
; | |
; to build I'm using nasm and ld: | |
; nasm -f elf64 ped_base64_2.asm -l out.lst && ld ped_base64_2.o -o base64en | |
SECTION .data ; Section containing initialised data | |
Base64Table: db "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | |
SECTION .bss ; Section containing uninitialized data | |
ChunkSize: equ 19 ; size in 3-byte work units (57 input bytes) | |
ReadBufferSize: equ ChunkSize * 3 | |
OutputBufferSize: equ ChunkSize * 4 | |
OutputBufer: resb OutputBufferSize + 16 | |
; +16 bytes is padding (mem-access and '='/EOL overwrite zone) | |
ReadBuffer: equ OutputBufer + ChunkSize + 4 | |
; read buffer is shared with output -> advanced just enough to let output | |
; overwrite only source data which were already processed | |
SECTION .text ; Section containing code | |
global _start ; Linker needs this to find the entry point! | |
_start: | |
; load some common values into preserved registers (these are "global") | |
xor ebp,ebp ; rbp = 0 | |
mov r12,ReadBuffer | |
mov r13,OutputBufer | |
.processChunkLoop: | |
call readInput | |
test rax,rax | |
jle .err_or_exit ; error or zero length -> exit | |
call processChunk ; leaves size of output in rdi | |
call writeOutput | |
jmp .processChunkLoop ; loop until full input was processed | |
.err_or_exit: | |
mov rdi,rax ; error code = 0 when OK, otherwise err_no of sys_read() | |
mov rax,60 ; sys_exit | |
syscall | |
readInput: ; out rax = length or error (<0) | |
xor eax,eax ; rax = 0 = sys_read | |
xor edi,edi ; rdi = 0 = stdin | |
mov rsi,r12 ; rsi = read buffer for data | |
mov edx,ReadBufferSize ; rdx = bytes to read (32b value) | |
syscall | |
ret | |
writeOutput: ; in rdi = length of output data to print | |
mov rdx,rdi ; output size | |
mov rsi,r13 ; OutputBufer | |
lea rax,[rbp+1] ; rax = 1 (sys_write) | |
mov rdi,rax ; rdi = 1 (stdout) | |
syscall | |
ret | |
processChunk: ; rax = size of data to process (non zero value) | |
mov rsi,r12 ; rsi = ReadBuffer | |
mov rdi,r13 ; rdi = OutputBufer | |
lea r8,[rsi+rax] ; r8 = first byte after bytes read (address) | |
mov [r8],ebp ; make sure input data are padded with 4+ zeroes | |
; convert input data into base64 encoding | |
.loop: | |
mov eax,[rsi] | |
add rsi,3 | |
bswap eax | |
shr eax,8 ; 24 bits (4x6) of input in big-endian order | |
; => b23..b18 is first 6-bits value to output, b5..b0 is last one | |
mov edx,eax ; copy eax into edx | |
shr eax,6 ; remove 6 from eax because they will already be processed | |
and edx,0x3F ; keep only last 6 bits | |
mov bh,[Base64Table+rdx]; translate into base64 (fourth output char) | |
mov edx,eax ; get copy of next 6 bits | |
shr eax,6 ; remove 6 processed bits | |
and edx,0x3F ; keep only last 6 bits | |
mov bl,[Base64Table+rdx]; translate into base64 (third output char) | |
shl ebx,16 ; shift fourth+third char into upper 16 bits of ebx | |
mov edx,eax ; get next 6 bits | |
shr eax,6 ; remove 6 processed bits | |
and edx,0x3F ; keep only last 6 bits | |
mov bh,[Base64Table+rdx]; convert bits into base64 (second output char) | |
mov bl,[Base64Table+rax]; convert bits into base64 (first output char) | |
mov [rdi],ebx ; write four output bytes | |
add rdi,4 | |
cmp rsi,r8 | |
jb .loop ; loop until all input data were processed (rsi >= r8) | |
; patch the output chunk to end with '=' or '==' if (0 != (size%3)) | |
sub r8,rsi ; r8 = 0, -1 (one char extra in output) or -2 (two chars extra) | |
mov word [rdi+r8],'==' ; overwrite any extra chars in output with '=' | |
; add newline after output | |
mov byte [rdi],10 | |
inc rdi | |
sub rdi,r13 ; rdi = size of output data | |
ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment