已更新
这是我与NASM
合作的第二天。在彻底了解了这个
section .programFlow
global _start
_start:
mov edx,len
mov ecx,msg
mov ebx,0x1 ;select STDOUT stream
mov eax,0x4 ;select SYS_WRITE call
int 0x80 ;invoke SYS_WRITE
mov ebx,0x0 ;select EXIT_CODE_0
mov eax,0x1 ;select SYS_EXIT call
int 0x80 ;invoke SYS_EXIT
section .programData
msg: db "Hello World!",0xa
len: equ $ - msg
我想将这些东西包装在一个汇编函数中。网络上的所有(或大部分)示例都使用extern
并调用printf
的{{1}}函数(请参阅下面的代码) - 我不希望如此。我想学习在汇编中创建一个“Hello World”函数,而不使用C
C
(甚至其他外部函数调用)。
printf
更新
我正在为Linux练习程序集,但由于我没有Linux机器,因此我在这里运行汇编代码compile_assembly_online。
答案 0 :(得分:2)
int 0x80
无法在Windows或DOS中运行,因为它是Linux的东西。这就是第一个必须改变的事情。
就在Windows下执行此操作而言,在某些时候您将需要调用Windows API函数,例如(在本例中)WriteConsole()
。那是根据需要绕过C库。
它 使用操作系统来完成输出到“屏幕”的繁重工作,但这与int 0x80
相同,可能需要它是Linux,Windows还是DOS。
如果是正版DOS,那么您最好的起点就是优秀Ralf Brown's Interrupt List,特别是Int21/Fn9。
答案 1 :(得分:2)
假设您的意思是在Windows命令提示符环境中,写入标准输出:
由于那些提供了旧DOS环境的虚拟化版本,我相信你可以使用旧的DOS中断:
int 21, function 9可以输出一个字符串:将AH设置为9,将DS:DX设置为以$
结尾的字符串,然后触发中断。
int 21, function 2可以输出单个字符,因此如果您需要输出$
(或者您不想要Ctrl + C和此类检查),可以重复使用该字符。 AH到2,DL到ASCII(我期待)字符代码,并触发中断。
答案 2 :(得分:1)
我想指出,Nasm“知道”某些部分名称 - “。text”,“。data”和“.bss”(还有一些你还不需要的部分)。领先的'。'是必需的,名称区分大小写。正如您在第一个示例中所做的那样,使用其他名称可能“有效”但可能无法为您提供所需的“属性”。例如,节.programData is going to be read-only. Since you don't try to write to it this isn't going to do any harm... but
节.data`应该是可写的。
尝试学习Linux的asm而不能尝试它一定很困难。也许这个在线网站对你来说足够了。有一个名为“andlinux”的东西(我认为)可以让你在Windows中运行Linux程序。或者你可以在“虚拟机”中运行Linux。或者你可以在你的许多备用驱动器之一上创建一个分区并实际安装Linux。
对于DOS,有DosBox ......或者你可以在其中一个额外的分区上安装“真正的DOS”。从那里,你可以在B800h:xxxx写“直接到屏幕”。 (“字符”的一个字节,“颜色”的下一个字节)。如果你想在没有操作系统帮助的情况下这样做,那可能就是你想要的。在保护模式OS中,忘了它。他们受到美国的保护!
通常,您可能只想知道如何编写子程序。我们可以编写一个子程序,其中“msg”和“len”硬编码到其中 - 不是很灵活。或者我们可以编写一个带有两个参数的子程序 - 在寄存器或堆栈中。或者我们可以编写一个子程序,它需要一个以零结尾的字符串(printf,sys_write不会),并计算出edx
中的长度。如果这就是您需要帮助的地方,那么我们就会分心int 80h
vs int 21h
vs WriteFile
。你可能需要再问一次......
call
将返回地址(调用之后的指令地址)放在堆栈上,ret
获取地址返回堆栈,所以我们不想改变ss:sp
之间的位置。我们可以更改它,但我们需要将它放回到ret
之前的位置。
; purpose: to demonstrate a subroutine
; assemble with: nasm -f bin -o myfile.com myfile.asm
; (for DOS)
; Nasm defaults to 16-bit code in "-f bin" mode
; but it won't hurt to make it clear
bits 16
; this does not "cause" our code to be loaded
; at 100h (256 decimal), but informs Nasm that
; this is where DOS will load a .com file
; (needed to calculate the address of "msg", etc.)
org 100h
; we can put our data after the code
; or we can jump over it
; we do not want to execute it!
; this will cause Nasm to move it after the code
section .data
msg db "Hello, World!", 13, 10, "$"
msg2 db "Goodbye cruel world!", 13, 10, "$"
section .text
; execution starts here
mov dx, msg ; address/offset of msg
call myprint
; "ret" comes back here
; no point in a subroutine if we're only going to do it once
mov dx, msg2
call myprint
; when we get back, do something intelligent
exit:
mov ah. 4Ch ; DOS's exit subfunction
int 21h
; ---------------------------------------
; subroutines go here, after the exit
; we don't want to "fall through" into 'em!
myprint:
; expects: address of a $-terminated string in dx
; returns: nothing
push ax ; don't really need to do this
mov ah, 9 ; DOS's print subfunction
int 21h ; do it
pop ax ; restore caller's ax - and our stack!
ret
; end of subroutine
这是未经测试的,但受到错别字和愚蠢的逻辑错误的影响,它“应该”起作用。我们可以使它变得更复杂 - 将参数传递给堆栈而不是dx
。我们可以为Linux提供一个例子(同样的一般想法)。我建议小步走......