linux - NASM Linux x64 |将二进制编码为base64

标签 linux assembly 64-bit nasm x86-64

我正在尝试将二进制文件编码为 base64。 自始至终,我都停留在几个步骤上,我也不确定这是否是思考的方式,请参阅下面代码中的评论:

SECTION .bss            ; Section containing uninitialized data

    BUFFLEN equ 6       ; We read the file 6 bytes at a time
    Buff:   resb BUFFLEN    ; Text buffer itself

SECTION .data           ; Section containing initialised data

    B64Str: db "000000"
    B64LEN equ $-B64Str

    Base64: db "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

SECTION .text           ; Section containing code

global  _start          ; Linker needs this to find the entry point!

_start: 
    nop         ; This no-op keeps gdb happy...

; Read a buffer full of text from stdin:
Read:
    mov eax,3       ; Specify sys_read call
    mov ebx,0       ; Specify File Descriptor 0: Standard Input
    mov ecx,Buff        ; Pass offset of the buffer to read to
    mov edx,BUFFLEN     ; Pass number of bytes to read at one pass
    int 80h         ; Call sys_read to fill the buffer
    mov ebp,eax     ; Save # of bytes read from file for later
    cmp eax,0       ; If eax=0, sys_read reached EOF on stdin
    je Done         ; Jump If Equal (to 0, from compare)

; Set up the registers for the process buffer step:
    mov esi,Buff        ; Place address of file buffer into esi
    mov edi,B64Str      ; Place address of line string into edi
    xor ecx,ecx     ; Clear line string pointer to 0


;;;;;;
  GET 6 bits from input
;;;;;;


;;;;;;
  Convert to B64 char
;;;;;;

;;;;;;
  Print the char
;;;;;;

;;;;;;
  process to the next 6 bits
;;;;;;


; All done! Let's end this party:
Done:
    mov eax,1       ; Code for Exit Syscall
    mov ebx,0       ; Return a code of zero 
    int 80H         ; Make kernel call

所以,在文本中,它应该这样做:

1) 十六进制值:

7C AA 78

2) 二进制值:

0111 1100 1010 1010 0111 1000

3) 6 位组:

011111 001010 101001 111000

4) 转换为数字:

31 10 41 56

5) 每个数字都是字母、数字或符号:

31 = f
10 = K
41 = p
56 = 4

所以,最终输出是:fKp4

所以,我的问题是: 如何获取 6 位以及如何将这些位转换为 char ?

最佳答案

几年后编辑:

最近确实有人遇到了这个例子,在讨论它是如何工作的以及如何将它转换为适用于 64b Linux 的 x64 时,我将它变成了完整的例子,源代码在这里:https://gist.github.com/ped7g/c96a7eec86f9b090d0f33ba36af056c1


你有两种主要的方式来实现它,要么通过能够选择任何 6 位的通用循环,要么通过处理 24 位(3 字节)输入的固定代码(将产生恰好 4 个 base64 字符并以字节结束-边界,因此您可以从 +3 偏移量读取接下来的 24 位)。

假设您有 esi 指向源二进制数据,这些数据被足够多的零填充以进行超出输入缓冲区安全范围的大量内存访问(最坏情况下 +3 字节)。

edi 指向某个输出缓冲区(至少有 ((input_length+2)/3*4) 个字节,可能带有一些填充,因为 B64 需要结束序列)。

; convert 3 bytes of input into four B64 characters of output
mov   eax,[esi]  ; read 3 bytes of input
      ; (reads actually 4B, 1 will be ignored)
add   esi,3      ; advance pointer to next input chunk
bswap eax        ; first input byte as MSB of eax
shr   eax,8      ; throw away the 1 junk byte (LSB after bswap)
; produce 4 base64 characters backward (last group of 6b is converted first)
; (to make the logic of 6b group extraction simple: "shr eax,6 + and 0x3F)
mov   edx,eax    ; get copy of last 6 bits
shr   eax,6      ; throw away 6bits being processed already
and   edx,0x3F   ; keep only last 6 bits
mov   bh,[Base64+edx]  ; convert 0-63 value into B64 character (4th)
mov   edx,eax    ; get copy of next 6 bits
shr   eax,6      ; throw away 6bits being processed already
and   edx,0x3F   ; keep only last 6 bits
mov   bl,[Base64+edx]  ; convert 0-63 value into B64 character (3rd)
shl   ebx,16     ; make room in ebx for next character (4+3 in upper 32b)
mov   edx,eax    ; get copy of next 6 bits
shr   eax,6      ; throw away 6bits being processed already
and   edx,0x3F   ; keep only last 6 bits
mov   bh,[Base64+edx]  ; convert 0-63 value into B64 character (2nd)
; here eax contains exactly only 6 bits (zero extended to 32b)
mov   bl,[Base64+eax]  ; convert 0-63 value into B64 character (1st)
mov   [edi],ebx  ; store four B64 characters as output
add   edi,4      ; advance output pointer

在最后一组 3B 输入之后,您必须用适当数量的 '=' 覆盖最后一个输出以修复输出的假零。 IE。输入 1B(需要 8 位,2x B64 字符)=> 输出以 '==' 结束,2B 输入(需要 16b,3x B64 字符)=> 结束 '=', 3B 输入 => 使用完整的 24 位 => 有效的 4x B64 字符。

如果你不想将整个文件读入内存并在内存中产生整个输出缓冲区,你可以限制长度的输入/输出缓冲区,比如只有 900B 输入 -> 1200B 输出,并在 900B block 中处理输入.或者您可以使用 3B -> 4B 输入/输出缓冲区,然后完全删除指针前进(甚至 esi/edi 用法,并使用固定内存),因为您必须加载/存储在/然后分别为每次迭代输出。

免责声明:这段代码的编写是为了简单明了,而不是为了提高性能,因为您询问了如何提取 6 位以及如何将值转换为字符,所以我想最好还是使用基本的 x86 asm 指令。

如果不分析代码的瓶颈和试验其他变体,我什至不确定如何让它表现得更好。部分寄存器的使用(bh, bl vs ebx)肯定会很昂贵,所以很可能有更好的解决方案(或者甚至可能是一些针对更大输入 block 的 SIMD 优化版本)。

而且我没有调试该代码,只是写在这里作为答案,所以请谨慎行事并检查调试器如何/是否工作。

关于linux - NASM Linux x64 |将二进制编码为base64,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47755183/

相关文章:

c - 内存如何确定变量的数据类型?

visual-studio - 在 Windows 上构建 64 位 Z3 时出错

c++ - 如何以最高精度 (C++) 将 uint64_t 转换为介于 0 和 1 之间的 double / float ?

linux - 使用命令行参数Bash更改文件名

gcc - x86-64 汇编语言中的 ELF 共享对象

windows - 用于检查 Windows DLL 的 Linux 工具

c - 如何在 mac 上编写自定义内核?

compiler-construction - x64 上的调用约定

linux - 在 Debian 中,守护进程会在文件系统的哪个位置提供动态测量结果?

c++ - Linux _SC_PHYS_PAGES 在 Mac OS X 上不工作