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

时间:2017-12-11 14:34:44

标签: 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中转换这些位?

1 个答案:

答案 0 :(得分:1)

你有两种主要的方法来实现它,或者通过能够选择任意6位的通用循环,或者通过固定代码处理24位(3字节)输入(将产生正好4个base64字符并以字节结束) -boundary,所以你可以从+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输入之后,您必须使用适当数量的'='覆盖最后一个输出以修复输出的假零。即输入1B(需要8位,2x B64字符)=>输出以'==',2B输入结束(需要16b,3x B64 char)=>结束'=',3B输入=>使用全24位=>有效的4x B64字符。

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

免责声明:这段代码是直截了当的,不是高效的,因为你问如何提取6位以及如何将值转换为字符,所以我想最好保留基本的x86 asm指令。

我甚至不确定如何在不分析代码瓶颈和尝试其他变体的情况下使其表现更好。当然,部分寄存器的使用(bh, bl vs ebx)成本很高,因此很可能是更好的解决方案(或者甚至可能是针对更大输入块的某些SIMD优化版本)。

我没有调试那些代码,只是在这里写回答,所以请谨慎行事,并检查调试器如何/如果有效。