Loops 如何编写明文和密钥长度不同的加密循环

Loops 如何编写明文和密钥长度不同的加密循环,loops,assembly,encryption,x86,irvine32,Loops,Assembly,Encryption,X86,Irvine32,请帮助我检查代码,我无法获得预期的输出 输入:提示用户插入纯文本,并键入 输出:使用vigenère密码计算并输出加密文本 我可以输入纯文本和密钥,但无法获得密文输出 INCLUDE IRVINE32.INC .DATA BUFMAX = 128 sPrompt1 BYTE "Enter the plain text:", 0 sPrompt2 BYTE "Key:",0 sEncrypt BYTE "Cip

请帮助我检查代码,我无法获得预期的输出

  • 输入:提示用户插入纯文本,并键入
  • 输出:使用vigenère密码计算并输出加密文本
我可以输入纯文本和密钥,但无法获得密文输出

INCLUDE IRVINE32.INC

.DATA
    BUFMAX = 128
    sPrompt1 BYTE "Enter the plain text:", 0
    sPrompt2 BYTE "Key:",0
    sEncrypt BYTE "Cipher text:",0
    buffer BYTE BUFMAX+1 DUP(?)
    key BYTE BUFMAX+1 DUP (?)
    bufsize DWORD ?
    keysize DWORD ?

.CODE

main PROC
    call Clrscr
    call inputString            ;input plain text, key
    call translateBuffer        ;encrypt the buffer
    
    call displayMessage         ;display encrypted message

    exit
    main ENDP
输入字符串

inputString PROC
;--------------------------------------------------------------------------------
;Prompts user to input string and key, saves the string and it's length
;recieves: nothing
;returns: nothing
;--------------------------------------------------------------------------------
    pushad
    mov edx, OFFSET sPrompt1    ;prompt plain text
    call WriteString
    mov ecx, BUFMAX
    mov edx, OFFSET buffer
    call ReadString
    mov bufsize, eax
    call Crlf

    mov edx, OFFSET sPrompt2    ;prompt key
    call WriteString
    mov ecx, BUFMAX
    mov edx, OFFSET key
    call ReadString
    mov keysize, eax
    call Crlf

    popad
    ret
    inputString ENDP
翻译缓冲

translateBuffer PROC
;--------------------------------------------------------------------------------
;translate the plain text with key to cipher text
;recieves: nothing
;returns: nothing
;--------------------------------------------------------------------------------
    pushad
    mov ecx, bufsize
    mov esi, 0
    mov edi, 0
    
L1: mov al, key[edi]
    xor buffer[esi], al
    inc esi
    inc edi
    cmp edi, keysize+1
    jne L1
    mov edi, 0
    loop L1

    popad
    ret
    translateBuffer ENDP
显示加密消息

displayMessage PROC 
;--------------------------------------------------------------------------------
; Displays the encrypted message. 
; Receives: EDX points to the message 
; Returns:  nothing 
;--------------------------------------------------------------------------------
    pushad 
    mov edx, OFFSET sEncrypt
    call WriteString 

    mov edx,OFFSET buffer 
    call WriteString 
    
    call Crlf 
    call Crlf
    popad 
    ret
    displayMessage  ENDP

END main

您的输入和输出过程似乎很好。在你的加密过程中,麻烦就在这两者之间

用户可以为纯文本和加密密钥输入不同长度的字符串。你的程序需要考虑这些不同的长度,以免溢出任何缓冲区,因为这正是你的代码中发生的事情。p> 问题1 由于
jne L1
指令走错了方向,绕过了正常的循环计数,因此循环运行的时间太长,并开始覆盖不再属于已定义缓冲区的内存。
循环只能遍历纯文本字符串。如果加密密钥中的文本恰好比纯文本字符串短,则需要重复密钥。因此,您必须将偏移寄存器
EDI
重置为0,但必须正常继续循环(而不仅仅是返回顶部!)

问题2 第二个问题是,您有这个
cmp edi,keysize+1
指令,由于
+1
,它将使用键字符串中的零终止符,就像它是该字符串的一部分一样。这绝对不是字符串终止符背后的想法。这不是绳子的一部分

    push  esi                   ; No need to preserve the scratch registers EAX, ECX, EDX 
    mov   esi, OFFSET buffer
    xor   edx, edx
    mov   ecx, bufsize
L1: movzx eax, byte ptr key[edx]
    xor   [esi], al
    inc   edx
    inc   esi
    cmp   edx, keysize
    je    L3                    ; Branches in the least common case
L2: dec   ecx
    jnz   L1
    pop   esi
    ret
L3: xor   edx, edx
    jmp   L2
在上面的代码片段中,我对代码做了一些改进

  • 只需将寄存器与自身进行异或运算,即可将寄存器归零
  • 千万不要使用
    循环
    指令,因为它非常慢
  • 还有一些更多的优化,你可以在这个答案下面的PeterCordes评论中读到

这是一个版本,它甚至使用
CMOVE
(如果相等,则条件移动)指令消除了条件分支,如果相等条件恰好为真,则该指令将(归零的)
EDX
寄存器存储在
EDI
寄存器上。如果没有,就根本没有动静

    xor   edi, edi
    xor   esi, esi
    xor   edx, edx
    mov   ecx, bufsize
L1: mov   al, buffer[esi]
    xor   al, key[edi]
    mov   buffer[esi], al
    inc   esi
    inc   edi
    cmp   edi, keysize
    cmove edi, edx
    dec   ecx
    jnz   L1
异或的危险
由于加密仅使用一个
XOR
操作,并且取决于两个字符串的组成,因此有可能在加密字符串的字节中获得一个零字节。显然,像写字符串一样依赖于字符串的零终止的打印函数将把第一个零作为字符串的结尾。如果这种情况发生在加密字符串的第一个位置,那么就没有任何东西可以打印…

Sry,朋友们。这是我的第一篇文章,我不太了解规则。我已经插入了预期的输出、输入以及我遇到的错误。当你运行它时会发生什么。它打印什么吗?这是一个重要的组成部分。仅仅说“它不工作”是没有帮助的,尤其是当有这么多代码的时候。见和。asm问题也有一个坏习惯,即发布整个程序,而不是只发布不起作用的部分(使用固定的输入),因此它也不是很小。@PeterCordes我认为这个问题现在已经足够清楚了。我想回答这个问题,并投票决定重新开放。@SepRoland:好的,既然你问了。我会一直关闭它,直到遵循标准指南,但至少每个函数都有注释。特别是因为调试问题通常对有类似问题的人来说没有什么未来的价值,而且在寻找重复的问题时会把事情弄得一团糟。在输出中,密码文本没有显示出来输入纯文本:abcde Key:ab Cipher text:``问题是:您输入的纯文本以小写字母“a”开头,同时加密密钥也以小写字母“a”开头。两者的<代码> XOR 产生一个零字节,写字符串将考虑一个空字符串,所以没有什么可以显示的!试试这个:使用完全不同的输入。不要忘记,如果任何
XOR
操作的结果产生一个<32字节,WriteString也可能不会打印该字节!对于branchy版本,您通常希望不采用常见情况,将指针重置设置为不符合要求,以便不采用常见情况以提高前端效率。(有条件地跳转到块并向后跳转)。另外,使用
movzx-eax,byte-ptr-buffer[esi]
在上一次迭代的eax上执行
mov al,mem
在Sandybridge(和非Intel)之后合并CPU上的一个字节,解码为微融合加载+ALU合并。字节异或和存储仍然正常;xor已经读取了它的目的地。哦,哈哈,再看看这个。如果您关心性能,您也不会使用
pushad
/
popad
。看起来EDX未使用,所以您只需保存/还原一个保留调用的寄存器即可获得足够的数据。实际上,OoO exec将隐藏内容。但一般来说,将
incedi
放在
cmp
的前面可能会稍有帮助。很可能所有3条指令都会在同一个时钟周期内发送到后端,但也有旧的顺序原子。哦,
keysize
不是汇编时间常数吗?通常,您希望将其加载到寄存器中,但我认为
cmp reg,[mem]
/
je
仍然可以将微观和宏观融合到前端的单个比较和分支+加载uop中。我猜,在SnB系列上,您将有足够的加载端口吞吐量,每2个时钟执行3次加载+1次存储。