Jump to content

Check out our Community Blogs

Register and join over 40,000 other developers!

Recent Status Updates

View All Updates

- - - - -

Assembly, File I/O and 'incbin' (Win32, NASM)

hello world assembly

  • Please log in to reply
No replies to this topic

#1 RhetoricalRuvim


    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1307 posts
  • Location:C:\Countries\US
  • Programming Language:C, Java, C++, PHP, Python, JavaScript

Posted 18 August 2011 - 06:08 PM

In programming, there are two kinds of data definition methods: compile-time and run-time. Compile-time is when the compiler sets things up to already be defined when the program starts. Run-time is when your program sets the values as it runs.

Here, we will look at different ways of defining and setting memory fields.

  • incbin - A NASM pseudo-instruction that includes everything from a file in the current location of the PE.
  • File I/O - We'll go over the Win32 API functions for accessing files.

incbin is a pseudo-instruction, unlike %include, which is a preprocessor directive.

When you use %include, NASM includes all the code from the file specified.

incbin, instead of including all the code, includes all the contents from the file specified.

It's kind of like using 'db <all the contents from the file>' .

incbin - Example Program - The Code
This is the source code:
;; Define the externs. 
extern MessageBoxA 
extern ExitProcess 

;; Construct the symbol import table. 
import MessageBoxA user32.dll 
import ExitProcess kernel32.dll 

;; The code section; use 32-bit code. 
section .text use32 
;; ..start is where the program starts running. 

;; Display a message box with title the_title and message msg. 
push dword 0 
push dword the_title 
push dword msg 
push dword 0 
call [MessageBoxA] 

;; Exit, returning 0. 
push dword 0 
call [ExitProcess] 

;; The data section. 
section .data 
;; Define the title. 
the_title                                db "Just A Test", 0 
;; Tell NASM that this is where msg is located. 
;; Then use binary include to include everything from file01.txt 
incbin "file01.txt" 
db 0 

;; The bss section. 
section .bss 

And this should go into file01.txt :
Hello, how are you? 

Nice day, isn't it?

incbin - Example Program - The Output
You should get a message box with everything that's in file01.txt ;

Did we read from a file? No. NASM did the reading from file01.txt when it was assembling our program. So it's the same as:
db "Hello, how are you? ", 13, 10, 13, 10, "Nice day, isn't it? "
We just simply defined the data area at msg to whatever was inside file01.txt

So how do we read from a file?

ReadFile - Reading From A File
ReadFile() is the Win32 API function for reading from a file.

So let's look at the function parameters:
hFile - The handle to the file to write to.
lpBuffer - The pointer to the memory buffer where to save the read data.
nNumberOfBytesToRead - The number of bytes to read from the file.
lpNumberOfBytesRead - The pointer to an integer that receives the number of bytes actually read.
lpOverlapped - A pointer to an OVERLAPPED structure. This parameter is optional, depending on how the file was opened; we won't care about this parameter right now.

What? No filename? And what is this file handle?

Let's take a look at how to get a file handle.

OpenFile - Opening A File
The OpenFile() Win32 API function opens a file.

The parameters:
lpFileName - The pointer to a string that contains the name of the file to open.
lpReOpenBuff - The pointer to an OFSTRUCT structure.
uStyle - The action to take.

The values I use most often, for the third parameter, are:
OF_CREATE (0x1000) - Make a new file; if the file exists, truncate it to 0 bytes in size.
OF_READ (0x0000) - Open the file for reading.
OF_WRITE (0x0001) - Open the file for writing.
OF_READWRITE (0x0002) - Open the file for reading and writing.

For more information, visit OpenFile Function (Windows).

CreateFile - Another Method For Opening A File
CreateFile() is another Win32 API function for opening file (or other I/O device) handles.

The parameters:
  • The pointer to a string with the filename.
  • The desired file access rights constant (/s).
  • The share mode flags for the file being opened or made.
  • An optional pointer to a SECURITY_ATTRIBUTES structure.
  • Creation disposition. An action to take when a file exists or does not exist.
  • Flags and attributes. Specifies whether the file should be archive, read-only, hidden, etc.
  • A valid handle to a template file that allows the GENERIC_READ access right. That file's attributes and extended attributes are used for the file being opened, if the file being opened is a new file.

Access Rights
The most common access rights are GENERIC_READ, for reading, or GENERIC_WRITE, for writing.
GENERIC_ALL (0x10000000) - Read, write, and execute access.
GENERIC_EXECUTE (0x20000000) - Execute access.
GENERIC_WRITE (0x40000000) - Write access.
GENERIC_READ (0x80000000) - Read access.

Share Mode Flags
FILE_SHARE_READ (0x01) - Other processes can read this file.
FILE_SHARE_WRITE (0x02) - Other processes can write to this file.
FILE_SHARE_DELETE (0x04) - Other processes can delete this file.
0 (0x00) - Other processes can't access this file.

Creation Disposition
CREATE_NEW (1) - Make a new file; fail if exists.
CREATE_ALWAYS (2) - Make a new file; if it exists, and if it is accessible, overwrite the file.
OPEN_EXISTING (3) - Open a file; fail, if not exists.
OPEN_ALWAYS (4) - Open a file; make a new file, if not exists.
TRUNCATE_EXISTING (5) - Open a file and truncate it, so that its size is 0 bytes; fail if not exists.

Flags and Attributes
FILE_ATTRIBUTE_NORMAL (0x80) - Normal attributes.
FILE_ATTRIBUTE_HIDDEN (0x02) - The file is hidden.

For more information about the CreateFile() function or its parameters, visit CreateFile Function (Windows).

When you're done using a file, always close it using the CloseHandle() Win32 API function.

CloseHandle - Closing A File
When you're done using the file, you can close the file using this function. This function is very simple and takes only one parameter; how hard is that?

- The handle to the file to close.

WriteFile - Writing To A File
The parameters of the WriteFile() Win32 API function are pretty much identical to the parameters of the ReadFile() Win32 API function; the only difference, as for the programmer's side, is that ReadFile() reads bytes from a file to memory, and WriteFile() writes bytes to a file from memory.

File Pointers
Windows also keeps track of where, in the file, the handle's pointer points to. A file pointer is kind of like a regular pointer that we use for referencing memory, but instead of pointing to a memory address, it has the offset into the file.

When you use ReadFile() or WriteFile(), you read or write starting at the current file pointer. The current file pointer is then appropriately modified, after the read or write operation.

For example, let's say the file pointer is at 20. You read 4 bytes. The file pointer is now at 24. Then you write 5 bytes. The file pointer is, at this point, at 29. And so on.

When you first open a file, the file pointer is normally at 0. So how do you change the file pointer without using read or write operations?

SetFilePointer - Setting The File Pointer
To set the file pointer for a file handle, you use the SetFilePointer Win32 API function.

The parameters:
  • The handle of which to set the pointer.
  • The distance to move the file pointer.
  • Optional (can be NULL); the pointer to an integer (4 bytes) that has the high-order double-word of the distance to move the file pointer. If not NULL, the integer pointed to by this parameter also receives the high-order double-word of the new file pointer.
  • The move method; where to start moving the file pointer from.

If the function succeeds, the return value is the low-order double-word of the new file pointer.

Move Method
FILE_BEGIN (0) - Start moving from the beginning of the file.
FILE_CURRENT (1) - Start moving from the current file pointer position.
FILE_END (2) - Start moving from the end of the file.

For more reference to this function, visit SetFilePointer Function (Windows).

There is also a Win32 API function for getting the size of a file.

GetFileSize - Getting The Size Of A File
The GetFileSize() Win32 API function gets the size of a file. Since files can be larger than 4 GB in size, we might need a 64-bit value to know the file size.

The parameters:
  • A handle to the file to get the size of.
  • An optional (can be NULL) pointer to an integer that would receive the high-order double-word of the file size.

The return value is the low-order double-word of the file size.

For more reference on this function, visit GetFileSize Function (Windows).

So what if we wanted to truncate a file?

SetEndOfFile - Truncating The Rest Of The File
This Win32 API function sets the end-of-file mark at the current file pointer offset. That means if the file pointer was 15 then the file is now 15 bytes in size.

The parameters:
  • The handle to the file.

Now that you know about Win32 API File I/O functions and NASM's syntax, we can proceed to writing the code for our next program.

Example Program - The Code
;; Define the externs. 
extern MessageBoxA 
extern ExitProcess 
extern OpenFile 
extern ReadFile 
extern WriteFile 
extern CloseHandle 
extern GetFileSize 

;; Construct our symbol import table. 
import MessageBoxA user32.dll 
import ExitProcess kernel32.dll 
import OpenFile kernel32.dll 
import ReadFile kernel32.dll 
import WriteFile kernel32.dll 
import CloseHandle kernel32.dll 
import GetFileSize kernel32.dll 

;; This goes into the code section; use 32-bit code. 
section .text use32 
;; This is where the program should start running. 

;; Call the main() function. 
call main 

;; Exit, returning whatever main() returned. 
push eax 
call [ExitProcess] 

	;; 12 bytes for local variable space and 
	;; 136 bytes for the OFSTRUCT structure; 
	;; that, together, would make 148 bytes. 
	enter 148, 0 
	;; Display a message box with msg as the message. 
	push dword 0 
	push dword the_title 
	push dword msg 
	push dword 0 
	call [MessageBoxA] 
	;; Open a file for writing or for creating. 
	lea ebx, [ebp-148]                ;; The address of the OFSTRUCT structure. 
	push dword 1 | 0x1000             ;; OF_WRITE | OF_CREATE 
	push ebx 
	push dword filename1 
	call [OpenFile] 
	mov dword [ebp-4], eax            ;; Save the file handle. 
	;; Write the message to the file. 
	lea ebx, [ebp-8]                  ;; The number of bytes written will be saved there. 
	push dword 0 
	push ebx 
	push dword msg_stop - msg         ;; Size of the message. 
	push dword msg                    ;; The address of the message. 
	push dword [ebp-4]                ;; The handle to use for writing to file. 
	call [WriteFile] 
	;; At this point, if we wanted to, we could check whether the number of 
	;; bytes actually written ([ebp-8]) equals the number of bytes intended 
	;; to write. If not equal, then some error must have occurred. 
	;; Close the file handle, because we're done using the file. 
	push dword [ebp-4]                ;; The file handle. 
	call [CloseHandle] 
	;; Open another file, for reading-only this time. 
	lea ebx, [ebp-148]                ;; The OFSTRUCT structure. 
	push dword 0                      ;; OF_READ 
	push ebx                          ;; The pointer to our OFSTRUCT structure. 
	push dword filename2              ;; The name of the file to open. 
	call [OpenFile] 
	mov dword [ebp-4], eax            ;; And we save the file handle. 
	cmp eax, -1                       ;; Check to see if the return is HFILE_ERROR (-1). 
	jz .e1                            ;; If equal - meaning OpenFile() failed - go to e1. 
	;; Get the size of the file. 
	push dword 0 
	push dword [ebp-4] 
	call [GetFileSize] 
	mov dword [ebp-8], eax            ;; Save the size of the file. 
	;; Check to make sure that the size of the file doesn't exceed the size of our buffer. 
	cmp eax, 1024 
	jng .over1 
		;; If the file size exceeds our buffer size, we'll either need to fail, or 
		;; we'll need to only read part of the file. But if you do fail in cases 
		;; like this, don't forget to close the file before exiting. 
		mov dword [ebp-8], 1024       ;; We'll change that value to the size of our buffer. 
	;; We read the text from the file to our buffer. 
	lea ebx, [ebp-12]                 ;; Where the number of bytes actually read would be saved. 
	push dword 0 
	push ebx 
	push dword [ebp-8] 
	push dword buffer 
	push dword [ebp-4] 
	call [ReadFile] 
	;; We're done using the file, for the day, so we'll close the file handle. 
	push dword [ebp-4] 
	call [CloseHandle] 
	;; We'll NULL-terminate the data that we just read, just in case. 
	mov eax, dword [ebp-12]           ;; The number of bytes actually read. 
	add eax, buffer                   ;; + the address of the buffer. 
	mov ebx, eax                      ;; = the byte right after the buffer. 
	mov byte [ebx], 0 
	;; We don't actually have to; the data section should be 0-initialized, 
	;; so that when we read data to the data section, there should be NULL 
	;; right after the last byte of the data we just read. 
	;; Now we display a message box with the newly-read data. 
	push dword 0 
	push dword the_title 
	push dword buffer 
	push dword 0 
	call [MessageBoxA] 
	;; We return 0. 
	xor eax, eax 
	;; We still need to take care of error 1. 
	;; Display a message box with the error1 error message. 
	push dword 0 
	push dword 0           ;; If you leave this parameter blank, a default title 
	                       ;; of "Error" will be used. 
	push dword msg_error1 
	push dword 0 
	call [MessageBoxA] 
	;; And we don't really have any open file handles or anything, so 
	;; we can exit now. 
	;; We'll return 1, to say that there was an error. 
	mov eax, 1 

section .data 
the_title                                    db "File Incbin Program", 0 
incbin "msg.txt" 
msg_stop                                     db 0 

filename1                                    db "FileIncbin.txt", 0 
filename2                                    db "msg2.txt", 0 

msg_error1                                   db "Could not open file msg2.txt", 0 

section .bss 
buffer                                       resb 1024 
padding_byte                                 resb 1 

Here's msg.txt:
Hello World! 


Here's msg2.txt:
Oh, hello again. 

So, did you write more programs? 
Well, anyway, gotta go now.

Example Program - The Output
You should get two message boxes for the output. The first one should contain the text from msg.txt and the second one should contain the text from msg2.txt; there should also appear another file in the folder the .exe is in; the file should have the same text as the first message box.

The text in the first message box is set by NASM when it assembles the program. Our program then prepares the text for and displays the second message box.

Here's what the output might look like:

First Tutorial:
Intro To Win32 Assembly, Using NASM

Previous Tutorial:
Application of Mouse and Keyboard Input

Next Tutorial:
Using Memory Allocation and Alphabet Algorithm

OpenFile: OpenFile Function (Windows)
CreateFile: CreateFile Function (Windows)
SetFilePointer: SetFilePointer Function (Windows)
GetFileSize: GetFileSize Function (Windows)

Attached Thumbnails

  • FileIncbin_output_cc.PNG

Edited by RhetoricalRuvim, 20 August 2011 - 07:00 PM.

  • 0

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