请参考IDA的反汇编结果或自行查阅相关资料,使用汇编语言编写一个简单的base64编码程序,能够将一个文件的内容编码成base64的形式并保存于该文件中(假定该文件内只有ASCII字符,不超过20字节)。
根据题目要求,编写了一个执行输入输出的base64编码程序,经过调试可以正常运行各种不同情况的base64编码转换。
1. 整体思路
base64编码算法是一种与ASCII编码算法不同的编码算法,ASCII编码转换为base64编码的方法如下图所示:
通过base64编码的转换步骤,我们得知转换过程实际上是将每3位ASCII 码对应的24位二进制编码分成四部分,再在前面补0后即得到4位的base64 编码。我们以此原理为核心,编写将输入的字符串转变为base64编码输出。
2. 完整代码
data segment
base64 db "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",'$'
encode db 31 dup(0);编码后最多28个编码字符+2个'='+1个'$'
ctrl db 13,10,'$'
scanf label byte;缓冲区
max db 22
act db ?
ascii db 22 dup(0)
data ends
code segment
main proc far
assume cs:code,ds:data
start:
;预处理
push ds
sub ax,ax
push ax
mov ax,data
mov ds,ax
;获取ASCII字符到缓冲区(不超过20个)
mov ah,0ah ;dos功能调用10号字符串输入:从键盘上输入一字符串到用户定义的输入缓冲区中,并送显示器显示
lea dx,scanf
int 21h
;计算循环几次(字符数除3向上取整)
sub ax,ax ;ax清零
mov al,act ;实际输入字符数
mov bl,3 ;除数
div bl ;ax除以操作数,商放在al中,余数放在ah中(需要提前将被除数放在ax中)
mov dh,ah ;备份保存
cmp ah,0 ;比较ah和0,若相同,则ZF=1
jz next ;如果ah=0(余数为0)则跳过下一步到next指令
inc al ;如果ah!=0(余数不为0)则将循环次数加一,末尾补0
next:
;循环预处理
mov ch,al ;计数器ch次,不使用cl是因为在移位时要使用cl存移位数
mov si,0 ;指向待处理的ASCII码,使用寄存器相对寻址,类似于高级语言数组访问,步长以字节为单位
mov di,0 ;指向编码后的base64码
;循环cl次,将3个ASCII码转化成4个base编码
loop1:
;获得第一个base码(第一个ASCII码的高6位)
mov al,ascii[si];将第一位ASCII码存入al,再存入ah备份
mov ah,al
inc si ;指向下一个ASCII码
mov cl,2 ;右移两位预处理
shr al,cl ;逻辑右移两位,得到第一个base64码al
mov bl,al ;将得到的第1个base码存入bl
mov bh,0 ;将bl存入为bx(使用bx的寄存器相对寻址方式)
mov dl,base64[bx];将对应的base64码字母存入dl
mov encode[di],dl;再存入内存,encode可看作解码的数组,di指向某位置
inc di ;指向下一个应存入的位置
;获得第二个base码(第一个ASCII码的低两位+第二个ASCII码的高四位)
mov cl,6 ;逻辑左移预处理
shl ah,cl ;将第一个ASCII码左移6位使最低2位到最高2位,低位清零
mov cl,2 ;逻辑右移预处理
shr ah,cl ;使高两位右移两位到第3、4位,高位低位都清零
mov al,ascii[si];将第二位ASCII码存入al
mov cl,4 ;逻辑右移预处理
shr al,cl ;将第2个ASCII码右移4位使最高4位到最低4位,高位清零
add al,ah ;将两个数相加,得到第二个base64码al
mov bl,al ;将得到的第2个base码存入bl
mov bh,0 ;将bl存入为bx
mov dl,base64[bx];将对应的base64码字母存入dl
mov encode[di],dl;再存入内存
inc di ;指向下一个应存入的位置
;获得第三个base码(第二个ASCII码的低四位+第三个ASCII码的高两位)
mov al,ascii[si];将第二位ASCII码存入al
inc si
mov cl,4
shl al,cl
mov cl,2
shr al,cl
mov ah,ascii[si];将第三位ASCII码存入ah
mov cl,6
shr ah,cl
add al,ah ;将两个数相加,得到第3个base64码al
mov bl,al ;将得到的第3个base码存入bl
mov bh,0 ;将bl存入为bx
mov dl,base64[bx];将对应的base64码字母存入dl
mov encode[di],dl;再存入内存
inc di ;指向下一个应存入的位置
;获得第四个base码(第三个ASCII码的低6位)
mov al,ascii[si];将第三位ASCII码存入al
inc si
mov cl,2
shl al,cl
mov cl,2
shr al,cl ;逻辑右移两位,得到第4个base64码al
mov bl,al ;将得到的第4个base码存入bl
mov bh,0 ;将bl存入为bx
mov dl,base64[bx];将对应的base64码字母存入dl
mov encode[di],dl;再存入内存
inc di ;指向下一个应存入的位置
;下一步判断
dec ch ;循环次数减一
cmp ch,0 ;判断循环次数是否为0
jnz loop1 ;不为0则继续循环
;结尾处理
sub di,2
cmp dh,1 ;如果余数不为1
jnz next2 ;就跳转下一步判断
mov al,'=' ;余数为1,先加一个‘=’
mov encode[di],al
next2:
inc di
cmp dh,0 ;如果余数为0
jz next3 ;就跳转结束
mov al,'='
mov encode[di],al ;余数为1或者2,再加一个‘=’
next3:
inc di
mov al,'$'
mov encode[di],al
;输出结果
lea dx,ctrl ;回车换行
mov ah,9
int 21h ;将当前数据区中以‘$’结尾的字符串送显示器显示
lea dx,encode
mov ah,9
int 21h
ret
main endp
code ends
end start
3. 代码释义
(1) 数据段(1-9行)
设立base64编码表、回车换行功能、存储输入的ASCII 码和输出的base64编码的区域以及dos10号调用的缓冲区等内容。
(2) 预处理与输入字符(10-27行)
代码段伪指令设立、汇编程序预处理以及通过dos10号调用从键盘获取输入的字符串。
(3) 循环预处理(28-45行)
-
计算循环次数
被除数为存入的字符串长度,除数根据base64的转换过程设定为3,得到商——循环次数及余数——尾处理的依据,将得到的结果分别存入al与ah寄存器中。 -
对于商的处理原则如下:
a. 若ah=0(余数为0),则循环次数恰好为商。
b. 若ah≠0(余数不为0),则将商加1,循环次数为商+1。 -
循环预处理(设立计数器ch记录循环次数,设立si与di用于遍历字符串内容进行读取和存入)。
(4) 主代码(46-109行)
将字符串按照base64转换规则转换。
- 处理的思想是通过各种操作将24个二进制位的ASCII码单元(每单元3个ASCII码)转换为4个base64编码存入encode[di]中
- 循环判断
每完成一次处理部分会让ch计数器内的循环次数自减,自减完成后判断循环次数是否为0。若循环次数不为0则跳回处理部分继续循环,若循环次数为0则代表字符串全部处理完成,并转入结尾处理部分。
(5) 结尾处理(110行-140行)
- 结尾base64码处理部分
由于如果字符个数不是3的整数倍的话,需要在后面补0。按照base64的原则全为0的字符需用=代替,但00000000的base64 编码为A,因此会导致出现bug。所以为修正尾部设计结尾base64码处理部分,保证输出正确的base64编码。 - 尾处理部分
在字符串的最后存入一个$表示字符串结束。完成后最后
通过dos9号调用先回车换行再输出转换后的base64编码,完成输出。
4. 运行结果
输入三种类型(余数为0、余数为1、余数为2)的字符串的运行结果,对比base64在线转换器的结果,可见三种字符串结果均相同,可以说明编写的汇编程序正确的完成了base64编码。
-
masm过程
-
link过程
-
余数为0的字符串(以Man为例,标准结果应为TWFu)
-
余数为1的字符串(以A为例,结果应为QQ==)
-
余数为2的字符串(以BC为例,结果应为QkM=)