Jump to content




Recent Status Updates

  • Photo
      15 Nov
    duzamucha

    Hi, I am final year Interior Design Student from University of Huddersfield. I am currently working on my final major project which is going to be linked to coding. I was hoping that you could help me with my research. I have prepared a short survey, it would be a massive help if you could fill it in for me. It takes less than 2 minutes to complete, I promise. Here is the link: https://www.surveymonkey.com/s/73XLJKK Thank you so much in advance!

View All Updates

Developed by TechBiz Xccelerator
Photo
- - - - -

Intro To Win32 Assembly, Using NASM, Part 3

hello world assembly

  • Please log in to reply
9 replies to this topic

#1 RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,304 posts
  • Location:C:\Countries\US

Posted 13 August 2011 - 03:05 PM

Hello World - The Plan
We want to make a hello world program that would do the following:
  • Allocate a console.
  • Set the console title to "HelloWorldProgram" .
  • Get and save the standard output handle.
  • Calculate the length of the "Hello World! \r\n" message.
  • Print the "Hello World! \r\n" message.
  • Set the console cursor position to (0, 15).
  • Wait 2000 milliseconds (2 seconds).
  • Free the console allocated earlier.
  • Exit, returning 0.

Hello World - The Code
Okay, it's time to write the hello world program. The comments within the code do a lot of the explanation of the code.

So here's the code:
;; When symbols are not defined within our program, we need to use 'extern', to tell NASM that those will be assigned when the program is linked. 
;; These are the symbols for the Win32 API import functions we will use. 
extern GetStdHandle 
extern WriteFile 
extern AllocConsole 
extern FreeConsole 
extern SetConsoleTitleA 
extern SetConsoleCursorPosition 
extern Sleep 
extern ExitProcess 

;; Now, we need a symbol import table, so that we can import Win32 API functions from their DLLs. 
;; Note, though, that some functions have ANSI and unicode versions; for those, a name suffix is 
;; required (ie "<function_name>A" for ANSI, and "<function_name>W" for unicode; SetConsoleTitleA 
;; is an example of one). 
import GetStdHandle kernel32.dll 
import WriteFile kernel32.dll 
import AllocConsole kernel32.dll 
import FreeConsole kernel32.dll 
import SetConsoleTitleA kernel32.dll 
import SetConsoleCursorPosition kernel32.dll 
import Sleep kernel32.dll 
import ExitProcess kernel32.dll 

;; Here, we tell NASM to put the following stuff into the code section of the program. 
;; The 'use32' tells NASM to use 32-bit code, and not 16-bit code. 
section .text use32 
;; The '..start:' special symbol tells NASM (and, later on, the linker) that this is 
;; where the program entry point is. This is where the instruction pointer will point 
;; to, when the program starts running. 
..start: 

;; Since this is a Windows subsystem program, we need to allocate a console, 
;; in order to use one. 
;; Note how we use 'AllocConsole' as if it was a variable. 'AllocConsole', to 
;; NASM, means the address of the AllocConsole "variable" ; but since the 
;; pointer to the AllocConsole() Win32 API function is stored in that 
;; variable, we need to call the address from that variable. 
;; So it's "call the code at the address: whatever's at the address AllocConsole" . 
call [AllocConsole] 

;; Here, we push the address of 'the_title' to the stack. 
push dword the_title 
;; And we call the SetConsoleTitleA() Win32 API function. 
call [SetConsoleTitleA] 

;; We push -11 (yes, that's legal, it basically means 0 - 11, in two's complement), 
;; which is the Windows constant for STD_OUTPUT_HANDLE, to the stack. 
push dword -11 
;; Then we call the GetStdHandle. 
call [GetStdHandle] 
;; The Win32 API functions return the result in the EAX register. 
;; Therefore, to save the return, we need to save the value in EAX. 
;; Here, we move the value from EAX to [hStdOut] ("to the memory location 
;; at the address hStdOut"). 
mov dword [hStdOut], eax 

;; We move the address of msg_len to EAX. 
mov eax, msg_len 
;; Then we subtract the address of msg from EAX, to get the 
;; size of the msg variable, since msg_len comes right after msg. 
sub eax, msg 
;; Since there's a trailing 0, the actual text is really 1 byte less. 
;; So we decrement (or subtract 1 from) EAX. 
dec eax 
;; Then we save that result in the msg_len variable. 
mov dword [msg_len], eax 

;; WriteFile() has 5 parameters. 
;; When we call a function in assembly language, we push the parameters 
;; to the stack in backwards order, so that it's easier for the function 
;; we're calling to access these parameters, because for that function 
;; the parameters will actually be in the correct order, because of 
;; the way the Intel procedure stack works. 

;; The fifth parameter is usually 0. 
push dword 0 
;; The fourth parameter is the address of the variable where we want 
;; the actual number of bytes written (or read, for ReadFile()) saved. 
push dword nBytes 
;; The third parameter is the number of bytes to write (or read, for ReadFile()). 
push dword [msg_len] 
;; The second parameter is the pointer to the buffer where 
;; the text to write (or read, for ReadFile()), is located. 
push dword msg 
;; The first parameter is the handle to the file we want to write to 
;; (or read from, for the ReadFile() function). 
push dword [hStdOut] 
;; Then we call the Win32 API WriteFile() function. 
call [WriteFile] 

;; It's time to set the console cursor position. 
;; We want to set the high-order part of EAX to 
;; the new Y coordinate of the console cursor 
;; and the low-order part of EAX to the new 
;; X coordinate of the console cursor. 

;; Set the low-order part of EAX to 15. 
mov ax, 15 
;; Shift the bits in EAX left, so that 
;; the high-order part of EAX is 15, now. 
shl eax, 16      ;; EAX is 32 bits in size, so it would make sense to 
                 ;; shift things by 16 bits. 
                 ;; Note that AX is not 15 anymore, because the 15 
                 ;; has been shifted. AX should be 0, at this point. 
;; Set the low-order part of EAX to 0. 
mov ax, 0        ;; I know it's kind of silly to set AX to 0 if it 
                 ;; should already be 0, but we do that anyway. 
;; The second parameter to the SetConsoleCursorPosition() function 
;; is a COORD structure (that we just made) for the new 
;; position of the console cursor. 
push eax 
;; The first parameter is the standard output handle of the console 
;; of which we want to set the cursor. 
push dword [hStdOut] 
;; Then we call the Win32 API SetConsoleCursorPosition() function. 
;; It's the same thing as pushing the EIP and jumping to the 
;; function, but we can't directly access EIP, so we have to 
;; use the CALL instruction. 
call [SetConsoleCursorPosition] 

;; Sleep() is a Win32 API function that suspends the execution of 
;; the current code for a number of milliseconds that we specify. 
;; So we specify 2000 milliseconds (2 seconds). 
push dword 2000 
;; And we call the Sleep() function. 
call [Sleep] 

;; When we're done using the console, we need to free it, 
;; if we were the ones who allocated it. 
;; Same applies for other resources, such as file handles 
;; and memory pointers; like if we open a file, we need to 
;; close the handle after we're done using the file. 
call [FreeConsole] 

;; XOR reg, reg is a way to clear reg, so that it's 0. 
xor eax, eax 
;; We pass EAX (which is 0) to ExitProcess(). 
push eax 
;; Then we call the ExitProcess() Win32 API function. 
call [ExitProcess] 

;; Now we tell NASM that this next stuff is supposed to go 
;; into the data section. 
section .data 
;; We define the_title, and initialize it to "HelloWorldProgram" 
the_title                  db "HelloWorldProgram", 0 
;; Now we define msg, and initialize that to "Hello World! \r\n" 
msg                        db "Hello World! ", 13, 10, 0 
                                 ;; Note that 13 means "\r" and 10 means "\n" 
                                 ;; 13 is the ASCII code for carriage return (CR) 
                                 ;; and 10 is the ASCII code for line feed (LF) 
                                 ;; CRLF is the character combination used for 
                                 ;; new lines (or at least under DOS/Windows). 
;; Since msg_len has to come right after msg, 
;; in order for us to get correct results in 
;; the above code, we have to define it right 
;; after msg and in the data section. 
;; We can initialize it to whatever we want, 
;; since it will be changed, later on, anyway. 
;; I decided to initialize it to 0. 
msg_len                    dd 0 

;; Here we tell NASM that the following is for the bss section. 
section .bss 
;; We reserve 1 double-word for hStdOut. 
hStdOut                    resd 1 
;; And we reserve 1 double-word for nBytes. 
nBytes                     resd 1 

Assembling And Linking - The Command-Line
Now time to open a command-prompt window.

Opening Command Prompt
If you're using Notepad++, open the .asm file with it, go to the 'Run' menu, and choose 'Open current dir cmd' .

Else, open command prompt and change to the directory where you have the .asm file saved.

If you don't want to do that, open notepad (the one that comes with Windows), or a similar text editor, and type:
cmd
Then save that file into the same directory (folder) that contains the .asm file. For the filename, type something like "cmd_dir.bat" (preferably with the quotes), and make sure you save it as a text document/file (if you're using an editor such as WordPad). Then go to that folder and open the cmd_dir.bat that you just saved.

Building The Project
You should now have a command prompt window open to the directory with the .asm file.

(In the command prompt window:)

To assemble a file, you type: "\nasm\nasm -fobj the_file.asm" (without the quotes, replace the_file.asm with the filename of the .asm file).

To link a file or files, type: "\alink\alink -oPE the_files" (without the quotes, replace the_files with a space-separated list of files to link for the current project).
If you want to specify an output filename for linking, use: "\alink\alink -oPE the_files -o the_file.exe" , instead, and instead of the_file.exe use the filename you want ALINK to save the PE as.

If you installed NASM or ALINK to a different directory, let's say to "F:\files", then add the directory name right before the command:
"F:\files\alink\alink -oPE ..."

For our hello world example, I saved the .asm file as "hello.asm" . Then I used these commands to assemble and link the program:
\nasm\nasm -fobj hello.asm
\alink\alink -oPE hello.obj

And "hello.obj" with "hello.exe" appeared in the folder where "hello.asm" was saved.

The Output
Here's somewhat what the output should look like:
hello_snap_cc.jpg

Well, it's the end of the tutorial. Hopefully you learned at least something.
I'm planning on making another tutorial sometime soon, so you can watch out for that.


Here's the link to my next tutorial, if you want to go on to that:
Local Variables and Functions



Tools:
NASM - Netwide Assembler
ALINK (Linker)
Notepad++ (Source Code Text Editor)

References:
PE/COFF Specification
NASM Manual
Some Common Instructions
Intel Architecture Software Developer's Manual volume 1
Intel Architecture Software Developer's Manual volume 2
Intel Architecture Software Developer's Manual volume 3







First Tutorial:
Part 1

Previous Tutorial:
Part 2

Next Tutorial:
Local Variables and Functions

Edited by RhetoricalRuvim, 03 February 2012 - 11:55 AM.

  • 0

#2 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4,625 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 20 August 2011 - 08:39 PM

Dude...this should've been part 1 :)
  • 0

sudo rm -rf / && echo $'Sanitize your inputs!'


#3 RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,304 posts
  • Location:C:\Countries\US

Posted 20 August 2011 - 09:21 PM

What do you mean?
  • 0

#4 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4,625 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 20 August 2011 - 09:22 PM

Hello World on the console, instead of that monster with BSS and idata sections with DLLs...
  • 0

sudo rm -rf / && echo $'Sanitize your inputs!'


#5 RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,304 posts
  • Location:C:\Countries\US

Posted 20 August 2011 - 09:29 PM

It's just I decided that I should probably explain everything first, so that the reader would understand what's going on in the hello world program.
  • 0

#6 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4,625 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 20 August 2011 - 09:31 PM

You explain as you go along, using helloworld as an application of the concepts. It's kinda like explaining Newtonian mechanics to a kid before you tell them why apples fall from trees.
  • 0

sudo rm -rf / && echo $'Sanitize your inputs!'


#7 Alexander

Alexander

    YOL9

  • Moderator
  • 3,946 posts
  • Location:Vancouver, Eh! Cleverness: 200
  • Programming Language:C, C++, PHP, Assembly

Posted 28 August 2011 - 02:25 PM

You explain as you go along, using helloworld as an application of the concepts. It's kinda like explaining Newtonian mechanics to a kid before you tell them why apples fall from trees.


I prefer to learn it like RR's method, looking at Hello World to me is like skipping what all of it is, only explaining to you, when you could maybe had explained to yourself what the hello world did to some extend in the background (and apply that to further applications)
  • 0

All new problems require investigation, and so if errors are problems, try to learn as much as you can and report back.


#8 RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,304 posts
  • Location:C:\Countries\US

Posted 28 August 2011 - 03:24 PM

Thanks.
  • 0

#9 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4,625 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 28 August 2011 - 03:25 PM

Well, to each his own. :)
  • 0

sudo rm -rf / && echo $'Sanitize your inputs!'


#10 Frank Kotler

Frank Kotler

    CC Lurker

  • Just Joined
  • Pip
  • 1 posts

Posted 04 February 2012 - 12:04 AM

"Where to start?" is a good question with any tutorial. I think I like this method. You tell us what we're going to do, introduce the toolset we're going to use to do it, then you walk us through doing it. Nice job!

If I may make a couple of observations...
;; Now we define msg, and initialize that to "Hello World! \r\n" 
msg                        db "Hello World! ", 13, 10, 0 
                                 ;; Note that 13 means "\r" and 10 means "\n" 
                                 ;; 13 is the ASCII code for carriage return (CR) 
                                 ;; and 10 is the ASCII code for line feed (LF) 
                                 ;; CRLF is the character combination used for 
                                 ;; new lines (or at least under DOS/Windows). 
;; Since msg_len has to come right after msg, 
;; in order for us to get correct results in 
;; the above code, we have to define it right 
;; after msg and in the data section. 
;; We can initialize it to whatever we want, 
;; since it will be changed, later on, anyway. 
;; I decided to initialize it to 0. 
msg_len                    dd 0 

In a fairly recent feature, Nasm understands "\r\n" and the like, if you enclose it in "back quotes". You could have defined the string as:
msg                        db `Hello World!\r\n`, 0 
as an alternative, if you wanted to. Also, if you wanted to introduce Nasm's use of the '$' character, in this context it means approximately "here" - the current point in the assembly. So you could initialize "msg_len" as a variable like:
msg_len dd $ - msg - 1
instead of doing it at runtime. Or as a constant (no '[]'s when you push this!) like:
msg_len equ $ - msg - 1

Just different ways of doing things. Programmer's choice! Carry on with the good work!

Best,
Frank
  • 0





Also tagged with one or more of these keywords: hello world, assembly

Powered by binpress