Linux ROT13密码在汇编中

Linux ROT13密码在汇编中,linux,assembly,x86,nasm,rot13,Linux,Assembly,X86,Nasm,Rot13,所以我已经让我的ROT13密码做了我想做的事情,但是在最后,命令提示符显示在与最后一行输出相同的行上。这是我在汇编中的第一个项目,所以我很不确定我做错了什么 程序不会在输出的末尾打印换行符,因此退出时光标位于非空行的末尾。shell不知道这一点,并在那里打印下一个提示 echo foo包含一个尾随的换行符,因此当shell打印下一个提示时,光标已经位于新行的开头echo-n foo不包含尾随的换行符,因此它将光标保留在以foo开头的行的末尾,并且您的提示会附加在该行上,就像您的程序一样。将这些e

所以我已经让我的ROT13密码做了我想做的事情,但是在最后,命令提示符显示在与最后一行输出相同的行上。这是我在汇编中的第一个项目,所以我很不确定我做错了什么

程序不会在输出的末尾打印换行符,因此退出时光标位于非空行的末尾。shell不知道这一点,并在那里打印下一个提示

echo foo
包含一个尾随的换行符,因此当shell打印下一个提示时,光标已经位于新行的开头
echo-n foo
不包含尾随的换行符,因此它将光标保留在以
foo
开头的行的末尾,并且您的提示会附加在该行上,就像您的程序一样。将这些echo命令导入
hd
,以查看它们打印的ASCII字符的十六进制转储


因此,解决方案是确保输出以换行符结尾(ASCII代码=10)。您的
msg4:db 10“读取错误”,10
字符串中已经存在该错误。(它以一个换行符开头,也以一个换行符结尾。)在C语言中,您可以编写“\nRead error\n”,但NASM语法不是这样工作的。它确实支持反引号字符串中的C样式转义,但人们通常使用数字常量编写换行符


用户输入(从sys_read获得)通常应以换行结束,除非用户在一行中键入256个字符,或使用ctrl-D使read提前返回。(或者类似地,管道输入不以换行符结尾,因此读取命中EOF)

我开始遵循比较的逻辑,但很快就累了。我不确定输入中的换行符会发生什么情况,但我怀疑您的代码修改了缓冲区中的换行符。您可能应该避免这种情况,并保持不变。我想您应该将它们添加到您的比较和分支列表中,以便不修改字符

对于rot13程序来说,这可能比在缓冲区的末尾添加一个额外的换行符,或者额外调用sys_write一次来打印一个换行符更有用


您可以使用
strace
测试程序进行的系统调用。e、 g.
strace./a.out
将解码您进行的read()和write()系统调用

有关更多调试提示,请参见标记wiki的底部。(除此之外还有很多有用的东西)


顺便说一句,您可以使用SSE2(将al广播到xmm寄存器的每个元素,并使用常量/pmovskb/test/jnz)在xmm寄存器中并行执行所有这些
CMPAL'?“
比较。但在你掌握了标量代码之前,不要担心这个问题


避免CMP/JCC的rats嵌套的另一种方法是将字母字符列为白名单,默认情况下不修改输入字符。

我不知道你为什么只把
'1'
列为黑名单,而不把其他数字列为黑名单,或者
'+'
但不把
'-'
列为黑名单,等等


下面是我如何实现您的循环的,使用一些“高级”技巧将多个类似条件折叠为单个条件。有关isalpha()的无符号比较技巧的解释,请参见上的答案

最初我打算实际计算AL中的小写旋转字符,然后找出它和原始小写字符之间的差异,并将其应用于DL。但后来我意识到我可以在早期的分支中有条件地修改DL

    ;; after the or al,0x20:    mov   ah, al       ; don't over-do it with upper-half byte registers.  False dependencies on AMD, and partial-reg merging stalls or slowdowns on pre-Haswell Intel if you're not careful.

    add     al, 'a'                 ; 'a' + al is the lower-cased ROT13 of the input character
    sub     ah, al                  ; ah = lcase(orig) - lcase(rot13)
    sub     dl, ah                  ; apply that delta to the original in dl
    ; dl is the original character - 13 (plus 26 if necessary)

因此,请确保输出以换行结束。(ASCII码=10)。你的一根弦中已经有了。与单独的
write()
系统调用不同,可能只需在保存用户字符串的缓冲区末尾存储一个10。顺便说一句,这是一个很好的作业格式化和对asm进行注释(并使用符号名,因此您需要的注释更少)。这比初学者问题中的大量代码转储更具可读性。你不需要在开始时使用
nop
start
\u start
符号可以具有相同的地址。或者您可以键入
b\u start
在那里设置断点。(有趣的事实是:
ld-e
选项允许您将入口点设置为您想要的任何符号名称。但不要这样做,这可能只是混淆。此外,在精简二进制文件中,您可以使用
readelf
查找入口点的数字地址,以便在那里设置断点。(
b*0x04000…
)我不能把这归功于…我在这方面做的所有工作都是修改循环结构。不过,我不太确定如何实施你的建议。你用粗体字的评论正是发生的事情。另外,我真的很讨厌有这么多的CMP使用al寄存器。这是我第一次合法地进入组装领域,所以直到现在我才知道有更好的方法,但非常感谢!有了你的见解,我理解得更好了,我解决了最初的问题。@swingonaspiral:除了SSE技巧之外,当你编写一个
switch()
语句时,编译器还有其他的技巧,其中有很多情况都采取相同的操作。e、 退房。(请参阅以获取解释。)@swingonaspiral:但实际上,没有任何花哨的技巧,简化代码的方法就是后退一步,简化逻辑。它是ROT13,所以我们可能只想修改字母字符,而不修改其他字符。所以,与其把你想保留的所有东西都列入黑名单,不如把大写字母和小写字母的范围列入白名单。@swingonaspiral:我很好奇我能使循环有多高效。我添加了一个看起来不错的编辑。你的初学者尝试效率低下是很正常的,但我想你可能会有兴趣看看有哪些技巧是可行的。实际上,在我得到你的白名单/黑名单评论后几天,我就考虑了它的功能,并在来之前做了更改
    ;; after the or al,0x20:    mov   ah, al       ; don't over-do it with upper-half byte registers.  False dependencies on AMD, and partial-reg merging stalls or slowdowns on pre-Haswell Intel if you're not careful.

    add     al, 'a'                 ; 'a' + al is the lower-cased ROT13 of the input character
    sub     ah, al                  ; ah = lcase(orig) - lcase(rot13)
    sub     dl, ah                  ; apply that delta to the original in dl
    ; dl is the original character - 13 (plus 26 if necessary)