FAT12 file starts at 0x4200 or 0x4400? - nasm

Following link http://www.c-jump.com/CIS24/Slides/FAT/lecture.html say Fat12 file starts at
0x4200, but when I use hexeditor watch the floppy image created by following commands, file seems start at 0x4400, and bootloader assembly code used jmp 0xc400 (0x8000+0x4400).
I tried change to jmp 0xc200, seems works as well. Which confuses me.
all:img
boot:
nasm -o boot.bin ipl.nas
nasm -o os.bin os.nas
img:boot
dd if=boot.bin of=myos.img count=1 bs=512
dd if=/dev/zero of=myos.img bs=512 seek=1 skip=1 count=2879
copy:
mkdir -p /tmp/floppy
sudo mount -o loop myos.img /tmp/floppy -o fat=12
sleep 1
sudo cp os.bin /tmp/floppy
sleep 1
sudo umount /tmp/floppy
run:copy
qemu-system-i386 -drive file=myos.img,if=floppy
clean:
rm *.bin *.img
os.nas starts with
; haribote-os
; TAB=4
ORG 0xc400
MOV AL,0x13
MOV AH,0x00
INT 0x10
fin:
HLT
JMP fin
ipl.nas jmp part of code
CMP CH, CYLS
JB readloop ; If CH < CYLS, go to readloop
; Now that you've read it, run haribote.sys!
JMP 0xc400
myos.img at 0x4200 and 0x4400
00004200 00 00 00 00 00 00 00 00
00004210 00 00 00 00 00 00 00 00
00004220 00 00 00 00 00 00 00 00
00004230 00 00 00 00 00 00 00 00
00004400 B0 13 B4 00 CD 10 F4 EB
00004410 00 00 00 00 00 00 00 00
ipl.nas first sector
; haribote-ipl
; TAB=4
CYLS EQU 10 ; How far to read
ORG 0x7c00 ; where this program is loaded
; the following is for a standard FAT12 formatted floppy disk
JMP entry
DB 0x90
DB "HARIBOTE" ; You can freely write the boot sector name (8
DW 512 ; one sector size (must be 512)
DB 1 ; cluster size (must be 1 sector)
DW 1 ; where the FAT starts (usually sector 1)
DB 2 ; Number of FATs (must be 2)
DW 224 ; Size of root directory area (usually 224
DW 2880 ; Size of this drive (must be 2880 sectors)
DB 0xf0 ; Media type (must be 0xf0)
DW 9 ; Length of FAT area (must be 9 sectors)
DW 18 ; how many sectors per track (must be 18)
DW 2 ; number of heads (must be 2)
DD 0 ; Always 0 here because no partition is used
DD 2880 ; write this drive size again
DB 0,0,0x29 ;
DD 0xffffffff ; probably volume serial number
DB "HARIBOTEOS " ; Disk name (11 bytes)
DB "FAT12 " ; Name of format (8 bytes)
RESB 18 ; Reserve 18 bytes for now
; program body
entry:
MOV AX,0 ; Register initialization
MOV SS,AX
MOV SP, 0x7c00
MOV DS,AX
; read disk
MOV AX, 0x0820
MOV ES,AX
MOV CH,0 ; Cylinder 0
MOV DH,0 ; head 0
MOV CL,2 ; sector 2
readloop:
MOV SI,0 ; register to count the number of failures
retry:
MOV AH,0x02 ; AH=0x02 : Disk read
MOV AL,1 ; 1 sector
MOV BX,0
MOV DL,0x00 ; Drive A
INT 0x13 ; Disk BIOS call
JNC next ; To next if no error occurs
ADD SI,1 ; Add 1 to SI
CMP SI,5 ; Compare SI and 5
JAE error ; If SI >= 5, go to error
MOV AH,0x00
MOV DL,0x00 ; Drive A
INT 0x13 ; Drive reset
JMP retry
next:
MOV AX,ES ; Advance address by 0x200
ADD AX, 0x0020
MOV ES,AX ; ADD ES,0x020 This is because there is no
ADD CL,1 ; add 1 to CL
CMP CL,18 ; Compare CL with 18
JBE readloop ; If CL <= 18, go to readloop
MOV CL,1
ADD DH,1
CMP DH,2
JB readloop ; If DH < 2, go to readloop
MOV DH,0
ADD CH,1
CMP CH, CYLS
JB readloop ; If CH < CYLS, go to readloop
; Now that you've read it, run haribote.sys!
JMP 0xc400

The webpage about FAT that you studied, deals with the matter in general and details what constitutes a legal FAT volume. However, an implementation can still make its own choices like about allocation strategies.
Not having your OS.BIN file stored in cluster #2, which corresponds to the very first cluster in the data region, might be considered a surprise. eg. MS-DOS does store its IO.SYS file starting with cluster #2.
It could be a Linux thing because skipping cluster #2 and starting from cluster #3 instead also happens in this superuser Q/A.
The data region starts at offset (1 + 9 + 9 + 14) * 512 = 16896 (0x4200)
Cluster #2 is at 0x4200
Cluster #3 is at 0x4400
...
Use your hex utility on MYOS.IMG to see what is in the first FAT structure at file offset 512.
cluster #2 cluster #3
00000200 F0 FF FF 00 F0 FF free cluster io.bin
00000200 F0 FF FF F7 FF FF bad cluster io.bin
00000200 F0 FF FF ?? F? FF some file 512+ io.bin
00000200 F0 FF FF FF FF FF some file 512- io.bin
I think you can omit the skip=1 mention in the line dd if=/dev/zero of=myos.img bs=512 seek=1 skip=1 count=2879.

Following link ... say Fat12 file starts at 0x4200 ...
It says that the area for files starts at 0x4200 on the disk whose boot sector is shown in chapter 10: A 1440K disk.
On a 720K or 360K disk, the area for files starts at a different offset; you have to calculate the offset from the information in the boot sector.
And the text is talking about the "file storage space", not about the "first file":
Just like a hard disk, multiple files are typically stored on a floppy disk.
The first file on the disk will typically be stored at the start of the file area (0x4200).
However, imagine the following scenario:
The disk formatting program (or the disk image creation tool) creates a temporary file; this file will be stored at offset 0x4200.
Now your file is copied to the disk. Because the cluster size is 0x200, the next free space is at 0x4200+0x200 = 0x4400.
The disk formatting program deletes the temporary file on the disk.
In this case, the already existing files (your file at position 0x4400) are NOT moved ...
Please also note that files can be "fragmented": This means that the first 0x200 bytes of a file may be stored at some position (for example 0x5000), the next 0x200 bytes of a file may be stored at a completely different position (for example 0x4800) and the next 0x200 bytes may be stored elsewhere (for example at offset 0xD000).
If you want to create a boot loader that is capable of booting from a file, you must parse the root directory and read out the "starting cluster" to calculate the first sector of the file.
Later, you must parse the FAT to calculate the next sector of the file.
... at least if you don't want to rely on the file to be "unfragmented" and to be stored at a certain offset. (MS-DOS copied the file to a certain offset and the MS-DOS boot loader assumed that the file was found there...)

Related

Assembly code to shell code: section .data and section .text in which order?

For an assignment, I wrote the following assembly code shell_exec.asm that should execute a shell in Linux:
section .data ; declare stuff
arg0 db "/bin/sh",0 ; 1st arg
align 4
argv dd arg0, 0 ; 2nd arg
envp dd 0 ; 3rd arg
section .text
global _start
_start:
mov eax, 0x0b ; execve
mov ebx, arg0 ; 1st arg
mov ecx, argv ; 2nd arg
mov edx, envp ; 3rd arg
int 0x80 ; kernel
I used nasm -f elf32 shell_exec.asm for compilation and ld -m elf_i386 -o shell_exec shell_exec.o for linking. Everything works so far and if I run ./shell_exec the shell spawns the way I want.
Now I wanted to extract the shell code (like \12\34\ab\cd\ef...) from this program. I used objdump -D -z shell_exec to show all parts of the code including the section .data and all zeroes. The output is as follows:
shell_exec: file format elf32-i386
Disassembly of section .text:
08049000 <_start>:
8049000: b8 0b 00 00 00 mov $0xb,%eax
8049005: bb 00 a0 04 08 mov $0x804a000,%ebx
804900a: b9 08 a0 04 08 mov $0x804a008,%ecx
804900f: ba 10 a0 04 08 mov $0x804a010,%edx
8049014: cd 80 int $0x80
Disassembly of section .data:
0804a000 <arg0>:
804a000: 2f das
804a001: 62 69 6e bound %ebp,0x6e(%ecx)
804a004: 2f das
804a005: 73 68 jae 804a06f <__bss_start+0x5b>
804a007: 00 add %al,(%eax)
0804a008 <argv>:
804a008: 00 a0 04 08 00 00 add %ah,0x804(%eax)
804a00e: 00 00 add %al,(%eax)
0804a010 <envp>:
804a010: 00 00 add %al,(%eax)
804a012: 00 00 add %al,(%eax)
If I only have a section .text within my assembly code, I can usually just copy all given values and use them as my shell code. But how is the order in case I have those two sections, namely .data and .text?
Edit 1
So, my second attempt is to do the assembly code like this:
section .text
global _start
_start:
mov ebp, esp
xor eax, eax
push eax ; -4
push "/sh " ; -8
push "/bin" ; -12
xor eax, eax
push eax
lea ebx, [ebp-12]
push ebx ; 1st arg
mov ecx, esp ; 2nd arg
lea edx, [ebp-4] ; 3rd arg
mov eax, 0x0b ; execve
int 0x80 ; kernel
This avoids using multiple sections, but sadly leads to a segmentation fault.

Basic input with x64 assembly code

I am writing a tutorial on basic input and output in assembly. I am using a Linux distribution (Ubuntu) that is 64 bit. For the first part of my tutorial I spoke about basic output and created a simple program like this:
global _start
section .text
_start:
mov rax,1
mov rdi,1
mov rsi,message
mov rdx,13
syscall
mov rax,60
xor rdi,rdi
syscall
section .data
message: db "Hello, World", 10
That works great. The system prints the string and exits cleanly. For the next part of my tutorial, I simply want to read one character in from the keyboard. From my understanding of this web site we change the rdi register to be 0 for a sys_read call.
I first subtract 8 from the current rsp and then load that address into the rsi register. (That is where I want to store the char). When I compile and run my program it appears to work... but the terminal seems to mimick the input I type in again.
Here is the program:
global _start
section .text
_start:
sub rsp,8 ; allocate space on the stack to read
mov rdi,0 ; set rdi to 0 to indicate a system read
mov rsi,[rsp-8]
mov rdx,1
syscall
mov rax,1
mov rdi,1
mov rsi,message
mov rdx,13
syscall
mov rax,60
xor rdi,rdi
syscall
section .data
message: db "Hello, World", 10
and this is what happens in my terminal...
matthew#matthew-Precision-WorkStation-690:~/Documents/Programming/RockPaperScissors$ nasm -felf64 rps.asm && ld rps.o && ./a.out
5
Hello, World
matthew#matthew-Precision-WorkStation-690:~/Documents/Programming/RockPaperScissors$ 5
5: command not found
matthew#matthew-Precision-WorkStation-690:~/Documents/Programming/RockPaperScissors$
The input 5 is repeated back to the terminal after the program has exited. What is the proper way to read in a single char using NASM and Linux x64?
In your first code section you have to set the SYS_CALL to 0 for SYS_READ (as mentioned rudimentically in the other answer).
So check a Linux x64 SYS_CALL list for the appropriate parameters and try
_start:
mov rax, 0 ; set SYS_READ as SYS_CALL value
sub rsp, 8 ; allocate 8-byte space on the stack as read buffer
mov rdi, 0 ; set rdi to 0 to indicate a STDIN file descriptor
lea rsi, [rsp] ; set const char *buf to the 8-byte space on stack
mov rdx, 1 ; set size_t count to 1 for one char
syscall
it appears to work... but the terminal seems to mimick the input I type in again.
No, the 5 + newline that bash reads is the one you typed. Your program waited for input but didn't actually read the input, leaving it in the kernel's terminal input buffer for bash to read after your program exited. (And bash does its own echoing of terminal input because it puts the terminal in no-echo mode before reading; the normal mechanism for characters to appear on the command line as you type is for bash to print what it reads.)
How did your program manage to wait for input without reading any? mov rsi, [rsp-8] loads 8 bytes from that address. You should have used lea to set rsi to point to that location instead of loading what was in that buffer. So read fails with -EFAULT instead of reading anything, but interestingly it doesn't check this until after waiting for there to be some terminal input.
I used strace ./foo to trace system calls made by your program:
execve("./foo", ["./foo"], 0x7ffe90b8e850 /* 51 vars */) = 0
read(0, 5
NULL, 1) = -1 EFAULT (Bad address)
write(1, "Hello, World\n", 13Hello, World
) = 13
exit(0) = ?
+++ exited with 0 +++
Normal terminal input/output is mixed with the strace output; I could have used -o foo.trace or whatever. The cleaned-up version of the read system call trace (without the 5\n mixed in) is:
read(0, NULL, 1) = -1 EFAULT (Bad address)
So (as expected for _start in a static executable under Linux), the memory below RSP was zeroed. But anything that isn't a pointer to writeable memory would have produced the same result.
zx485's answer is correct but inefficient (large code-size and an extra instruction). You don't need to worry about efficiency right away, but it's one of the main reasons for doing anything with asm and there's interesting stuff to say about this case.
You don't need to modify RSP; you can use the red-zone (memory below RSP) because you don't need to make any function calls. This is what you were trying to do with rsp-8, I think. (Or else you didn't realize that it was only safe because of special circumstances...)
The read system call's signature is
ssize_t read(int fd, void *buf, size_t count);
so fd is an integer arg, so it's only looking at edi not rdi. You don't need to write the full rdi, just the regular 32-bit edi. (32-bit operand-size is usually the most efficient thing on x86-64).
But for zero or positive integers, just setting edi also sets rdi anyway. (Anything you write to edi is zero-extended into the full rdi) And of course zeroing a register is best done with xor same,same; this is probably the best-known x86 peephole optimization trick.
As the OP later commented, reading only 1 byte will leave the newline unread, when the input is 5\n, and that would make bash read it and print an extra prompt. We can bump up the size of the read and the space for the buffer to 2 bytes. (There'd be no downside to using lea rsi, [rsp-8] and leave a gap; I'm using lea rsi, [rsp-2] to pack the buffer right below argc on the stack, or below the return value if this was a function instead of a process entry point. Mostly to show exactly how much space is needed.)
; One read of up to 2 characters
; giving the user room to type a digit + newline
_start:
;mov eax, 0 ; set SYS_READ as SYS_CALL value
xor eax, eax ; rax = __NR_read = 0 from unistd_64.h
lea rsi, [rsp-2] ; rsi = buf = rsp-2
xor edi, edi ; edi = fd = 0 (stdin)
mov edx, 2 ; rdx = count = 2 char
syscall ; sys_read(0, rsp-2, 2)
; total = 16 bytes
This assembles like so:
+ yasm -felf64 -Worphan-labels -gdwarf2 foo.asm
+ ld -o foo foo.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400080
$ objdump -drwC -Mintel
0000000000400080 <_start>:
400080: 31 c0 xor eax,eax
400082: 48 8d 74 24 ff lea rsi,[rsp-0x1]
400087: 31 ff xor edi,edi
400089: ba 01 00 00 00 mov edx,0x1
40008e: 0f 05 syscall
; next address = ...90
; I left out the rest of the program so you can't actually *run* foo
; but I used a script that assembles + links, and disassembles the result
; The linking step is irrelevant for just looking at the code here.
By comparison, zx485's answer assembles to 31 bytes. Code size is not the most important thing, but when all else is equal, smaller is better for L1i cache density, and sometimes decode efficiency. (And my version has fewer instructions, too.)
0000000000400080 <_start>:
400080: 48 c7 c0 00 00 00 00 mov rax,0x0
400087: 48 83 ec 08 sub rsp,0x8
40008b: 48 c7 c7 00 00 00 00 mov rdi,0x0
400092: 48 8d 34 24 lea rsi,[rsp]
400096: 48 c7 c2 01 00 00 00 mov rdx,0x1
40009d: 0f 05 syscall
; total = 31 bytes
Note how those mov reg,constant instructions use the 7-byte mov r64, sign_extended_imm32 encoding. (NASM optimizes those to 5-byte mov r32, imm32 for a total of 25 bytes, but it can't optimize mov to xor because xor affects flags; you have to do that optimization yourself.)
Also, if you are going to modify RSP to reserve space, you only need mov rsi, rsp not lea. Only use lea reg1, [rsp] (with no displacement) if you're padding your code with longer instructions instead of using a NOP for alignment. For source registers other than rsp or rbp, lea won't be longer but it is still slower than mov. (But by all means use lea to copy-and-add. I'm just saying it's pointless when you can replace it with a mov.)
You could save even more space by using lea edx, [rax+1] instead of mov edx,1 at essentially no performance cost, but that's not something compilers normally do. (Although perhaps they should.)
You need to set eax to the system call number for read.

Position Independent Code pointing to wrong address

I have a small example program written in NASM(2.11.08) targeting the macho64 architecture. I'm running OSX 10.10.3:
bits 64
section .data
msg1 db 'Message One', 10, 0
msg1len equ $-msg1
msg2 db 'Message Two', 10, 0
msg2len equ $-msg2
section .text
global _main
extern _printf
_main:
sub rsp, 8 ; align
lea rdi, [rel msg1]
xor rax, rax
call _printf
lea rdi, [rel msg2]
xor rax, rax
call _printf
add rsp, 8
ret
I'm compiling and linking using the following command line:
/usr/local/bin/nasm -f macho64 test2.s
ld -macosx_version_min 10.10.0 -lSystem -o test2 test2.o
When I do an object dump on the test2 executable, this is the relevant snippet(I can post more if I'm wrong!):
0000000000001fb7 <_main>:
1fb7: 48 83 ec 08 sub $0x8,%rsp
1fbb: 48 8d 3d 56 01 00 00 lea 0x156(%rip),%rdi # 2118 <msg2+0xf3>
1fc2: 48 31 c0 xor %rax,%rax
1fc5: e8 14 00 00 00 callq 1fde <_printf$stub>
1fca: 48 8d 3d 54 00 00 00 lea 0x54(%rip),%rdi # 2025 <msg2>
1fd1: 48 31 c0 xor %rax,%rax
1fd4: e8 05 00 00 00 callq 1fde <_printf$stub>
1fd9: 48 83 c4 08 add $0x8,%rsp
1fdd: c3 retq
...
0000000000002018 <msg1>:
0000000000002025 <msg2>:
And, finally, the output:
$ ./test2
Message Two
$
My question is, what happened to msg1?
I'm assuming msg1 isn't printed because 0x14f(%rip) is not the correct address (just nulls).
Why is lea edi, [rel msg2] pointing to the correct address, while lea edi, [rel msg1] is pointing past msg2, into NULLs?
It looks like the 0x14f(%rip) offset is exactly 0x100 beyond where msg1 lies in memory (this is true throughout many tests of this problem).
What am I missing here?
Edit: Whichever message (msg1 or msg2) appears last in the .data section is the only message that gets printed.
IDK about the Mach-o ABI, but if it's the same as the SystemV x86-64 ABI GNU/Linux uses, then I think your problem is that you need to clear eax to tell a varargs function like printf that there are zero FP.
Also, lea rdi, [rel msg1] would be a much better choice. As it stands, your code is only position-independent within the low 32bits of virtual address space, because you're truncating the pointers to 32bits.
It appears NASM has a bug. This same problem came up again: NASM 2 lines of db (initialized data) seemingly not working. There, the OP confirmed that the data was present, but labels were wrong, and is hopefully reporting it upstream.

Why does the Solaris assembler generate different machine code than the GNU assembler here?

I wrote this little assembly file for amd64. What the code does is not important for this question.
.globl fib
fib: mov %edi,%ecx
xor %eax,%eax
jrcxz 1f
lea 1(%rax),%ebx
0: add %rbx,%rax
xchg %rax,%rbx
loop 0b
1: ret
Then I proceeded to assemble and then disassemble this on both Solaris and Linux.
Solaris
$ as -o y.o -xarch=amd64 -V y.s
as: Sun Compiler Common 12.1 SunOS_i386 Patch 141858-04 2009/12/08
$ dis y.o
disassembly for y.o
section .text
0x0: 8b cf movl %edi,%ecx
0x2: 33 c0 xorl %eax,%eax
0x4: e3 0a jcxz +0xa <0x10>
0x6: 8d 58 01 leal 0x1(%rax),%ebx
0x9: 48 03 c3 addq %rbx,%rax
0xc: 48 93 xchgq %rbx,%rax
0xe: e2 f9 loop -0x7 <0x9>
0x10: c3 ret
Linux
$ as --64 -o y.o -V y.s
GNU assembler version 2.22.90 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.22.90.20120924
$ objdump -d y.o
y.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <fib>:
0: 89 f9 mov %edi,%ecx
2: 31 c0 xor %eax,%eax
4: e3 0a jrcxz 10 <fib+0x10>
6: 8d 58 01 lea 0x1(%rax),%ebx
9: 48 01 d8 add %rbx,%rax
c: 48 93 xchg %rax,%rbx
e: e2 f9 loop 9 <fib+0x9>
10: c3 retq
How comes the generated machine code is different? Sun as generates 8b cf for mov %edi,%ecx while gas generates 89 f9 for the very same instruction. Is this because of the various ways to encode the same instruction under x86 or do these two encodings really have a particular difference?
Some x86 instructions have multiple encodings that do the same thing. In particular, any instruction that acts on two registers can have the registers swapped and the direction bit in the instruction reversed.
Which one a given assembler/compiler picks simply depends on what the tool authors chose.
You've not specified the operand size for the mov, xor and add operations. This creates some ambiguity. The GNU assembler manual, i386 Mnemonics, mentions this:
If no suffix is specified by an instruction then as tries to fill in the missing suffix based on the destination register operand (the last one by convention). [ ... ] . Note that this is incompatible with the AT&T Unix assembler which assumes that a missing mnemonic suffix implies long operand size.
This implies the GNU assembler chooses differently - it'll pick the opcode with the R/M byte specifying the target operand (because the destination size is known/implied) while the AT&T one chooses the opcode where the R/M byte specifies the source operand (because the operand size is implied).
I've done that experiment though and given explicit operand sizes in your assembly source, and it doesn't change the GNU assembler output. There is, though, the other part of the documentation above,
Different encoding options can be specified via optional mnemonic suffix. `.s' suffix swaps 2 register operands in encoding when moving from one register to another.
which one can use; the following sourcecode, with GNU as, creates me the opcodes you got from Solaris as:
.globl fib
fib: movl.s %edi,%ecx
xorl.s %eax,%eax
jrcxz 1f
leal 1(%rax),%ebx
0: addq.s %rbx,%rax
xchgq %rax,%rbx
loop 0b
1: ret

Linux NASM - rewriting character stored in .data segment - segfault [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
probably a very beginner's question, but I'm really interested in how to make it work.
I have following assembly code (heavily inspired from here, the rename() example):
[SECTION .text]
global _start
_start:
mov esi, msg ; saves pointer to string to ESI
xor eax, eax
mov byte [esi+6], al ; terminates first string with NULL char
mov byte [esi+13], al ; terminates second string with NULL char
mov byte al, 38 ; syscall number (38 = rename)
lea ebx, [esi] ; put the adress of /tmp/a in EBX
lea ecx, [esi+7] ; put the adress of /tmp/b in ECX
int 0x80 ; syscall execution
mov al, 0x01 ; prepare to exit!
xor ebx, ebx
int 0x80 ; exit!
[SECTION .data]
msg: db '/tmp/a#/tmp/b#'
Let me explain it: This program calls syscall rename to rename file /tmp/a to /tmp/b The string in section .data contains name of the source file and name of the target file.
Because I want to avoid NULLs, I decided to put # instead of NULLs and change it on runtime. However, the program terminates with SEGFAULT. It really seems that there is a problem with rewriting the # character(s) in .data segment. My question is - how should I deal with it and make it work? I know it's a beginner question, maybe I'm missing something very important.
Thanks for any advice.
EDIT - commands used for assembling and linking
This is for NASM:
nasm -f elf -o ThisWorks.o ThisWorks.asm
And this for linker (notice that I'm building it as 32bit, although I have 64bit Phenom II).
ld -melf_i386 -o ThisWorks.aout ThisWorks.o
Than I execute it:
./ThisWorks.aout
And the result is:
Segmentation fault
Disassembly
This is disassembly by objdump -D ThisWorks.aout
ThisWorks.aout: file format elf32-i386
Disassembly of section .text:
08048080 <_start>:
8048080: be 9c 90 04 08 mov $0x804909c,%esi
8048085: 31 c0 xor %eax,%eax
8048087: 88 46 06 mov %al,0x6(%esi)
804808a: 88 46 0d mov %al,0xd(%esi)
804808d: b0 26 mov $0x26,%al
804808f: 8d 1e lea (%esi),%ebx
8048091: 8d 4e 07 lea 0x7(%esi),%ecx
8048094: cd 80 int $0x80
8048096: b0 01 mov $0x1,%al
8048098: 31 db xor %ebx,%ebx
804809a: cd 80 int $0x80
Disassembly of section .data:
0804909c <msg>:
804909c: 2f das
804909d: 74 6d je 804910c <_end+0x60>
804909f: 70 2f jo 80490d0 <_end+0x24>
80490a1: 61 popa
80490a2: 23 2f and (%edi),%ebp
80490a4: 74 6d je 8049113 <_end+0x67>
80490a6: 70 2f jo 80490d7 <_end+0x2b>
80490a8: 62 23 bound %esp,(%ebx)
SOLUTION
The debugging has shown, that program works normally, but falls into segfault when there is no file to rename. Otherwise my code works as expected. Sorry for that.
Running your probram under strace show:
execve("./a.out", ["./a.out"], [/* 67 vars */]) = 0
[ Process PID=7054 runs in 32 bit mode. ]
rename("/tmp/a", "/tmp/b") = -1 ENOENT (No such file or directory)
syscall_4294967041(0, 0x80490a3, 0, 0x804909c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = -1 (errno 38)
--- SIGSEGV (Segmentation fault) # 0 (0) ---
+++ killed by SIGSEGV +++
Segmentation fault
Conclusions:
Your problem has nothing to do with rewriting the .data section -- the rename syscall is executed as you would expect.
Your setup for exit is incorrect.
To fix it, change mov al, 0x01 to mov eax, 0x01.
Assuming you are writing shellcode: You cannot access anything in the .data segment from your shellcode, because your shellcode will execute in the process of the exploited piece of software and there will be no data segment there (or more precisely, there is only the data segment of the exploited process). Shellcode has no segmented structure, it is generally just a linear buffer of bytes. In any case, you will have to tuck your data onto the end of your shellcode.

Resources