“Hello World”function不使用C printf

更新

这是我与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 

我想把这个东西包装在一个汇编函数中。 networking上的所有(或大部分)例子都是使用externC调用printf函数(见下面的代码) – 我不想这样做。 我想学会在程序集中创build一个“Hello World”函数,而不使用C printf (甚至是其他外部函数调用)。

 global _main extern _printf section .text _main: push message call _printf add esp, 4 ret section .data message: db "Hello, World", 10, 0 

更新

我正在练习用于Linux的汇编,但是由于我没有Linux机器,我在这里运行汇编代码compile_assembly_online 。

Solutions Collecting From Web of "“Hello World”function不使用C printf"

假设你的意思是在Windows命令提示环境下写入标准输出:

由于那些提供了旧的DOS环境的虚拟化版本,我相信你可以使用旧的DOS中断:

int 21,函数9可以输出一个字符串:设置AH为9,DS:DX为以$结尾的字符串,并触发中断。

int 21,函数2可以输出单个字符,所以如果你需要输出$ (或者你不想Ctrl + C和这样的检查),你可以重复使用它。 AH到2,DL到ASCII(我预期的)字符码,并触发中断。

int 0x80不会在Windows或DOS工作,只是因为它是一个Linux的东西。 所以这是第一个需要改变的地方。

就Windows而言,在某些时候,你需要调用一个Windows API函数,比如(在本例中) WriteConsole() 。 这是根据需要绕过C库。

它使用操作系统来完成输出到“屏幕”的繁重工作,但是这与int 0x80是一样的,无论是Linux,Windows还是DOS,都可能需要它。

如果它真正的DOS,那么最好的开始是Ralf Brown的中断列表 ,特别是Int21 / Fn9 。

我想指出的是,纳斯姆“知道”某些部分的名称 – “.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 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 80hint 21h vs WriteFile 。 你可能需要再问一次

编辑:好的,一个子程序。 这个非显而易见的部分是这个call把返回地址(在调用之后的指令的地址)放在堆栈上,并且ret得到地址以返回堆栈,所以我们不想改变在哪里ss:sp指向中间。 我们可以改变它,但是我们需要把它放回原来的位置。

 ; 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提供一个例子(相同的概念)。 我建议小步走