Boot sector printing the wrong thing when I %include my print function - string

I am having an issue with some assembly code. I am trying to print out a string using a function from a different assembly file. But it doesn't output the string but instead an "S ". How do I fix this? I would like to add that I use the NASM assembler.
code:
string.asm
print_string:
pusha
mov ah, 0x0e
loop:
mov al, [bx]
cmp al, 0
je return
int 0x10
inc bx
jmp loop
return:
popa
ret
boot_sector.asm -
[org 0x7c00]
%include "string.asm"
mov bx, [my_string]
call print_string
my_string:
db 'hello world', 0
times 510 - ($ - $$) db 0
dw 0xaa55

Execution of a boot sector begins with the first byte. In this case, the first instruction is the top of your function, because you put it first.
The code assembles exactly the same as if you had included it manually before assembling. So your boot sector is really:
[org 0x7c00]
print_string:
pusha
...
ret
mov bx, [my_string] ; BX = load first 2 bytes of my_string.
; should have been
; mov bx, my_string ; BX = address of my_string. mov bx, imm16
call print_string
It should be pretty obvious why that doesn't work, and you would have noticed this if you single-stepped your code with the debugger built-in to BOCHS (or any other way of debugging a boot sector). Even just looking at disassembly might have clued you in.
Solution: put the %include after your other code, and avoid having execution fall into it. e.g. put this after the call:
cli ; disable interrupts
hlt ; halt until the next interrupt. (except for NMI)
(If NMI is possible, you can put the hlt inside an infinite loop with jmp.)
This is not your only bug. As #MichaelPetch points out, you were loading 2 bytes from the string instead of putting its address into BX.

Related

NASM ASSEMBLY - Print "Hello World"

I've created a string and turned it into an array. Looping through each index and moving to the al register so it can print out to the vga. The problem is, it prints the size of the string with no problem, but the characters in gibberish. Can you please help me figure out what the problem is in the code. It will be highly appreciated.
org 0
bits 16
section .text
global _start
_start:
mov si, msg
loop:
inc si
mov ah, 0x0e
mov al, [si]
or al, al
jz end
mov bh, 0x00
int 0x10
jmp loop
end:
jmp .done
.done:
jmp $
msg db 'Hello, world!',0xa
len equ $ - msg
TIMES 510 - ($ - $$) db 0
DW 0xAA55
bootloader code
ORG 0x7c00
BITS 16
boot:
mov ah, 0x02
mov al, 0x01
mov ch, 0x00
mov cl, 0x02
mov dh, 0x00
mov dl, 0x00
mov bx, 0x1000
mov es, bx
int 0x13
jmp 0x1000:0x00
times 510 - ($ - $$) db 0
dw 0xAA55
The bootloader
Before tackling the kernel code, let's look at the bootloader that brings the kernel in memory.
You have written a very minimalistic version of a bootloader, one that omits much of the usual stuff like setting up segment registers, but thanks to its reduced nature that's not really a problem.
What could be a problem is that you wrote mov dl, 0x00, hardcoding a zero to select the first floppy as your bootdisk. No problem if this is indeed the case, but it would be much better to just use whatever value the BIOS preloaded the DL register with. That's the ID for the disk that holds your bootloader and kernel.
What is a problem is that you load the kernel to the segmented address 0x1000:0x1000 and then later jump to the segmented address 0x1000:0x0000 which is 4096 bytes short of the kernel. You got lucky that the kernel code did run in the end, thanks to the memory between these two addresses most probably being filled with zero-bytes that (two by two) translate into the instruction add [bx+si], al. Because you omitted setting up the DS segment register, we don't know what unlucky byte got overwritten so many times. Let's hope it was not an important byte...
mov bx, 0x1000
mov es, bx
xor bx, bx <== You forgot to write this instruction!
int 0x13
jmp 0x1000:0x0000
What is a problem is that you ignore the possibility of encountering troubles when loading a sector from the disk. At the very least you should inspect the carry flag that the BIOS.ReadSector function 02h reports and if the flag is set you could abort cleanly. A more sophisticated approach would also retry a limited number of times, say 3 times.
ORG 0x7C00
BITS 16
; IN (dl)
mov dh, 0x00 ; DL is bootdrive
mov cx, 0x0002
mov bx, 0x1000
mov es, bx
xor bx, bx
mov ax, 0x0201 ; BIOS.ReadSector
int 0x13 ; -> AH CF
jc ERR
jmp 0x1000:0x0000
ERR:
cli
hlt
jmp ERR
times 510 - ($ - $$) db 0
dw 0xAA55
The kernel
After the jmp 0x1000:0x0000 instruction has brought you to the first instruction of your kernel, the CS code segment register holds the value 0x1000. None of the other segment registers did change, and since you did not setup any of them in the bootloader, we still don't know what any of them contain. However in order to retrieve the bytes from the message at msg with the mov al, [si] instruction, we need a correct value for the DS data segment register. In accordance with the ORG 0 directive, the correct value is the one we already have in CS. Just two 1-byte instructions are needed: push cs pop ds.
There's more to be said about the kernel code:
The printing loop uses a pre-increment on the pointer in the SI register. Because of this the first character of the string will not get displayed. You could compensate for this via mov si, msg - 1.
The printing loop processes a zero-terminating string. You don't need to prepare that len equate. What you do need is an explicit zero byte that terminates the string. You should not rely on that large number of zero bytes thattimes produced. In some future version of the code there might be no zero byte at all!
You (think you) have included a newline (0xa) in the string. For the BIOS.Teletype function 0Eh, this is merely a linefeed that moves down on the screen. To obtain a newline, you need to include both carriage return (13) and linefeed (10).
There's no reason for your kernel code to have the bootsector signature bytes at offset 510. Depending on how you get this code to the disk, it might be necessary to pad the code up to (a multiple of) 512, so keep times 512 - ($ - $$) db 0.
The kernel:
ORG 0
BITS 16
section .text
global _start
_start:
push cs
pop ds
mov si, msg
mov bx, 0x0007 ; DisplayPage=0, GraphicsColor=7 (White)
jmp BeginLoop
PrintLoop:
mov ah, 0x0E ; BIOS.Teletype
int 0x10
BeginLoop:
mov al, [si]
inc si
test al, al
jnz PrintLoop
cli
hlt
jmp $-2
msg db 'Hello, world!', 13, 10, 0
TIMES 512 - ($ - $$) db 0

x86 Assembly String Buffer Number to ASCII

I was writing an x86 assembly program to output a number in hexadecimal. The program was assembled using nasm and the image file ran by qemu. The behavior of the program confused me a lot. As the working program below suggests, I wouldn't have to add 0x30 to a digit to get it to print the character of that digit.
; Boot sector code offset: 0x7c00
[org 0x7c00]
mov dx, 0x1fb6 ; The hexadecimal to be printed
call print_hex ; call the function
jmp $ ; jump infinitely
%include "print_string.asm" ; Include the print_string function
print_hex:
pusha ; push all registers to stack
mov ax, 0x4 ; rotate through the number four times
print_hex_loop:
cmp ax, 0x0 ; compare the counter with 0
jle print_hex_end ; if it is zero then jump to the end
mov cx, dx ; move dx to cx
and cx, 0x000F ; take the lower four binary digits of cx
cmp cx, 0xa ;compare the digits with 0xa
jge print_hex_letter ; if it is larger than a, jump to printing character
add cx, 0x0 ; otherwise print the ascii of a number
jmp print_hex_modify_string ; jump to routine for modifing the template
print_hex_letter:
add cx, 0x7 ; print the ascii of a letter
print_hex_modify_string:
mov bx, HEX_OUT ; bring the address of HEX_OUT into dx
add bx, 0x1 ; skip the 0x
add bx, ax ; add the bias
add byte [bx], cl ; move the character into its position
shr dx, 4 ; shift right 4 bits
sub ax, 0x1 ; subtract 1 from the counter
jmp print_hex_loop ; jump back to the start of the function
print_hex_end:
mov bx, HEX_OUT ; move the address of HEX_OUT to bx
call print_string ; call the function print_string
popa ; pop all registers from stack
ret ; return to calling function
HEX_OUT:
db '0x0000',0 ; The template string for printing
times 510-($-$$) db 0 ; fill zeros
dw 0xaa55 ; MAGIC_FLAG for boot
boot_sect.asm
print_string:
pusha
mov ah, 0x0e
mov al, [bx]
print_string_loop:
cmp al, 0x0
je print_string_end
int 0x10
add bx, 0x1
mov al, [bx]
jmp print_string_loop
print_string_end:
popa
ret
print_string.asm
The output of this program is what I expected, but when I tried to add 0x30 on the numerals to get the ASCII code of the digits, the output was gibberish. Is there some trick to it or am I missing some key points here?
Thanks!
The answer to your original question:
Because you do add byte [bx], cl to write digit into buffer, and the buffer already contains '0', so the first time it will work correctly. Calling print_hex second time will produce gibberish again, as the HEX_OUT content is already modified (trivia: which hex number printed as first would allow also some second value to be printed correctly?).
Now just for fun I'm adding how I would probably do print_hex for myself. Maybe it will give you additional ideas for your x86 ASM programming, I tried to comment it a lot to explain why I'm doing things in a way I'm doing them:
First I would separate formatting function, so I could eventually reuse it elsewhere, so input is both number and target buffer pointer. I'm using LUT (look up table) for ASCII conversion, as the code is simpler. If you care about size, it's possible to do it in code with branching in less bytes and use the slower pusha/popa to save registers.
format_hex:
; dx = number, di = 4B output buffer for "%04X" format of number.
push bx ; used as temporary to calculate digits ASCII
push si ; used as pointer to buffer for writing chars
push dx
lea si,[di+4] ; buffer.end() pointer
format_hex_loop:
mov bx,dx ; bx = temporary to extract single digit
dec si ; si = where to write next digit
and bx,0x000F ; separate last digit (needs whole bx for LUT indexing)
shr dx,4 ; shift original number one hex-digit (4 bits) to right
mov bl,[format_hex_ascii_lut+bx] ; convert digit 0-15 value to ASCII
mov [si],bl ; write it into buffer
cmp di,si ; compare buffer.begin() with pointer-to-write
jb format_hex_loop ; loop till first digit was written
pop dx ; restore original values of all modified regs
pop si
pop bx
ret
format_hex_ascii_lut: ; LUT for 0-15 to ASCII conversion
db '0123456789ABCDEF'
Then for convenience a print_hex function may be added too, providing its own buffer for formatting with "0x" and nul terminator:
print_hex:
; dx = number to print
push di
push bx
; format the number
mov di,HEX_OUT+2
call format_hex
; print the result to screen
lea bx,[di-2] ; bx = HEX_OUT
; HEX_OUT was already set with "0x" and nul-terminator, otherwise I would do:
; mov word [bx],'0x'
; mov byte [bx+6],0
call print_string
pop bx
pop di
ret
HEX_OUT:
db '0x1234',0 ; The template string for printing
And finally example usage from the boot code:
mov dx,0x1fb6 ; The hexadecimal to be printed
call print_hex
mov dx,ax ; works also when called second time
call print_hex ; (but would be nicer to print some space between them)
jmp $ ; loop infinitely
(I did verify this code to some extend (that it will compile and run), although only by separate parts of it and in 32b environment (patching few lines to make it 32b), so some bug may have slipped in. I don't have 16b environment to verify it as complete boot code.)

How can i copy an array in nasm x86 assembly for Linux, porting 16-bit DOS code?

I have to write a program which copy an array in other array, using x86 assembler
The original code is written in MSDOS' TASM for 8086 processor, but I want port this to Linux NASM using i386 processor
The code in TASM is this:
.MODEL SMALL
.DATA
TABLE_A DB 10, 5, 1
TABLE_B DB 0, 0, 0
.CODE
MOV AX, SEG TABLE_B
MOV DS, AX
MOV SI, 0
LOOP:
MOV AL, TABLE_A[SI]
MOV TABLE_B[SI], AL
INC SI
CMP SI, 2
JBE LOOP
MOV AH, 4Ch
INT 21h
END
I'm trying to rewrite this in nasm, but I don't get to sit in the correct array position, similar to TABLE_A[SI] instruction
How can I do it?
The final code in nasm is this
section .text
global _start
cpu 386
_start:
MOV ESI, TABLE_A
MOV EDI, TABLE_B
MOV CX, 3
COPY_LOOP:
MOV AL, [ESI]
MOV [EDI], AL
INC SI
INC DI
LOOP COPY_LOOP
MOV AX,1
INT 80h
section .data
TABLE_A DB 10, 5, 1
TABLE_B DB 0, 0, 0
How could I do it?
(question from comments on self-answer)
Well, first you read Instruction reference guide to understand what the instruction does, and then you can use it, if it fits your purpose. This is the important step, keep re-reading instruction details every so often, to verify it does modify registers and flags in a way you expect it. Especially if in debugger you see the CPU state of change you didn't expect.
As you are in linux, the ds/es segment registers are very likely already set to reasonably values (covering .data section), so after setting eSi to Source address, eDi to Destination address, and eCx to Count, you write instead of COPY_LOOP: just rep movsb ... and then exit trough int 80h (eax=1). (notice the emphasized letters in register names, Intel picked those intentionally to make it easy to recall)
BTW, just now I noticed, you wrote in your code sort of bugs:
inc si/di should be inc esi/edi, because you use esi/edi to address. If you would be copying array over 64k memory boundary, inc si would wrap around on it.
set ecx to 3, in 32b mode the loop instruction does use whole 32b ecx, not 16b part cx only. If the code ahead of copy would use some large number in ecx setting some of upper 16 bits, your loop would copy many more bytes than only 3.
ahead of calling int 80h again you must set whole eax with the function number, otherwise you risk to have some garbage in upper 16 bits of eax from previous code, requesting invalid function.
So after applying these your code may look like this:
section .text
global _start
cpu 386
_start:
MOV ESI, TABLE_A
MOV EDI, TABLE_B
MOV ECX, 3
REP MOVSB ; copy ECX bytes from DS:ESI to ES:EDI
MOV EAX,1 ; call sys_exit, again FIXED to EAX!
INT 80h
section .data
TABLE_A DB 10, 5, 1
TABLE_B DB 0, 0, 0
If you did read the docs about registers, you should already understand what is difference between eax and ax. In Linux you are in 32b mode (when you link the binary as 32b elf, nowadays the 64b may be default on 64b system, which differs a bit from 32b mode), so by default use the 32b register variants. Unless you really want the 16b/8b variant for particular reason, and you make sure the code doesn't work later with 32b register while you set only less of it (like loop, rep movsb and int 80h do).
Also it makes the code usually faster, as using 16b ax in 32b mode requires additional opcode byte ahead of instruction, for example mov eax,ebx is 2 bytes opcode 89 D8, mov ax,bx is 3 bytes opcode 66 89 D8.
In response to marc
I tried this form, without successful result:
MOV SI, 0
MOV AX, 0
LOOP:
MOV AX, [TABLE_A + SI]
MOV [TABLE_B + SI], AX
INC SI
CMP SI, 2
JBE LOOP
Use pointers (SI, DI) to the arrays and CX as counter :
MOV SI, Table_A ;POINTER TO TABLE_A.
MOV DI, Table_B ;POINTER TO TABLE_B.
MOV CX, 3 ;ARRAY LENGTH.
REPEAT:
MOV AL, [SI]
MOV [DI], AL
INC SI
INC DI
LOOP REPEAT ;CX-1. IF CX>0 JUMP TO REPEAT.

I'm getting a segmentation fault in my assembly program [duplicate]

The tutorial I am following is for x86 and was written using 32-bit assembly, I'm trying to follow along while learning x64 assembly in the process. This has been going very well up until this lesson where I have the following simple program which simply tries to modify a single character in a string; it compiles fine but segfaults when ran.
section .text
global _start ; Declare global entry oint for ld
_start:
jmp short message ; Jump to where or message is at so we can do a call to push the address onto the stack
code:
xor rax, rax ; Clean up the registers
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
; Try to change the N to a space
pop rsi ; Get address from stack
mov al, 0x20 ; Load 0x20 into RAX
mov [rsi], al; Why segfault?
xor rax, rax; Clear again
; write(rdi, rsi, rdx) = write(file_descriptor, buffer, length)
mov al, 0x01 ; write the command for 64bit Syscall Write (0x01) into the lower 8 bits of RAX
mov rdi, rax ; First Paramter, RDI = 0x01 which is STDOUT, we move rax to ensure the upper 56 bits of RDI are zero
;pop rsi ; Second Parameter, RSI = Popped address of message from stack
mov dl, 25 ; Third Parameter, RDX = Length of message
syscall ; Call Write
; exit(rdi) = exit(return value)
xor rax, rax ; write returns # of bytes written in rax, need to clean it up again
add rax, 0x3C ; 64bit syscall exit is 0x3C
xor rdi, rdi ; Return value is in rdi (First parameter), zero it to return 0
syscall ; Call Exit
message:
call code ; Pushes the address of the string onto the stack
db 'AAAABBBNAAAAAAAABBBBBBBB',0x0A
This culprit is this line:
mov [rsi], al; Why segfault?
If I comment it out, then the program runs fine, outputting the message 'AAAABBBNAAAAAAAABBBBBBBB', why can't I modify the string?
The authors code is the following:
global _start
_start:
jmp short ender
starter:
pop ebx ;get the address of the string
xor eax, eax
mov al, 0x20
mov [ebx+7], al ;put a NULL where the N is in the string
mov al, 4 ;syscall write
mov bl, 1 ;stdout is 1
pop ecx ;get the address of the string from the stack
mov dl, 25 ;length of the string
int 0x80
xor eax, eax
mov al, 1 ;exit the shellcode
xor ebx,ebx
int 0x80
ender:
call starter
db 'AAAABBBNAAAAAAAABBBBBBBB'0x0A
And I've compiled that using:
nasm -f elf <infile> -o <outfile>
ld -m elf_i386 <infile> -o <outfile>
But even that causes a segfault, images on the page show it working properly and changing the N into a space, however I seem to be stuck in segfault land :( Google isn't really being helpful in this case, and so I turn to you stackoverflow, any pointers (no pun intended!) would be appreciated
I would assume it's because you're trying to access data that is in the .text section. Usually you're not allowed to write to code segment for security. Modifiable data should be in the .data section. (Or .bss if zero-initialized.)
For actual shellcode, where you don't want to use a separate section, see Segfault when writing to string allocated by db [assembly] for alternate workarounds.
Also I would never suggest using the side effects of call pushing the address after it to the stack to get a pointer to data following it, except for shellcode.
This is a common trick in shellcode (which must be position-independent); 32-bit mode needs a call to get EIP somehow. The call must have a backwards displacement to avoid 00 bytes in the machine code, so putting the call somewhere that creates a "return" address you specifically want saves an add or lea.
Even in 64-bit code where RIP-relative addressing is possible, jmp / call / pop is about as compact as jumping over the string for a RIP-relative LEA with a negative displacement.
Outside of the shellcode / constrained-machine-code use case, it's a terrible idea and you should just lea reg, [rel buf] like a normal person with the data in .data and the code in .text. (Or read-only data in .rodata.) This way you're not trying execute code next to data, or put data next to code.
(Code-injection vulnerabilities that allow shellcode already imply the existence of a page with write and exec permission, but normal processes from modern toolchains don't have any W+X pages unless you do something to make that happen. W^X is a good security feature for this reason, so normal toolchain security features / defaults must be defeated to test shellcode.)

Printing a string in assembly using no predefined function

I have to define a function in assembly that will allow me to loop through a string of declared bytes and print them using a BIOS interrupt. I'm in 16 bit real mode. This is an exercise on writing a little bootloader from a textbook, but it seems that it was only a draft and it's missing some stuff.
I have been given the following code:
org 0x7c00
mov bx, HELLO_MSG
call print_string
mov bx, GOODBYE_MSG
call print_string
jmp $ ;hang so we can see the message
%include "print_string.asm"
HELLO_MSG:
db 'Hello, World!', 0
GOODBYE_MSG:
db 'Goodbye!', 0
times 510 - ($ - $$) db 0
dw 0xaa55
My print_string.asm looks like this:
print_string:
pusha
mov ah, 0x0e
loop:
mov al, bl
cmp al, 0
je return
int 0x10
inc bx
jmp loop
return:
popa
ret
I have some idea of what I'm doing, but the book doesn't explain how to iterate through something. I know how to do it in C but this is my first time using assembly for something other than debugging C code. What happens when I boot through the emulator is that it prints out a couple of lines of gibberish and eventually hangs there for me to see my failure in all it's glory. Hahaha.
Well, it looks like it loads the address of the string into the BX register before calling the function.
The actual function looks like it is trying to loop through the string, using BX as a pointer and incrementing it (inc bx) until it hits the ASCII NUL at the end of the string (cmp al, 0; je return)...
...but something is wrong. The "mov al, bl" instruction does not look correct, because that would move the low 8 bits of the address into al to be compared for an ASCII NUL, which does not make a lot of sense. I think it should be something more like "mov al, [bx]"; i.e. move the byte referenced by the BX address into the AL register -- although it has been a long time since I've worked with assembly so I might not have the syntax correct.
Because of that bug, the 10h interrupt would also be printing random characters based on the address of the string rather than the contents of the string. That would explain the gibberish you're seeing.
I think the issue is you cannot count on the int preserving any of your registers, so you need to protect them. Plus, what Steven pointed out regarding loading of your string address:
; Print the string whose address is in `bx`, segment `ds`
; String is zero terminated
;
print_string:
pusha
loop:
mov al, [bx] ; load what `bx` points to
cmp al, 0
je return
push bx ; save bx
mov ah, 0x0e ; load this every time through the loop
; you don't know if `int` preserves it
int 0x10
pop bx ; restore bx
inc bx
jmp loop
return:
popa
ret

Resources