I'm trying to compile my code into raw binary and as suggested by other SO posts (like this and this) I tried objdump:
$ gcc -c foo.c
$ objcopy -O binary foo.o foo.bin
Then I tried to make sure if this is valid:
$ objdump -d foo.o
foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 89 7d fc mov %edi,-0x4(%rbp)
b: 48 89 75 f0 mov %rsi,-0x10(%rbp)
f: bf 00 00 00 00 mov $0x0,%edi
14: b8 00 00 00 00 mov $0x0,%eax
19: e8 00 00 00 00 callq 1e <main+0x1e>
1e: b8 00 00 00 00 mov $0x0,%eax
23: c9 leaveq
24: c3 ret
$ hexdump -C foo.bin
00000000 14 00 00 00 00 00 00 00 01 7a 52 00 01 78 10 01 |.........zR..x..|
00000010 1b 0c 07 08 90 01 00 00 1c 00 00 00 1c 00 00 00 |................|
00000020 00 00 00 00 25 00 00 00 00 41 0e 10 86 02 43 0d |....%....A....C.|
00000030 06 60 0c 07 08 00 00 00 |.`......|
00000038
Evidently something is wrong. I checked this with the results of a gcc cross-compilation, with much the same obviously incorrect results.
You can pass -j .text to objcopy.
Related
Where are jump tables located in x86 elf code?
progname: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
f: 83 7d fc 09 cmpl $0x9,-0x4(%rbp)
13: 0f 87 ac 00 00 00 ja c5 <main+0xc5>
19: 8b 45 fc mov -0x4(%rbp),%eax
1c: 48 8b 04 c5 00 00 00 mov 0x0(,%rax,8),%rax
23: 00
20: R_X86_64_32S .rodata+0x48
24: ff e0 jmpq *%rax
26: bf 00 00 00 00 mov $0x0,%edi
27: R_X86_64_32 .rodata
2b: b8 00 00 00 00 mov $0x0,%eax
30: e8 00 00 00 00 callq 35 <main+0x35>
31: R_X86_64_PLT32 printf-0x4
35: e9 9b 00 00 00 jmpq d5 <main+0xd5>
3a: bf 00 00 00 00 mov $0x0,%edi
3b: R_X86_64_32 .rodata+0xc
3f: b8 00 00 00 00 mov $0x0,%eax
44: e8 00 00 00 00 callq 49 <main+0x49>
45: R_X86_64_PLT32 printf-0x4
49: e9 87 00 00 00 jmpq d5 <main+0xd5>
4e: bf 00 00 00 00 mov $0x0,%edi
4f: R_X86_64_32 .rodata+0x18
53: b8 00 00 00 00 mov $0x0,%eax
58: e8 00 00 00 00 callq 5d <main+0x5d>
59: R_X86_64_PLT32 printf-0x4
5d: eb 76 jmp d5 <main+0xd5>
5f: bf 00 00 00 00 mov $0x0,%edi
60: R_X86_64_32 .rodata
64: b8 00 00 00 00 mov $0x0,%eax
69: e8 00 00 00 00 callq 6e <main+0x6e>
6a: R_X86_64_PLT32 printf-0x4
6e: eb 65 jmp d5 <main+0xd5>
70: bf 00 00 00 00 mov $0x0,%edi
71: R_X86_64_32 .rodata+0xc
75: b8 00 00 00 00 mov $0x0,%eax
7a: e8 00 00 00 00 callq 7f <main+0x7f>
7b: R_X86_64_PLT32 printf-0x4
7f: eb 54 jmp d5 <main+0xd5>
81: bf 00 00 00 00 mov $0x0,%edi
82: R_X86_64_32 .rodata+0x18
86: b8 00 00 00 00 mov $0x0,%eax
8b: e8 00 00 00 00 callq 90 <main+0x90>
8c: R_X86_64_PLT32 printf-0x4
90: eb 43 jmp d5 <main+0xd5>
92: bf 00 00 00 00 mov $0x0,%edi
93: R_X86_64_32 .rodata
97: b8 00 00 00 00 mov $0x0,%eax
9c: e8 00 00 00 00 callq a1 <main+0xa1>
9d: R_X86_64_PLT32 printf-0x4
a1: eb 32 jmp d5 <main+0xd5>
a3: bf 00 00 00 00 mov $0x0,%edi
a4: R_X86_64_32 .rodata+0xc
a8: b8 00 00 00 00 mov $0x0,%eax
ad: e8 00 00 00 00 callq b2 <main+0xb2>
ae: R_X86_64_PLT32 printf-0x4
b2: eb 21 jmp d5 <main+0xd5>
b4: bf 00 00 00 00 mov $0x0,%edi
b5: R_X86_64_32 .rodata+0x18
b9: b8 00 00 00 00 mov $0x0,%eax
be: e8 00 00 00 00 callq c3 <main+0xc3>
bf: R_X86_64_PLT32 printf-0x4
c3: eb 10 jmp d5 <main+0xd5>
c5: bf 00 00 00 00 mov $0x0,%edi
c6: R_X86_64_32 .rodata+0x24
ca: b8 00 00 00 00 mov $0x0,%eax
cf: e8 00 00 00 00 callq d4 <main+0xd4>
d0: R_X86_64_PLT32 printf-0x4
d4: 90 nop
d5: b8 00 00 00 00 mov $0x0,%eax
da: c9 leaveq
db: c3 retq
I have a basic switch case with 10 cases (including default case). If x=2, then it goes to 2nd block in switch case code.
Need help in understanding where exactly gcc generated jump tables are located in elf files and in which section. I pasted output of objdump -dr progname above.
I've been trying to create an ELF executable using libelf, but haven't gotten it running yet. Whenever I try to run it, I just get a segfault. strace outputs:
execve("./test.elf", ["./test.elf"], 0x7ffc28d60660 /* 63 vars */) = -1 EINVAL (Invalid argument)
+++ killed by SIGSEGV +++
readelf gives:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1000
Start of program headers: 64 (bytes into file)
Start of section headers: 152 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 1
Size of section headers: 64 (bytes)
Number of section headers: 3
Section header string table index: 2
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000001000 00000078
0000000000000008 0000000000000000 AX 0 0 1
[ 2] .shstrtab STRTAB 0000000000000000 00000080
0000000000000011 0000000000000000 A 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000078 0x0000000000001000 0x0000000000001000
0x0000000000000008 0x0000000000000008 R E 0x1
Section to Segment mapping:
Segment Sections...
00 .text
There is no dynamic section in this file.
There are no relocations in this file.
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
No version information found in this file.
And here's the hexdump of the file:
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 02 00 3e 00 01 00 00 00 00 10 00 00 00 00 00 00 |..>.............|
00000020 40 00 00 00 00 00 00 00 98 00 00 00 00 00 00 00 |#...............|
00000030 00 00 00 00 40 00 38 00 01 00 40 00 03 00 02 00 |....#.8...#.....|
00000040 01 00 00 00 05 00 00 00 78 00 00 00 00 00 00 00 |........x.......|
00000050 00 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 |................|
00000060 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 |................|
00000070 01 00 00 00 00 00 00 00 31 c0 ff c0 b3 2a cd 80 |........1....*..|
00000080 00 2e 74 65 78 74 00 2e 73 68 73 74 72 74 61 62 |..text..shstrtab|
00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000000d0 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 |................|
000000e0 06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 |................|
000000f0 78 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 |x...............|
00000100 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
00000110 00 00 00 00 00 00 00 00 07 00 00 00 03 00 00 00 |................|
00000120 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000130 80 00 00 00 00 00 00 00 11 00 00 00 00 00 00 00 |................|
00000140 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
00000150 00 00 00 00 00 00 00 00 |........|
00000158
From what I've read and comparing it to working executables, this should work. But it doesn't, and I have no idea why. Any help is appreciated, thanks.
I figured it out! When I was looking at a working executable for reference, I was looking at a PIE, but I was trying to create a non-PIE. I didn't realize the EHDR type needs to be ET_DYN for a PIE. Also, a few of the addresses were wrong.
The new readelf:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1078
Start of program headers: 64 (bytes into file)
Start of section headers: 152 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 1
Size of section headers: 64 (bytes)
Number of section headers: 3
Section header string table index: 2
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000001078 00000078
0000000000000008 0000000000000000 AX 0 0 1
[ 2] .shstrtab STRTAB 0000000000000000 00000080
0000000000000011 0000000000000000 A 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000078 0x0000000000001078 0x0000000000001078
0x0000000000000008 0x0000000000000008 R E 0x1
Section to Segment mapping:
Segment Sections...
00 .text
There is no dynamic section in this file.
There are no relocations in this file.
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
No version information found in this file.
And the new hexdump:
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 03 00 3e 00 01 00 00 00 78 10 00 00 00 00 00 00 |..>.....x.......|
00000020 40 00 00 00 00 00 00 00 98 00 00 00 00 00 00 00 |#...............|
00000030 00 00 00 00 40 00 38 00 01 00 40 00 03 00 02 00 |....#.8...#.....|
00000040 01 00 00 00 05 00 00 00 78 00 00 00 00 00 00 00 |........x.......|
00000050 78 10 00 00 00 00 00 00 78 10 00 00 00 00 00 00 |x.......x.......|
00000060 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 |................|
00000070 01 00 00 00 00 00 00 00 31 c0 ff c0 b3 2a cd 80 |........1....*..|
00000080 00 2e 74 65 78 74 00 2e 73 68 73 74 72 74 61 62 |..text..shstrtab|
00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000000d0 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 |................|
000000e0 06 00 00 00 00 00 00 00 78 10 00 00 00 00 00 00 |........x.......|
000000f0 78 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 |x...............|
00000100 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
00000110 00 00 00 00 00 00 00 00 07 00 00 00 03 00 00 00 |................|
00000120 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000130 80 00 00 00 00 00 00 00 11 00 00 00 00 00 00 00 |................|
00000140 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
00000150 00 00 00 00 00 00 00 00 |........|
00000158
Assembling the following code on macOS:
global start
default rel
section .text
start:
lea rdx, [buffer + 0]
lea rdx, [buffer + 1]
lea rdx, [buffer + 2]
lea rdx, [buffer + 3]
lea rdx, [buffer + 4]
lea rdx, [buffer + 5]
lea rdx, [buffer + 6]
lea rdx, [buffer + 7]
lea rdx, [buffer + 8]
section .data
buffer: db 0,0,0
using the command nasm -fmacho64 -w+all test.asm -o test.o, yields: (with gobjdump -d test.o)
0000000000000000 <start>:
0: 48 8d 15 38 00 00 00 lea 0x38(%rip),%rdx # 3f <buffer>
7: 48 8d 15 32 00 00 00 lea 0x32(%rip),%rdx # 40 <buffer+0x1>
e: 48 8d 15 2c 00 00 00 lea 0x2c(%rip),%rdx # 41 <buffer+0x2>
15: 48 8d 15 26 00 00 00 lea 0x26(%rip),%rdx # 42 <buffer+0x3>
1c: 48 8d 15 20 00 00 00 lea 0x20(%rip),%rdx # 43 <buffer+0x4>
23: 48 8d 15 1a 00 00 00 lea 0x1a(%rip),%rdx # 44 <buffer+0x5>
2a: 48 8d 15 14 00 00 00 lea 0x14(%rip),%rdx # 45 <buffer+0x6>
31: 48 8d 15 0e 00 00 00 lea 0xe(%rip),%rdx # 46 <buffer+0x7>
38: 48 8d 15 08 00 00 00 lea 0x8(%rip),%rdx # 47 <buffer+0x8>
This looks correct. When then linking that into an executable using ld test.o -o test, we get: (with gobjdump -d test)
0000000000001fc1 <start>:
1fc1: 48 8d 15 38 00 00 00 lea 0x38(%rip),%rdx # 2000 <buffer>
1fc8: 48 8d 15 32 00 00 00 lea 0x32(%rip),%rdx # 2001 <buffer+0x1>
1fcf: 48 8d 15 2c 00 00 00 lea 0x2c(%rip),%rdx # 2002 <buffer+0x2>
1fd6: 48 8d 15 23 00 00 00 lea 0x23(%rip),%rdx # 2000 <buffer>
1fdd: 48 8d 15 1d 00 00 00 lea 0x1d(%rip),%rdx # 2001 <buffer+0x1>
1fe4: 48 8d 15 17 00 00 00 lea 0x17(%rip),%rdx # 2002 <buffer+0x2>
1feb: 48 8d 15 11 00 00 00 lea 0x11(%rip),%rdx # 2003 <buffer+0x3>
1ff2: 48 8d 15 0b 00 00 00 lea 0xb(%rip),%rdx # 2004 <buffer+0x4>
1ff9: 48 8d 15 05 00 00 00 lea 0x5(%rip),%rdx # 2005 <buffer+0x5>
And suddenly, unexpectedly, buffer + n gets changed into buffer + (n - 3) if n >= 3. The nasm version I'm using is 2.13.01 and the ld is macOS Sierra's system linker, with ld -v giving:
#(#)PROGRAM:ld PROJECT:ld64-274.2
configured to support archs: armv6 armv7 armv7s arm64 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em (tvOS)
LTO support using: LLVM version 8.0.0, (clang-800.0.42.1)
TAPI support using: Apple TAPI version 1.30
Why is this happening? Note that the same code seems to assemble and link fine on linux, as well as using yasm. In other places I read that nasm has some weird problems with the Mach-O 64 format, so this is probably a nasm bug in that area.
This seems to be fixed in nasm version 2.13.03, as tested today. (Also in 2.13.02, but that version has other problems with the macho64 target anyway, re #MichaelPetch; .02's output is equal in this case to .03's.) The output is as follows for the test.asm and commands given in the question.
nasm -fmacho64 -w+all test.asm -o test.o; gobjdump -d test.o:
test.o: file format mach-o-x86-64
Disassembly of section .text:
0000000000000000 <start>:
0: 48 8d 15 00 00 00 00 lea 0x0(%rip),%rdx # 7 <start+0x7>
7: 48 8d 15 01 00 00 00 lea 0x1(%rip),%rdx # f <start+0xf>
e: 48 8d 15 02 00 00 00 lea 0x2(%rip),%rdx # 17 <start+0x17>
15: 48 8d 15 03 00 00 00 lea 0x3(%rip),%rdx # 1f <start+0x1f>
1c: 48 8d 15 04 00 00 00 lea 0x4(%rip),%rdx # 27 <start+0x27>
23: 48 8d 15 05 00 00 00 lea 0x5(%rip),%rdx # 2f <start+0x2f>
2a: 48 8d 15 06 00 00 00 lea 0x6(%rip),%rdx # 37 <start+0x37>
31: 48 8d 15 07 00 00 00 lea 0x7(%rip),%rdx # 3f <buffer>
38: 48 8d 15 08 00 00 00 lea 0x8(%rip),%rdx # 47 <buffer+0x8>
ld test.o -o test; gobjdump -d test:
test: file format mach-o-x86-64
Disassembly of section .text:
0000000000001fc1 <start>:
1fc1: 48 8d 15 38 00 00 00 lea 0x38(%rip),%rdx # 2000 <buffer>
1fc8: 48 8d 15 32 00 00 00 lea 0x32(%rip),%rdx # 2001 <buffer+0x1>
1fcf: 48 8d 15 2c 00 00 00 lea 0x2c(%rip),%rdx # 2002 <buffer+0x2>
1fd6: 48 8d 15 26 00 00 00 lea 0x26(%rip),%rdx # 2003 <buffer+0x3>
1fdd: 48 8d 15 20 00 00 00 lea 0x20(%rip),%rdx # 2004 <buffer+0x4>
1fe4: 48 8d 15 1a 00 00 00 lea 0x1a(%rip),%rdx # 2005 <buffer+0x5>
1feb: 48 8d 15 14 00 00 00 lea 0x14(%rip),%rdx # 2006 <buffer+0x6>
1ff2: 48 8d 15 0e 00 00 00 lea 0xe(%rip),%rdx # 2007 <buffer+0x7>
1ff9: 48 8d 15 08 00 00 00 lea 0x8(%rip),%rdx # 2008 <buffer+0x8>
Worth noting is that my ld has updated and ld -v now gives:
#(#)PROGRAM:ld PROJECT:ld64-305
configured to support archs: armv6 armv7 armv7s arm64 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em (tvOS)
LTO support using: LLVM version 9.0.0, (clang-900.0.39.2) (static support for 21, runtime is 21)
TAPI support using: Apple TAPI version 900.0.15 (tapi-900.0.15)
I want to reverse old .exe file; I'm 90% sure it is Delphi (class names are being with 'T' => "TCommonDialog")
I can't load file into IDR (becouse it is not valid PE-executable?) jet still .exe works just fine and icon is showing just right.
I was trying to maniupulate header but every time I just corrupt .exe
Header with MZ:
00000000 4d 5a 00 01 01 00 00 00 08 00 10 00 ff ff 08 00 |MZ..............|
00000010 00 01 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........#.......|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080
Further is longer header, but with NE; I was trying to change it to PE.
At this point I don't know what am I doing, I just mess with everything
00000000 4e 45 06 01 17 07 5a 00 00 00 00 00 0a 03 16 00 |NE....Z.........|
00000010 00 20 00 40 0e 00 01 00 00 00 16 00 16 00 0c 00 |. .#............|
00000020 0e 00 40 00 f0 00 9f 06 a9 06 c1 06 71 08 00 00 |..#.........q...|
00000030 0e 00 04 00 00 00 02 00 00 00 00 00 00 00 0a 03 |................|
00000040 e4 36 cc 3d 10 1d cd 3d e5 3a 47 3e 10 1d 47 3e |.6.=...=.:G>..G>|
00000050 d3 3e 6f 3f 10 1d 70 3f d6 42 96 3f 10 1d 97 3f |.>o?..p?.B.?...?|
00000060 d9 46 20 3a 10 1d 21 3a 8f 4a 09 33 10 1d 0a 33 |.F :..!:.J.3...3|
00000070 cd 4d 99 30 10 1d 9a 30 df 50 1c 33 10 1d 1c 33 |.M.0...0.P.3...3|
00000080 17 54 7b 9a 10 1d 7b 9a d3 5d 33 3c 10 1d 33 3c |.T{...{..]3<..3<|
00000090 90 00 17 3a 50 1d 17 3a 57 04 c1 2f 50 1d c1 2f |...:P..:W../P../|
000000a0 5b 07 2c 41 50 1d 2d 41 77 0b b9 66 50 1d ba 66 |[.,AP.-Aw..fP..f|
000000b0 f5 11 28 70 50 1d 28 70 1f 19 cc 22 50 1d cc 22 |..(pP.(p..."P.."|
000000c0 55 1b 00 6f 50 1d 00 6f 6c 22 84 7a 50 1d 85 7a |U..oP..ol".zP..z|
000000d0 45 2a 31 51 50 1d 31 51 65 2f 8d 30 50 0d 8d 30 |E*1QP.1Qe/.0P..0|
000000e0 99 32 c6 1f 50 0d c6 1f eb 34 5d 1f 59 0d 8c 2f |.2..P....4].Y../|
000000f0 04 00 03 80 01 00 00 00 00 00 a9 61 30 00 10 1c |...........a0...|
00000100 01 80 00 00 00 00 0e 80 01 00 00 00 00 00 d9 61 |...............a|
00000110 10 00 10 1c e4 03 00 00 00 00 0a 80 18 00 00 00 |................|
00000120 00 00 e9 61 30 00 30 1c ed 03 00 00 00 00 19 62 |...a0.0........b|
00000130 10 11 30 1c f1 03 00 00 00 00 29 73 80 04 30 1c |..0.......)s..0.|
00000140 fb 03 00 00 00 00 a9 77 40 00 30 1c 04 04 00 00 |.......w#.0.....|
00000150 00 00 e9 77 f0 04 30 1c 11 04 00 00 00 00 d9 7c |...w..0........||
00000160 30 00 30 1c 1c 04 00 00 00 00 09 7d 80 00 30 1c |0.0........}..0.|
00000170 25 04 00 00 00 00 89 7d e0 00 30 1c 39 04 00 00 |%......}..0.9...|
00000180 00 00 69 7e 40 00 30 1c 48 04 00 00 00 00 a9 7e |..i~#.0.H......~|
00000190 c0 01 30 1c 54 04 00 00 00 00 69 80 d0 05 30 1c |..0.T.....i...0.|
000001a0 5b 04 00 00 00 00 39 86 50 00 30 1c 65 04 00 00 |[.....9.P.0.e...|
000001b0 00 00 89 86 60 00 30 1c 72 04 00 00 00 00 e9 86 |....`.0.r.......|
000001c0 50 00 30 1c 80 04 00 00 00 00 39 87 50 00 30 1c |P.0.......9.P.0.|
000001d0 8f 04 00 00 00 00 89 87 40 00 30 1c 9c 04 00 00 |........#.0.....|
000001e0 00 00 c9 87 50 00 30 1c a9 04 00 00 00 00 19 88 |....P.0.........|
000001f0 40 00 30 1c b6 04 00 00 00 00 59 88 50 00 30 1c |#.0.......Y.P.0.|
00000200 c3 04 00 00 00 00 a9 88 50 00 30 1c d1 04 00 00 |........P.0.....|
00000210 00 00 f9 88 50 00 30 1c de 04 00 00 00 00 49 89 |....P.0.......I.|
00000220 40 00 30 1c eb 04 00 00 00 00 89 89 70 01 30 1c |#.0.........p.0.|
00000230 f8 04 00 00 00 00 f9 8a 90 00 30 1c 00 05 00 00 |..........0.....|
00000240 00 00 02 80 17 00 00 00 00 00 89 8b 10 00 30 1c |..............0.|
00000250 05 05 00 00 00 00 99 8b 10 00 30 1c 0d 05 00 00 |..........0.....|
00000260 00 00 a9 8b 10 00 30 1c 18 05 00 00 00 00 b9 8b |......0.........|
00000270 10 00 30 1c 1f 05 00 00 00 00 c9 8b 10 00 30 1c |..0...........0.|
00000280 29 05 00 00 00 00 d9 8b 10 00 30 1c 33 05 00 00 |).........0.3...|
00000290 00 00 e9 8b 10 00 30 0c 3c 05 00 00 00 00 f9 8b |......0.<.......|
000002a0 10 00 30 0c 45 05 00 00 00 00 09 8c 10 00 30 1c |..0.E.........0.|
000002b0 4c 05 00 00 00 00 19 8c 10 00 30 1c 51 05 00 00 |L.........0.Q...|
000002c0 00 00 29 8c 10 00 30 1c 57 05 00 00 00 00 39 8c |..)...0.W.....9.|
000002d0 10 00 30 1c 5c 05 00 00 00 00 49 8c 10 00 30 1c |..0.\.....I...0.|
000002e0 63 05 00 00 00 00 59 8c 20 00 30 1c 68 05 00 00 |c.....Y. .0.h...|
000002f0 00 00 79 8c 20 00 30 1c 6f 05 00 00 00 00 99 8c |..y. .0.o.......|
00000300 20 00 30 1c 74 05 00 00 00 00 b9 8c 20 00 30 1c | .0.t....... .0.|
00000310 79 05 00 00 00 00 d9 8c 20 00 30 1c 7f 05 00 00 |y....... .0.....|
00000320 00 00 f9 8c 20 00 30 1c 88 05 00 00 00 00 19 8d |.... .0.........|
00000330 20 00 30 1c 90 05 00 00 00 00 39 8d 20 00 30 1c | .0.......9. .0.|
00000340 98 05 00 00 00 00 59 8d 20 00 30 1c 9e 05 00 00 |......Y. .0.....|
00000350 00 00 79 8d 20 00 30 1c a6 05 00 00 00 00 01 80 |..y. .0.........|
00000360 06 00 00 00 00 00 99 8d 20 00 30 1c 01 80 00 00 |........ .0.....|
00000370 00 00 c9 8d 20 00 30 1c 02 80 00 00 00 00 f9 8d |.... .0.........|
00000380 20 00 30 1c 03 80 00 00 00 00 29 8e 20 00 10 1c | .0.......). ...|
00000390 04 80 00 00 00 00 59 8e 20 00 10 1c 05 80 00 00 |......Y. .......|
000003a0 00 00 89 8e 20 00 30 1c 06 80 00 00 00 00 0c 80 |.... .0.........|
000003b0 06 00 00 00 00 00 b9 8d 10 00 30 1c fb ff 00 00 |..........0.....|
000003c0 00 00 e9 8d 10 00 30 1c fc ff 00 00 00 00 19 8e |......0.........|
000003d0 10 00 30 1c fd ff 00 00 00 00 49 8e 10 00 30 1c |..0.......I...0.|
000003e0 fe ff 00 00 00 00 79 8e 10 00 30 1c ff ff 00 00 |......y...0.....|
000003f0 00 00 a9 8e 10 00 30 1c fa ff 00 00 00 00 06 80 |......0.........|
00000400 11 00 00 00 00 00 b9 8e 20 00 30 1c 01 8f 00 00 |........ .0.....|
00000410 00 00 d9 8e 20 00 30 1c 02 8f 00 00 00 00 f9 8e |.... .0.........|
00000420 20 00 30 1c 03 8f 00 00 00 00 19 8f 20 00 30 1c | .0......... .0.|
00000430 04 8f 00 00 00 00 39 8f 20 00 30 1c 05 8f 00 00 |......9. .0.....|
00000440 00 00 59 8f 20 00 30 1c 06 8f 00 00 00 00 79 8f |..Y. .0.......y.|
00000450 20 00 30 1c 07 8f 00 00 00 00 99 8f 10 00 30 1c | .0...........0.|
00000460 08 8f 00 00 00 00 a9 8f 10 00 30 1c 09 8f 00 00 |..........0.....|
00000470 00 00 b9 8f 20 00 30 1c 0a 8f 00 00 00 00 d9 8f |.... .0.........|
00000480 20 00 30 1c 0b 8f 00 00 00 00 f9 8f 20 00 30 1c | .0......... .0.|
00000490 f9 8f 00 00 00 00 19 90 20 00 30 1c fa 8f 00 00 |........ .0.....|
000004a0 00 00 39 90 10 00 30 1c fb 8f 00 00 00 00 49 90 |..9...0.......I.|
000004b0 10 00 30 1c fd 8f 00 00 00 00 59 90 10 00 30 1c |..0.......Y...0.|
000004c0 fe 8f 00 00 00 00 69 90 10 00 30 1c ff 8f 00 00 |......i...0.....|
000004d0
If you look at the information provided by fileformat.info, you'll see that this is very likely an NE executable. These also start with MZ, but the rest is different.
Reading the bytes at offsets 0000000C and 0000000D, this is probably a Windows 3.x Protected Mode program. If it is made with Delphi, that can only have been Delphi 1, which did not produce PE executables, but 16 bit Windows executables instead.
I am in the process of learning Assembly, and I tried writing my own bootloader. It works fine on VirtualBox , but it doesn't work on a actual PC.
On pc 'Hello World!' doesn't get printed.
This is the code is use:
BITS 16
ORG 0x7C00
jmp boot_sector
;------------------------------
OEMLabel db "FLOPPYDR"
BytesPerSector dw 512
SectorsPerCluster db 1
ReservedForBoot dw 1
NumberOfFats db 2
RootDirEntries dw 224
LogicalSectors dw 2880
MediumByte db 0xF0
SectorsPerFat dw 9
SectorsPerTrack dw 18
Sides dw 2
HiddenSectors dd 0
LargeSectors dd 0
DriveNo dw 0
Signature db 41
VolumeID dd 0x00
VolumeLabel db "FLOPPYDRIVE"
FileSystem db "FAT12"
;##############################
boot_sector:
;##############################
mov ax, 0x0000 ; Set up the stack
mov ss, ax ; Is this done correctly?
mov sp, 0x7C00 ; (I dont quite understand)
int 0x10 ; Set video mode
int 0x13 ; Reset the drive
mov ah, 0x02 ; Read more sectors
mov al, 2 ; Read two extra sectors,
mov bx, main_sector ; starting from the second.
mov ch, 0 ;
mov cl, 2 ; dl has been set already (?)
mov dh, 0 ;
int 0x13 ;
mov [bootdev], dl ; Store original dl in bootdev
jmp main_sector ; Go to the main sector (0x200 I think)
times 510 - ($ - $$) db 0 ; Fill in the rest of the sector with 0s
dw 0xAA55 ; and 0xAA55 at the end for signature
;##############################
main_sector:
;##############################
jmp Start
;------------------------------
bootdev db 0
msg db 'Hello World!', 10, 13, 0
;------------------------------
print_string:
mov ah, 0x0E
mov bh, 0
cmp al, 0
jne .loop
mov bl, 0x0F
.loop:
lodsb
cmp al, 0
je .end
int 0x10
jmp .loop
.end:
ret
;------------------------------
Start:
mov si, msg
call print_string
hlt
times 512 - ($ - main_sector) db 0
I've also commented in some questions, but these are not my main question (well, maybe me not knowing the answer causes the problem). Why doesn't this work on a real PC?
To compile I use nasm -f bin boot.asm -o boot.bin and to create a virtual floppydisk file I use mkfile 1474560 floppy.flp
Then I open up floppy.flp using HexEdit and replace the first 64 lines (0x00 - 0x3F) with the content of the boot.bin file (opened using HexEdit).
E9 38 00 46 4C 4F 50 50 59 44 52 00 02 01 01 00
02 E0 00 40 0B F0 09 00 12 00 02 00 00 00 00 00
00 00 00 00 00 00 29 00 00 00 00 46 4C 4F 50 50
59 44 52 49 56 45 46 41 54 31 32 B8 00 00 8E D0
BC 00 7C CD 10 CD 13 B4 02 B0 02 BB 00 7E B5 00
B1 02 B6 00 CD 13 88 16 03 7E E9 A3 01 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA <-- End of first sector
E9 24 00 00 48 65 6C 6C 6F 20 57 6F 72 6C 64 21
0A 0D 00 B4 0E B7 00 3C 00 75 02 B3 0F AC 3C 00
74 04 CD 10 EB F7 C3 BE 04 7E E8 E6 FF F4 00 00 (The rest is just 0's).
This is the Terminal when I burn floppy.flp to the USB drive:
Last login: Wed Sep 23 12:10:48 on ttys000
MacBook-Air:~ sasha$ cd ~/Desktop
MacBook-Air:Desktop sasha$ diskutil list
/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *121.3 GB disk0
1: Apple_HFS 209.7 MB disk0s1
2: Apple_CoreStorage 120.5 GB disk0s2
3: Apple_Boot Recovery HD 650.0 MB disk0s3
/dev/disk1
#: TYPE NAME SIZE IDENTIFIER
0: Apple_HFS Macintosh HD *120.1 GB disk1
Logical Volume on disk0s2
8CD6A846-395D-4C97-A5DE-0A7ABA9F1C99
Unencrypted
/dev/disk2
#: TYPE NAME SIZE IDENTIFIER
0: Apple_partition_scheme *17.1 MB disk2
1: Apple_partition_map 32.3 KB disk2s1
2: Apple_HFS Flash Player 17.1 MB disk2s2
/dev/disk3
#: TYPE NAME SIZE IDENTIFIER
0: FLOPPYDRIVE *1.0 GB disk3
MacBook-Air:Desktop sasha$ diskutil unmountdisk /dev/disk3
Unmount of all volumes on disk3 was successful
MacBook-Air:Desktop sasha$ sudo dd bs=512 if=floppy.flp of=/dev/disk3
Password:
2880+0 records in
2880+0 records out
1474560 bytes transferred in 0.843982 secs (1747146 bytes/sec)
MacBook-Air:Desktop sasha$
I've written about bootloaders at some length recently on Stackoverflow. Most issues that involve the situation where it works on one emulator or VM but not on another (or physical hardware) usually come down to making false assumptions about the state of the segment registers when the BIOS jumps to your code. Under some emulators the segment registers may have more sane values in them, but that usually isn't the case. From my previous answer I had these two tips that seem to apply here:
When the BIOS jumps to your code you can't rely on DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts.
The direction flag used by lodsb, movsb etc could be either set or cleared. If the direction flag is set improperly SI/DI registers may be adjusted in the wrong direction. Use STD/CLD to set it to the direction you wish (CLD=forward/STD=backwards). In this case the code assumes forward movement so one should use CLD. More on this can be found in an instruction set reference
Your assembly code is set to compile and link assuming an origin point of 0x7C00 (via ORG 0x7C00). Your code that accesses variables like msg and bootdev will be made with the assumption that their memory address will be absolute within the segment (DS). This means that if you have an invalid DS segment then you could be addressing variables, data, and labels at the wrong locations. As an example:
mov [bootdev], dl
has an implicit reference to DS and is equivalent to addressing it with an explicit DS segment:
mov [ds:bootdev], dl
If DS has some random value in it then you'll likely be accessing memory in places you don't expect. For some environments DS might just be zero so your code will work.
How do you know which segment to use? The bootloader is loaded by the BIOS at physical memory 0x0000:0x7C00(segment:offset) . Your origin point (Set with ORG directive) matches the offset so that means in your case DS should be set to zero.
In your code ES should also be set to zero. The reason is that INT 0x13 AH=0x02 (disk read) says:
ES:BX Buffer Address Pointer
Imagine if ES is set to random garbage, the disk read will likely read into memory you didn't intend. So just like DS, ES must also be set. You've written your bootloader and kernel in the same file with the origin point of 0x7C00 so again you just need to use an ES segment set to zero.
When setting up your stack you can set ES and DS appropriately.
mov ax, 0x0000 ; Set up the stack
mov ss, ax ; Is this done correctly?
mov sp, 0x7C00 ; (I dont quite understand)
mov ds, ax ; Set DS to 0 because that is what your code needs
mov es, ax ; ES the same as DS.
cld ; Read my tip #2
You did ask if you set your stack properly. There is nothing wrong with it. Your instructions effectively set up a a stack that grows downward from 0x0000:0x7C00 just below the area occupied by your bootloader. That leaves about 27kb (0x7C00-0x1000) of stack space. 4k is plenty for BIOS calls and your current code. The first 0x1000 of memory are generally for interrupt table/BIOS data area etc.
One other bug I noticed in your code was when you try to reset the disk drive:
int 0x10 ; Set video mode
int 0x13 ; Reset the drive
You set AX to zero above these 2 lines. INT 0x10 AH=0x00 (set video mode) has the side effect of returning information in AX. Since AX can be clobbered your call to INT 0x13 AH=0x00 will likely be wrong. You need to clear AH (or all of AX) prior to calling int 0x13 to reset the drive. The code should look like:
int 0x10 ; Set video mode
xor ax,ax ; clear AX (AH=0)
int 0x13 ; Reset the drive
There is a minor issue at the top of your program and would likely only be a problem if you put this bootloader on a properly formatted FAT12 disk image and tried to mount it in your OS. You have:
jmp boot_sector
;------------------------------
OEMLabel db "FLOPPYDR"
The disk structure you have within your bootloader should have OEMLabel starting from the 4th byte. jmp boot_sector could be encoded as a 2 or 3 byte instruction by NASM. Use short to force a 2 byte encoding followed by a NOP (1 byte instruction). This will place OEMLabel at the 4th byte in the file. It could look like this:
jmp short boot_sector
nop
;------------------------------
OEMLabel db "FLOPPYDR"
Alternatively you can encode a JMP that may be 2 or 3 bytes when encoded and pad it with NOP if necessary using NASM's TIMES directive so that OEMLabel always starts at the 4th byte:
jmp boot_sector
times 3-($-$$) nop
;------------------------------
OEMLabel db "FLOPPYDR"
A trick to avoid using hexedit to manually insert the bootloader code at the beginning of a disk image is to use dd. You can use dd to overwrite the first 1024 bytes and keep the rest intact. Try dd if=boot.bin of=floppy.flp bs=512 count=2 conv=notrunc . This should open up floppy.flp write 2 512 byte sectors containing the 1024 bytes from boot.bin without truncating the file (conv=notrunc)
When a bootloader is started the contents of almost all registers (including segment registers) is "undefined". The only register that actually does have a known value is DL (which contains the BIOS' drive number for the disk).
All instructions that refer to memory use an implied or explicit segment register. For example, both mov [bootdev], dl and lodsb rely on the (implied) DS segment register, which is never set and is still undefined.
Like all undefined values, it's possible (due to pure luck) for them to be a value that makes things work by accident. Your code would work if the BIOS happened to leave the value 0x0000 in DS.