Intentionally Vulnerable Test Program Not Behaving as Expected - c

I have been playing around with intentionally vulnerable c programs using strcpy, sprint, gets, etc. These all behaved as expected when running on linux but something strange is happening on my OS X machine. Here is the program I have wrote:
#include <stdio.h>
int main(int argc, char **argv) {
char buffer[64];
strcpy(buffer, argv[1]);
printf("buffer: %s\n", buffer);
return 0;
}
I ran it like this:
(gdb) run test
Starting program: /Users/****/test2 test
buffer: test
[Inferior 1 (process 5290) exited normally]
(gdb) run `python -c 'print "A"*64'`
Starting program: /Users/****/test2 `python -c 'print "A"*64'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5291) exited normally]
(gdb) run `python -c 'print "A"*70'`
Starting program: /Users/****/test2 `python -c 'print "A"*70'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5294) exited normally]
(gdb) run `python -c 'print "A"*80'`
Starting program: /Users/****/test2 `python -c 'print "A"*80'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5297) exited normally]
(gdb) run `python -c 'print "A"*100'`
Starting program: /Users/****/test2 `python -c 'print "A"*100'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGABRT, Aborted.
0x00007fff8eef3866 in ?? ()
(gdb)
First of all, I had expected 80 bytes to be enough to crash it. Second I expected to see 0x4141414141414141 instead of 0x00007fff8eef3866 due to the fact that I just attempted to overwrite some memory with a bunch of A's. Where is the other data coming from? Also, why did the program get SIGABRT? Why is there no seg fault?
Here is the assembly:
.section __TEXT,__text,regular,pure_instructions
.globl _main
.align 4, 0x90
_main: ## #main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
subq $112, %rsp
movq ___stack_chk_guard#GOTPCREL(%rip), %rax
movq (%rax), %rax
movq %rax, -8(%rbp)
leaq -96(%rbp), %rax
movl $0, -12(%rbp)
movl %edi, -16(%rbp)
movq %rsi, -24(%rbp)
movq -24(%rbp), %rsi
movq 8(%rsi), %rsi
movq %rax, %rdi
callq _strcpy
leaq L_.str(%rip), %rdi
leaq -96(%rbp), %rsi
movq %rax, -104(%rbp) ## 8-byte Spill
movb $0, %al
callq _printf
movq ___stack_chk_guard#GOTPCREL(%rip), %rsi
movq (%rsi), %rsi
movq -8(%rbp), %rdi
cmpq %rdi, %rsi
movl %eax, -108(%rbp) ## 4-byte Spill
jne LBB0_2
## BB#1: ## %SP_return
movl $0, %eax
addq $112, %rsp
popq %rbp
ret
LBB0_2: ## %CallStackCheckFailBlk
callq ___stack_chk_fail
.cfi_endproc
.section __TEXT,__cstring,cstring_literals
L_.str: ## #.str
.asciz "buffer: %s\n"
.subsections_via_symbols
[UPDATE]
Actually, none of the registers seem to be getting overwritten, yet it looks like they should be:
Starting program: /Users/henrypitcairn/test2 python -c 'print "A"*128'
Breakpoint 1, 0x0000000100000ed4 in main ()
(gdb) c
Continuing.
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGABRT, Aborted.
0x00007fff8eef3866 in ?? ()
(gdb) info registers
rax 0x0 0
rbx 0x7fff77921310 140735199449872
rcx 0x7fff5fbff9f8 140734799804920
rdx 0x0 0
rsi 0x6 6
rdi 0xc07 3079
rbp 0x7fff5fbffa20 0x7fff5fbffa20
rsp 0x7fff5fbff9f8 0x7fff5fbff9f8
r8 0x0 0
r9 0x0 0
r10 0x8000000 134217728
r11 0x206 518
r12 0x0 0
r13 0x0 0
r14 0x6 6
r15 0x0 0
rip 0x7fff8eef3866 0x7fff8eef3866
eflags 0x206 [ PF IF ]
cs 0x7 7
ss *value not available*
ds *value not available*
es *value not available*
fs 0x0 0
gs 0x30000 196608
(gdb) disas main
Dump of assembler code for function main:
0x0000000100000ed0 <+0>: push %rbp
0x0000000100000ed1 <+1>: mov %rsp,%rbp
0x0000000100000ed4 <+4>: sub $0x70,%rsp
0x0000000100000ed8 <+8>: mov 0x131(%rip),%rax # 0x100001010
0x0000000100000edf <+15>: mov (%rax),%rax
0x0000000100000ee2 <+18>: mov %rax,-0x8(%rbp)
0x0000000100000ee6 <+22>: lea -0x60(%rbp),%rax
0x0000000100000eea <+26>: movl $0x0,-0xc(%rbp)
0x0000000100000ef1 <+33>: mov %edi,-0x10(%rbp)
0x0000000100000ef4 <+36>: mov %rsi,-0x18(%rbp)
0x0000000100000ef8 <+40>: mov -0x18(%rbp),%rsi
0x0000000100000efc <+44>: mov 0x8(%rsi),%rsi
0x0000000100000f00 <+48>: mov %rax,%rdi
0x0000000100000f03 <+51>: callq 0x100000f54
0x0000000100000f08 <+56>: lea 0x7b(%rip),%rdi # 0x100000f8a
0x0000000100000f0f <+63>: lea -0x60(%rbp),%rsi
0x0000000100000f13 <+67>: mov %rax,-0x68(%rbp)
0x0000000100000f17 <+71>: mov $0x0,%al
0x0000000100000f19 <+73>: callq 0x100000f4e
0x0000000100000f1e <+78>: mov 0xeb(%rip),%rsi # 0x100001010
0x0000000100000f25 <+85>: mov (%rsi),%rsi
0x0000000100000f28 <+88>: mov -0x8(%rbp),%rdi
0x0000000100000f2c <+92>: cmp %rdi,%rsi
0x0000000100000f2f <+95>: mov %eax,-0x6c(%rbp)
0x0000000100000f32 <+98>: jne 0x100000f43 <main+115>
0x0000000100000f38 <+104>: mov $0x0,%eax
0x0000000100000f3d <+109>: add $0x70,%rsp
0x0000000100000f41 <+113>: pop %rbp
0x0000000100000f42 <+114>: retq
0x0000000100000f43 <+115>: callq 0x100000f48
End of assembler dump.
(gdb)

From your assembly code you can see that the compiler added a stack_chk_guard, to get more "easily manipulated" programs try compiling with -fno-stack-protector (assuming it's gcc). Some more options (also in llvm) are here.
It also shows that the compiler is saving 112 bytes on the stack for local variables, which include 4 byte and 8 byte register spills - so it's possible that the rest 100 bytes are the buffer + some padding for protection, which might explain why you see it failing above 100.
You actually didn't finish the entire frame there, you're probably overrunning the spilled registers, which explains why they appear to have been "changed" in your previous runs, and also possibly why you got a SIGABORT instead of seg fault and why the return address is normal - you didn't overwrite the return address, you overwrote some register (leading to god knows what)

Related

Does GDB shows wrong data when run over an compiler optimized binary image?

I am having a code like -
100 static void
101 func (<struct type1> *arg1, <struct type2> **arg2)
102 {
103 <struct type2> *var1;
...
...
...
112 var1 = *arg2; /* arg2 is no where else used in this function */
113 if (!var1) {
114 return;
115 }
...
... /* var1 has been referenced to read its properties and to set its properties */
120
...
...
125 my_free(); /* crash happened at this line - my_free() would call __free() */
...
...
130
}
The program has crashed and created a core like -
#2 0x000055ea29cceb76 in func [__be_func...] (arg1=arg1#entry=0x7f8bcc440098, arg2=arg2#entry=0x0) at myfile.c:121
You could notice that the second argument arg2 is shown by GDB as 0x0.
However, if it was NULL then program could have crashed at line#112 itself.
So I decided to disassemble the code and below is the excerpt after disassembling the above function -
(gdb) disass
Dump of assembler code for function func:
0x000055ea29cce790 <+0>: push %rbp
0x000055ea29cce791 <+1>: mov %rsp,%rbp
0x000055ea29cce794 <+4>: push %r15
0x000055ea29cce796 <+6>: push %r14
0x000055ea29cce798 <+8>: push %r13
0x000055ea29cce79a <+10>: push %r12
0x000055ea29cce79c <+12>: push %rbx
0x000055ea29cce79d <+13>: sub $0x38,%rsp
0x000055ea29cce7a1 <+17>: mov %rdi,%r14
0x000055ea29cce7a4 <+20>: movabs $0x8000000000000000,%r15
0x000055ea29cce7ae <+30>: addq $0x1,0x1864a08a(%rip)
0x000055ea29cce7b6 <+38>: lea -0x1(%r15),%rdx
0x000055ea29cce7ba <+42>: mov %rdi,-0x38(%rbp)
0x000055ea29cce7be <+46>: and %rdx,%r14
0x000055ea29cce7c1 <+49>: and %rdx,%rsi
0x000055ea29cce7c4 <+52>: mov $0xffffffffffffff7f,%r13
0x000055ea29cce7cb <+59>: and 0x50(%r14),%r13
0x000055ea29cce7cf <+63>: movl $0x1000000,-0x50(%rbp)
0x000055ea29cce7d6 <+70>: movl $0x0,-0x48(%rbp)
0x000055ea29cce7dd <+77>: mov (%rsi),%rcx
0x000055ea29cce7e0 <+80>: bswap %rcx
0x000055ea29cce7e3 <+83>: mov %rcx,%rbx
0x000055ea29cce7e6 <+86>: mov %rdx,-0x40(%rbp)
0x000055ea29cce7ea <+90>: and %rdx,%rbx
0x000055ea29cce7ed <+93>: je 0x55ea29ccebcb
0x000055ea29cce7f3 <+99>: bswap %r13
0x000055ea29cce7f6 <+102>: testb $0x20,0x10d0(%r14)
0x000055ea29cce7fe <+110>: jne 0x55ea29cce80b
0x000055ea29cce800 <+112>: cmpl $0x0,0x48(%r14)
0x000055ea29cce805 <+117>: je 0x55ea29cce9e1
0x000055ea29cce80b <+123>: mov %rsi,-0x58(%rbp)
0x000055ea29cce80f <+127>: mov 0x1210(%r14),%r11
0x000055ea29cce816 <+134>: bswap %r11
...
...
...
...
My analysis is -
Since arg2 is only used at line# 112, may be due to compiler optimisation, it is not saved onto the stack frame and just held in register %rsi. And once after the execution of line# 112, the value in %rsi has got changed and arg2 is lost indefinitely.
If my analysis is correct, shouldn't GDB shown arg2 as <optimized out> in the callstack rather than as 0x0?
Edit#1 to my analysis -
when I dumped the stackframe of the function func
(gdb) i r rbp
rbp 0x7f8bcd651730 0x7f8bcd651730
(gdb) x/13xg 0x7F8BCD6516D0
0x7f8bcd6516d0: 0x0000000b00000006
0x00007f8bcd651748 <--- stored register %rsi value, which represents the 2nd argument - ie `arg2`
0x7f8bcd6516e0: 0x0000000001000000
0x0000000000000000
0x7f8bcd6516f0: 0x7fffffffffffffff
0x80007f8bcc440098 <--- stored register %rdi value, which represents the 1st argument - ie `arg1`
0x7f8bcd651700: 0x80007f8bd51a6230
0x00007f8bd51a6248
0x7f8bcd651710: 0x00007f8bcd651748
0x00007f8bd51a6230
0x7f8bcd651720: 0x80007f8bcc440098
0x0000000000000000
0x7f8bcd651730: 0x00007f8bcd651780
As you can see the 2nd argument arg2 is not really NULL.
Important thing to notice is - 1st argument is stored as 0x80007f8bcc440098, whereas 2nd argument is stored as 0x00007f8bcd651748.
The difference is that 1st argument's MSB bit is set to 1.
Why is GDB not printing rightly when the address's MSB is not set to 1?
It appear to be an issue with compiler.
Output of info address arg2 showed that 2nd argument arg2 doesn't contains an entry in stack location.

Why gdb shows different addresses in RIP-relative mode from absolute address?

Having this in c:
#include <stdio.h>
#include <stdlib.h>
int x;
int main(){
printf("eneter x\n");
scanf("%i",&x);
printf("you enetered: %i\n", x);
return 0;
}
in gdb:
starti
disas main
0x0000555555555155 <+0>: push %rbp
0x0000555555555156 <+1>: mov %rsp,%rbp
0x0000555555555159 <+4>: lea 0xea4(%rip),%rdi # 0x555555556004
0x0000555555555160 <+11>: callq 0x555555555030 <puts#plt>
0x0000555555555165 <+16>: lea 0x2ed8(%rip),%rsi # 0x555555558044 <x>
0x000055555555516c <+23>: lea 0xe9a(%rip),%rdi # 0x55555555600d
0x0000555555555173 <+30>: mov $0x0,%eax
0x0000555555555178 <+35>: callq 0x555555555050 <__isoc99_scanf#plt>
0x000055555555517d <+40>: mov 0x2ec1(%rip),%eax # 0x555555558044 <x>
0x0000555555555183 <+46>: mov %eax,%esi
0x0000555555555185 <+48>: lea 0xe84(%rip),%rdi # 0x555555556010
0x000055555555518c <+55>: mov $0x0,%eax
0x0000555555555191 <+60>: callq 0x555555555040 <printf#plt>
0x0000555555555196 <+65>: mov $0x0,%eax
0x000055555555519b <+70>: pop %rbp
0x000055555555519c <+71>: retq
here the relative address of x variable is $rip+0x2ed8 (from instruction lea 0x2ed8(%rip),%rsi # 0x555555558044). But as you can see in the comment #, the absolute address is 0x555555558044. Ok will I get that address when try to read from the relative one? Lets see:
x $rip+0x2ed8
0x555555558055: 0x00000000
nop - relative address did not use the absolute address, where the x var is really stored (0x555555558055 != 0x555555558044) the difference is 17 bytes. Is it the number of bytes of the instruction itself (lea + operands)? I do not know, but do not think so. So why does relative and absolute addressing differ in gdb?
PS, generated assembly:
.file "a.c"
.comm x,4,4
.section .rodata
.LC0:
.string "eneter x"
.LC1:
.string "%i"
.LC2:
.string "you enetered: %i\n"
.text
.globl main
.type main, #function
main:
pushq %rbp #
movq %rsp, %rbp #,
# a.c:5: printf("eneter x\n");
leaq .LC0(%rip), %rdi #,
call puts#PLT #
# a.c:6: scanf("%i",&x);
leaq x(%rip), %rsi #,
leaq .LC1(%rip), %rdi #,
movl $0, %eax #,
call __isoc99_scanf#PLT #
# a.c:7: printf("you enetered: %i\n", x);
movl x(%rip), %eax # x, x.0_1
movl %eax, %esi # x.0_1,
leaq .LC2(%rip), %rdi #,
movl $0, %eax #,
call printf#PLT #
# a.c:8: return 0;
movl $0, %eax #, _6
# a.c:9: }
popq %rbp #
ret
.size main, .-main
.ident "GCC: (Debian 8.3.0-6) 8.3.0"
.section .note.GNU-stack,"",#progbits
Here, the RIP-relative mode is used:
# a.c:6: scanf("%i",&x);
leaq x(%rip), %rsi #,
where the x is position of the x symbol. But in comments, someone said, that $rip+0x2ed8 is not the same, and the offset 0x2ed8 does not lead to the address of the x. But why those two differ? but should be RIP-relative mode addressing and both should gain the same offset (and thus address).
0x0000555555555165 <+16>: lea 0x2ed8(%rip),%rsi # 0x555555558044 <x>
0x000055555555516c <+23>: lea 0xe9a(%rip),%rdi # 0x55555555600d
A RIP relative address in an instruction is relative to the address just after the current instruction (i.e. the address of the instruction plus the size of the instruction, or the address of the following instruction). This is because when the instruction has been loaded into the processor, the RIP register is advanced by the size of the current instruction just before it is executed. (At least that is the model that is followed even though modern processors use all sorts of tricks behind the scenes to speed up execution.) (Note: The above is true for several CPU architectures, including x86 variants, but some other CPU architectures differ in the point from which PC-relative addresses are measured1.)
The first instruction above is at address 0x555555555165 and the following instruction is at address 0x55555555516c (the instruction is 7 bytes long). In the first instruction, the RIP relative address 0x2ed8(%rip) refers to 0x2ed8 + 0x000055555555516c = 0x555555558044.
Note that if you set a breakpoint on an instruction in a debugger and show the registers when the breakpoint is reached, RIP will point to the current instruction, not the next one, because the current instruction is not being executed yet.
1 Thanks to Peter Cordes for details about PC-relative addressing for ARM and RISC-V CPU architectures.

can't find assembly for pre-written functions

I am a huge fan of network protocols and libnet, which is why I've been trying to imitate some network protocols that are not included by libnet. Capturing packets, imitating headers etc works so far. Now I need a way to actually write these exact packets to my network card. I've tried libnet_adv_write_rawipv4() and -link(), both won't work. I can't cull the headers with libnet_adv_cull_header() because of the stupid errors and bugs. So I figured, that the problem could be solved with a little assembly: get the assembly code for the actual libnet_build() and libnet_write() call, alter some bytes and voila: raw bytes get written to the network card. So I have written a dummy program:
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
int main() {
libnet_t *l;
l = libnet_init(LIBNET_RAW4, 0, NULL);
libnet_build_tcp(2000, 450, 0, 1234, TH_SYN, 254, 0, NULL, LIBNET_TCP_H + 5,
"aaaaa", 5, l, 0);
libnet_build_ipv4(LIBNET_TCP_H + LIBNET_IPV4_H + 5, 0, 1, 0, 64, 6, 0,
2186848448, 22587584, NULL, 0, l, 0);
libnet_write(l);
return 0;
}
Works so far. Now I got the assembly version of the program using
gcc -o program program.c -S
And this is where the actual problem starts:
.LC0:
.string "aaaaa"
.text
.globl main
.type main, #function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $0, %edx
movl $0, %esi
movl $1, %edi
call libnet_init
movq %rax, -8(%rbp)
subq $8, %rsp
pushq $0
pushq -8(%rbp)
pushq $5
pushq $.LC0
pushq $25
pushq $0
pushq $0
movl $254, %r9d
movl $2, %r8d
movl $1234, %ecx
movl $0, %edx
movl $450, %esi
movl $2000, %edi
call libnet_build_tcp
addq $64, %rsp
subq $8, %rsp
pushq $0
pushq -8(%rbp)
pushq $0
pushq $0
pushq $22587584
pushq $-2108118848
pushq $0
movl $6, %r9d
movl $64, %r8d
movl $0, %ecx
movl $1, %edx
movl $0, %esi
movl $45, %edi
call libnet_build_ipv4
addq $64, %rsp
movq -8(%rbp), %rax
movq %rax, %rdi
call libnet_write
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
See this?
call libnet_build_ipv4
I can't copy the assembly code of these build() or write() calls, because all there is is a reference to them. Now, where would I find the assembly code for these pre-written functions included in libnet-functions.h (libnet_build_ipv4(), libnet_build_tcp(), libnet_write()) ?????
GDB is your friend in situations like this. You don't say anything about what platform you're on, the following example works on Ubuntu, but should work similarly on other distributions.
First, make sure that you have debug-symbols for libnet installed:
sudo apt install libnet1-dbg
Find out where libnet is installed:
~$ dpkg -L libnet1 | grep \.so
/usr/lib/x86_64-linux-gnu/libnet.so.1.7.0
/usr/lib/x86_64-linux-gnu/libnet.so.1
Open it (or your own application) with GDB:
~$ gdb /usr/lib/x86_64-linux-gnu/libnet.so.1.7.0
Reading symbols from /usr/lib/x86_64-linux-gnu/libnet.so.1.7.0...Reading symbols from /usr/lib/debug//usr/lib/x86_64-linux-gnu/libnet.so.1.7.0...done.
done.
Use the disassemble command to inspect anything you like:
(gdb) disassemble libnet_build_ipv4
Dump of assembler code for function libnet_build_ipv4:
0x0000000000007d60 <+0>: push %r15
0x0000000000007d62 <+2>: push %r14
0x0000000000007d64 <+4>: push %r13
0x0000000000007d66 <+6>: push %r12
0x0000000000007d68 <+8>: push %rbp
0x0000000000007d69 <+9>: push %rbx
0x0000000000007d6a <+10>: sub $0x48,%rsp
0x0000000000007d6e <+14>: mov 0xa8(%rsp),%rbx
0x0000000000007d76 <+22>: mov %edx,0x8(%rsp)
0x0000000000007d7a <+26>: mov %fs:0x28,%rax
0x0000000000007d83 <+35>: mov %rax,0x38(%rsp)
0x0000000000007d88 <+40>: xor %eax,%eax
0x0000000000007d8a <+42>: mov %ecx,0x14(%rsp)
0x0000000000007d8e <+46>: mov 0x80(%rsp),%r14d
0x0000000000007d96 <+54>: test %rbx,%rbx
0x0000000000007d99 <+57>: mov 0x98(%rsp),%r15
0x0000000000007da1 <+65>: je 0x810a <libnet_build_ipv4+938>
0x0000000000007da7 <+71>: mov %esi,%r13d
0x0000000000007daa <+74>: mov 0xb0(%rsp),%esi
0x0000000000007db1 <+81>: mov %edi,%ebp
0x0000000000007db3 <+83>: mov $0xd,%ecx
0x0000000000007db8 <+88>: mov $0x14,%edx
0x0000000000007dbd <+93>: mov %rbx,%rdi
0x0000000000007dc0 <+96>: mov %r9d,0x1c(%rsp)
0x0000000000007dc5 <+101>: mov %r8d,0x18(%rsp)
0x0000000000007dca <+106>: callq 0xea10 <libnet_pblock_probe>
0x0000000000007dcf <+111>: test %rax,%rax
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb)

Unexpected behaviour in simple pointer arithmetics in kernel space C code [duplicate]

I am currently following this workbook on build an operating system.
My intention is to write a 64-bit kernel. I have got as far as loading the "kernel" code and writing individual characters to the frame buffer while in text mode.
My problem appears when I add a level of indirection to writing a single character to the frame buffer by wrapping the code in a function. It would appear that the char value passed into the function is being corrupted in some way.
I have three files:
bootloader.asm
; bootloader.asm
[org 0x7c00]
KERNEL_OFFSET equ 0x1000
mov bp, 0x9000
mov sp, bp
; load the kernel from boot disk
mov bx, KERNEL_OFFSET
mov dl, dl ; boot drive is set to dl
mov ah, 0x02 ; bios read sector
mov al, 15 ; read 15 sectors
mov ch, 0x00 ; cylinder 0
mov cl, 0x02 ; read from 2nd sector
mov dh, 0x00 ; select head 0
int 0x13
; THERE COULD BE ERRORS HERE BUT FOR NOW ASSUME IT WORKS
; switch to protected mode
cli
lgdt [gdt.descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_SEGMENT:start_protected_mode
[bits 32]
start_protected_mode:
mov ax, DATA_SEGMENT
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
call KERNEL_OFFSET
jmp $
[bits 16]
gdt: ; Super Simple Global Descriptor Table
.start:
.null:
dd 0x0
dd 0x0
.code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
.data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
.end:
.descriptor:
dw .end - .start
dd .start
CODE_SEGMENT equ gdt.code - gdt.start
DATA_SEGMENT equ gdt.data - gdt.start
times 510-($-$$) db 0
dw 0xaa55
bootkernel.asm
[bits 32]
[extern main]
[global _start]
_start:
call main
jmp $
kernel.c
// LEGACY MODE VIDEO DRIVER
#define FRAME_BUFFER_ADDRESS 0xb8002
#define GREY_ON_BLACK 0x07
#define WHITE_ON_BLACK 0x0f
void write_memory(unsigned long address, unsigned int index, unsigned char value)
{
unsigned char * memory = (unsigned char *) address;
memory[index] = value;
}
unsigned int frame_buffer_offset(unsigned int col, unsigned int row)
{
return 2 * ((row * 80u) + col);
}
void write_frame_buffer_cell(unsigned char c, unsigned char a, unsigned int col, unsigned int row)
{
unsigned int offset = frame_buffer_offset(col, row);
write_memory(FRAME_BUFFER_ADDRESS, offset, c);
write_memory(FRAME_BUFFER_ADDRESS, offset + 1, a);
}
void main()
{
unsigned int offset = frame_buffer_offset(0, 1);
write_memory(FRAME_BUFFER_ADDRESS, offset, 'A');
write_memory(FRAME_BUFFER_ADDRESS, offset + 1, GREY_ON_BLACK);
write_frame_buffer_cell('B', GREY_ON_BLACK, 0, 1);
}
The .text section is linked to start from 0x1000 which is where the bootloader expects the kernel to start.
The linker.ld script is
SECTIONS
{
. = 0x1000;
.text : { *(.text) } /* Kernel is expected at 0x1000 */
}
The Make file that puts this all together is:
bootloader.bin: bootloader.asm
nasm -f bin bootloader.asm -o bootloader.bin
bootkernel.o: bootkernel.asm
nasm -f elf64 bootkernel.asm -o bootkernel.o
kernel.o: kernel.c
gcc-6 -Wextra -Wall -ffreestanding -c kernel.c -o kernel.o
kernel.bin: bootkernel.o kernel.o linker.ld
ld -o kernel.bin -T linker.ld bootkernel.o kernel.o --oformat binary
os-image: bootloader.bin kernel.bin
cat bootloader.bin kernel.bin > os-image
qemu: os-image
qemu-system-x86_64 -d guest_errors -fda os-image -boot a
I've taken a screen shot of the output that I am getting. I expect 'A' to appear in the 0th column of the 1st row and for 'B' to appear on the 1st column of the 0th row. For some reason I am getting another character.
Output of gcc-6 -S kernel.c
.file "kernel.c"
.text
.globl write_memory
.type write_memory, #function
write_memory:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -24(%rbp)
movl %esi, -28(%rbp)
movl %edx, %eax
movb %al, -32(%rbp)
movq -24(%rbp), %rax
movq %rax, -8(%rbp)
movl -28(%rbp), %edx
movq -8(%rbp), %rax
addq %rax, %rdx
movzbl -32(%rbp), %eax
movb %al, (%rdx)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size write_memory, .-write_memory
.globl frame_buffer_offset
.type frame_buffer_offset, #function
frame_buffer_offset:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -8(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
sall $4, %eax
movl %eax, %edx
movl -4(%rbp), %eax
addl %edx, %eax
addl %eax, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size frame_buffer_offset, .-frame_buffer_offset
.globl write_frame_buffer_cell
.type write_frame_buffer_cell, #function
write_frame_buffer_cell:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %esi, %eax
movl %edx, -28(%rbp)
movl %ecx, -32(%rbp)
movb %dil, -20(%rbp)
movb %al, -24(%rbp)
movl -32(%rbp), %edx
movl -28(%rbp), %eax
movl %edx, %esi
movl %eax, %edi
call frame_buffer_offset
movl %eax, -4(%rbp)
movzbl -20(%rbp), %edx
movl -4(%rbp), %eax
movl %eax, %esi
movl $753666, %edi
call write_memory
movzbl -24(%rbp), %eax
movl -4(%rbp), %edx
leal 1(%rdx), %ecx
movl %eax, %edx
movl %ecx, %esi
movl $753666, %edi
call write_memory
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size write_frame_buffer_cell, .-write_frame_buffer_cell
.globl main
.type main, #function
main:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $1, %esi
movl $0, %edi
call frame_buffer_offset
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
movl $65, %edx
movl %eax, %esi
movl $753666, %edi
call write_memory
movl -4(%rbp), %eax
addl $1, %eax
movl $7, %edx
movl %eax, %esi
movl $753666, %edi
call write_memory
movl $0, %ecx
movl $1, %edx
movl $7, %esi
movl $66, %edi
call write_frame_buffer_cell
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size main, .-main
.ident "GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901"
.section .note.GNU-stack,"",#progbits
I can reproduce your exact output if the code is modified to be:
unsigned int offset = frame_buffer_offset(0, 1);
write_memory(FRAME_BUFFER_ADDRESS, offset, 'A');
write_memory(FRAME_BUFFER_ADDRESS, offset + 1, GREY_ON_BLACK);
write_frame_buffer_cell('B', GREY_ON_BLACK, 1, 0);
The difference being in the last line ('B', GREY_ON_BLACK, 1, 0);. Originally you had ('B', GREY_ON_BLACK, 0, 1); . This is in line with what you described you were trying to do when you said:
I've taken a screen shot of the output that I am getting. I expect 'A' to appear in the 0th column of the 1st row and for 'B' to appear on the 1st column of the 0th row.
I gather you may have posted the wrong code in this question. This is the output I get:
It seems you are new to OS development. Your bootloader code only places the CPU into 32-bit protected mode, but to run a 64-bit kernel you need to be in 64-bit longmode. If you are just getting started I'd suggest falling back to writing a 32-bit kernel for purposes of learning at this early stage. At the bottom I have a 64-bit long mode section with a link to a longmode tutorial that could be used to modify your bootloader to run 64-bit code.
Primary Issue Causing Unusual Behaviour
You are experiencing an issue primarily related to the fact that you are generating 64-bit code with GCC but you are running it in 32-bit protected mode according to your bootloader code. 64-bit code generation running in 32-bit protected mode may appear to execute, but it will do it incorrectly. In simple OSes where you are simply displaying to the video display you may often see unexpected output as a side effect. Your program could triple fault the machine, but you got unlucky that the side effect seemed to display something on the video display. You may have been under the false impression that things were working as they should when they really weren't.
This question is somewhat similar to another Stackoverflow question. After the original poster of that question made available a complete example it became clear that it was his issue. Part of my answer to him to resolve the issue was as follows:
Likely Cause of Undefined Behavior
After all the code and the make file were made available in EDIT 2 it became clear that one significant problem was that most of the code was compiled and linked to 64-bit objects and executables. That code won't work in 32-bit protected mode.
In the make file make these adjustments:
When compiling with GCC you need to add -m32 option
When assembling with GNU Assembler (as) targeting 32-bit objects you need to use --32
When linking with LD you need to add the -melf_i386 option
When assembling with NASM targeting 32-bit objects you need to change -f elf64 to -f elf32
With that in mind you can alter your Makefile to generate 32-bit code. It could look like:
bootloader.bin: bootloader.asm
nasm -f bin bootloader.asm -o bootloader.bin
bootkernel.o: bootkernel.asm
nasm -f elf32 bootkernel.asm -o bootkernel.o
kernel.o: kernel.c
gcc-6 -m32 -Wextra -Wall -ffreestanding -c kernel.c -o kernel.o
kernel.bin: bootkernel.o kernel.o linker.ld
ld -melf_i386 -o kernel.bin -T linker.ld bootkernel.o kernel.o --oformat binary
os-image: bootloader.bin kernel.bin
cat bootloader.bin kernel.bin > os-image
qemu: os-image
qemu-system-x86_64 -d guest_errors -fda os-image -boot a
I gather when you started having issues with your code you ended up trying 0xb8002 as the address for your video memory. It should be 0xb8000. You'll need to modify:
#define FRAME_BUFFER_ADDRESS 0xb8002
To be:
#define FRAME_BUFFER_ADDRESS 0xb8000
Making all these changes should resolve your issues. This is what the output I got looked like after the changes mentioned above:
Other observations
In write_memory you use:
unsigned char * memory = (unsigned char *) address;
Since you are using 0xb8000 that is memory mapped to the video display you should mark it as volatile since a compiler could optimize things away not knowing that there is a side effect to writing to that memory (namely displaying characters on a display). You might wish to use:
volatile unsigned char * memory = (unsigned char *) address;
In your bootloader.asm You really should explicitly set the A20 line on. You can find information about doing that in this OSDev Wiki article. The status of the A20 line at the point a bootloader starts executing may vary between emulators. Failure to set it on could cause issues if you try to access memory areas on an odd numbered megabyte boundary (like 0x100000 to 0x1fffff, 0x300000 to 0x1fffff etc). Accesses to the odd numbered megabyte memory regions will actually read data from the even numbered memory region just below it. This is usually not behaviour you want.
64-bit long mode
If you want to run 64-bit code you will need to place the processor into 64-bit long mode. This is a bit more involved than entering 32-bit protected mode. Information on 64-bit longmode can be found in the OSDev wiki. Once properly in 64-bit longmode you can use 64-bit instructions generated by GCC.

Can someone please tell me whats wrong with the following Assembl code. Getting Segmentation fault

Getting segmentation fault in the function, due to instruction
movq -8(%rbp), %rax, one before the printf. I can't understand why ?
Note : this is not gcc generated assembly, but by compiler i am writing. Assembly code is almost similar to what gcc generates.
.text
.globl main
.type main, #function
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $2, -4(%rbp)
leaq -4(%rbp), %rax
movl %eax, %edi
movb $0, %al
call fcvt2
movl %eax, -4(%rbp)
leaq .LC0(%rip), %rdi
movl -4(%rbp), %esi
movb $0, %al
call printf
leave
ret
.globl fcvt2
.type fcvt2, #function
fcvt2:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
movq %rdi, -8(%rbp)
leaq .LC1(%rip), %rdi
movq -8(%rbp), %rax
movl (%rax), %esi
movb $0, %al
call printf
movq -8(%rbp), %rax
movl (%rax), %edi
movl %edi, %eax
leave
ret
.section .rodata
.LC1:
.string "It should be : %d\f"
.LC0:
.string "%d\n"
And C Program is :
int fcvt2(int *ip) {
int i;
printf("It should be : %d\f", *ip);
return *ip;
}
void main() {
int i;
i = 2;
i = fcvt2(&i);
printf("%d\n",i);
return;
}
gdb output at fault point:
rax 0xffffdd4c 4294958412
rbx 0x0 0
rcx 0x7ffffff7 2147483639
rdx 0x7ffff7dd3780 140737351858048
rsi 0x7fffffffdd48 140737488346440
rdi 0xffffdd4c 4294958412
rbp 0x7fffffffdd30 0x7fffffffdd30
rsp 0x7fffffffdd00 0x7fffffffdd00
r8 0x0 0
r9 0x9 9
r10 0x7ffff7dd1b78 140737351850872
r11 0x246 582
r12 0x400430 4195376
r13 0x7fffffffde30 140737488346672
r14 0x0 0
r15 0x0 0
rip 0x40059c 0x40059c <fcvt2+20>
eflags 0x10206 [ PF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
movl %eax, %edi in the caller truncates the pointer arg to fcvt2. You actually segfault on mov (%rax),%esi. rax, not the instruction before it like you claimed. (Time for a refresher on your GDB skills?)
leaq -4(%rbp), %rax generated it correctly in %rax, but then your compiler forgot that it was a 64-bit pointer to a 32-bit value. (Ideally you'd want to leaq -4(%rbp), %rdi directly into the arg register.)
Off topic: if you don't need to preserve the upper bytes of EAX, movb $0, %al is less efficient than xor %eax, %eax. I think you're doing this for the x86-64 SysV variadic function convention, and you're right that only %al needs to say how many XMM register args there are, not the whole %eax, so you got that right. But zeroing eax is the most efficient way to zero al. Of course, you don't need to do this at all for non-variadic functions, but your compiler is obviously still in the just-get-it-working phase, so doing it unconditionally isn't a correctness problem; you never need to pass anything else in rax, and function calls are always assumed to clobber rax.
(Also related: Haswell/Skylake partial registers have false dependencies: al isn't renamed separately from rax anymore)

Resources