You are not logged in.

#1 2019-09-14 20:57:18

eight.bit.al
Member
From: The top of the World
Registered: 2015-10-01
Posts: 370

Disk Editor/Disassembler - (LONG)

Back in the early eighties I spent many long hours with disk editors/disassemblers cracking copy protections schemes. I did it for a couple of reasons; 1) for the preservation of my equipment; 2) thirst for knowledge, and lastly 3) stick it to the man. Let me explain the first one.

In the Commodore 64/128 ecosystem, the disk drive is an intelligent peripheral with it own operating system. A common copy protection scheme was(is?) to place a deliberate error on a disk sector; there were maybe almost a dozen types of errors. The program called the drive to read that sector and the drive would return the error message stating the type of error. As you are guessing, if the error type matched, the program would continue to load. Here comes the rub.

When the drive tried to read the errant sector, the drive OS would freak and bang the read head against the stopper thinking maybe it had lost track of where it was on the magnetic surface, and then return the error type. Many drives were knocked out of alignment in the process. It was a well known issue and I realigned many drives for friends. Not my drives, not then, not ever.  tongue

Long story short:

I want to study the code in boot sectors, I'll explain why if anyone want to know. But I'm having trouble finding tools. Plenty of disk editors, but the disassembly part leaves me flat. Programming on today's computers is way more involved than back in the day and I don't want to spend the time learning some complicated IDE. Just disassemble the code to a text document and I'll take it from there. Or am I asking too much.

Also, I've read the sources from several distros, but it doesn't match what i find in the boot sector using

dd if=/dev/hda of=mbr.bin bs=512 count=1

and then reading it with a hex editor. Starman has some good stuff to read for example, but is out of date.

Any help?

Thanks for reading.

8bit

“When I dare to be powerful – to use my strength in the service of my vision, then it becomes less and less important whether I am afraid.” - Audre Lorde


A mind is a terrible thing not to change.
The problem lies not with new ideas, but in escaping the old ones.

Offline

#2 2019-09-14 21:36:06

twoion
ほやほや
Registered: 2015-08-10
Posts: 2,553

Re: Disk Editor/Disassembler - (LONG)

Are you talking about dumping MBR or GPT partition tables or about disassembling bootloader code for x86 MBRs?


At the end of the river the sundown beams

Offline

#3 2019-09-14 21:56:40

eight.bit.al
Member
From: The top of the World
Registered: 2015-10-01
Posts: 370

Re: Disk Editor/Disassembler - (LONG)

Disassembling the boot sector code from a file generated buy the dd line listed above. Or any other approach that might be better.

Back in the day, I had a program that would disassemble from the drive. One could cruse around the disk looking for code between the non code data, and follow the chain from sector to sector, filling a buffer to print it out later.

8bit

smile

Last edited by eight.bit.al (2019-09-14 23:35:12)


A mind is a terrible thing not to change.
The problem lies not with new ideas, but in escaping the old ones.

Offline

#4 2019-09-15 10:53:01

twoion
ほやほや
Registered: 2015-08-10
Posts: 2,553

Re: Disk Editor/Disassembler - (LONG)

How about writing a MBR parser based on https://wiki.osdev.org/MBR_(x86), that is, start from the spec partsing your image then go deeper? Because it interests me I threw something quick together (not very good code, maybe bugs). The bootloader is only the 440 to 446 bytes, the rest is the partition table which we can dump:

#!/usr/bin/env python3

from dataclasses import dataclass
import struct
import sys

buf = open(sys.argv[1], 'rb').read()

# Parse istruct
# https://wiki.osdev.org/MBR_(x86)

@dataclass
class MBRPartition:
    image: bytes
    bootable: bool = False
    driveattributes: bytes = None
    chsstart: int = 0
    type: int = 0
    chslast: int = 0
    lbastart: int = 0
    lbaend: int = 0
    sectors: int = 0

    def __post_init__(self):
        self.bootable = struct.unpack_from("<B",self.image,offset=0)[0] & (0x1<<6)
        self.driveattributes = self.image[0]
        self.chsstart = struct.unpack("<I",(self.image[0x1:0x4]+b"\x00"))[0]
        self.type = self.image[0x4]
        self.chslast = struct.unpack("<I", (self.image[0x05:0x08]+b"\x00"))[0]
        self.lbastart = struct.unpack("<I",self.image[0x08:0x0c])[0]
        self.sectors = struct.unpack("<I",self.image[0x0c:])[0]
        self.lbaend = self.lbastart + self.sectors # is this wrong?

@dataclass
class MBR:
    bootstrap : bytes
    diskId    : bytes
    reserved  : bytes
    # Order not guaranteed to be physical
    part1     : bytes
    part2     : bytes
    part3     : bytes
    part4     : bytes
    signature : bytes
    partitions : list = None

    def __post_init__(self):
        self.__validate()
        self.__parse_partitions()

    def __validate(self):
        assert len(self.bootstrap) == 440
        assert len(self.diskId)    == 4
        assert len(self.reserved)  == 2
        assert len(self.part1)     == 16
        assert len(self.part2)     == 16
        assert len(self.part3)     == 16
        assert len(self.part4)     == 16
        assert self.signature      == b"\x55\xaa"

    def __parse_partitions(self):
        self.partitions = [
            MBRPartition(image=self.part1),
            MBRPartition(image=self.part2),
            MBRPartition(image=self.part3),
            MBRPartition(image=self.part4),
        ]

mbr = MBR(
        bootstrap = buf[0:0x1b8],
        diskId    = buf[0x1b8:0x1bc],
        reserved  = buf[0x1bc:0x1be],
        part1     = buf[0x1be:0x1ce],
        part2     = buf[0x1ce:0x1de],
        part3     = buf[0x1de:0x1ee],
        part4     = buf[0x1ee:0x1fe],
        signature = buf[0x1fe:0x200]
)

print("==> Disk ID")
print(hex(struct.unpack("<I",mbr.diskId)[0]))

print("==> Partitions")
for p in mbr.partitions:
    print(p)

print("==> Dumping bootstrap code")
open("boostrap.bin","wb").write(mbr.bootstrap)

Thiswill decode the partition table and dump the 440 byte bootloader binary code like so, beware that not all numbers are converted from little endian properly yet (all numbers without struct.unpack):

$ python3 mbr.py dump.bin
==> Disk ID
0x8cf450f7
==> Partitions
MBRPartition(image=b'\x00 !\x00\x83\xfe\xff\xff\x00\x08\x00\x00\x01\xb0\xeb\x0b', bootable=0, driveattributes=0,
 chsstart=8480, type=131, chslast=16777214, lbastart=2048, lbaend=199997441, sectors=199995393)
MBRPartition(image=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', bootable=0, driveattribu
tes=0, chsstart=0, type=0, chslast=0, lbastart=0, lbaend=0, sectors=0)
MBRPartition(image=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', bootable=0, driveattribu
tes=0, chsstart=0, type=0, chslast=0, lbastart=0, lbaend=0, sectors=0)
MBRPartition(image=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', bootable=0, driveattribu
tes=0, chsstart=0, type=0, chslast=0, lbastart=0, lbaend=0, sectors=0)
==> Dumping bootstrap code

(I used a MBR disk here with only 1 partition). Not sure if there is an off-by-one error in the lbaend calculation, fdisk shows 1 sector less. Then you can disassemble the dumped 440 byte boostrap.bin like so

$ objdump -D -b binary -mi386 -Maddr16,data16 boostrap.bin
   0:	eb 63                	jmp    0x65
   2:	90                   	nop
   3:	10 8e d0 bc          	adc    %cl,-0x4330(%bp)
   7:	00 b0 b8 00          	add    %dh,0xb8(%bx,%si)
   b:	00 8e d8 8e          	add    %cl,-0x7128(%bp)
   f:	c0 fb be             	sar    $0xbe,%bl
  12:	00 7c bf             	add    %bh,-0x41(%si)
  15:	00 06 b9 00          	add    %al,0xb9
  19:	02 f3                	add    %bl,%dh
  1b:	a4                   	movsb  %ds:(%si),%es:(%di)
  1c:	ea 21 06 00 00       	ljmp   $0x0,$0x621
  21:	be be 07             	mov    $0x7be,%si
  24:	38 04                	cmp    %al,(%si)
  26:	75 0b                	jne    0x33
  28:	83 c6 10             	add    $0x10,%si
  2b:	81 fe fe 07          	cmp    $0x7fe,%si
  2f:	75 f3                	jne    0x24
  31:	eb 16                	jmp    0x49
  33:	b4 02                	mov    $0x2,%ah
  35:	b0 01                	mov    $0x1,%al
  37:	bb 00 7c             	mov    $0x7c00,%bx
  3a:	b2 80                	mov    $0x80,%dl
  3c:	8a 74 01             	mov    0x1(%si),%dh
  3f:	8b 4c 02             	mov    0x2(%si),%cx
  42:	cd 13                	int    $0x13
  44:	ea 00 7c 00 00       	ljmp   $0x0,$0x7c00
  49:	eb fe                	jmp    0x49
	...
  5b:	80 01 00             	addb   $0x0,(%bx,%di)
  5e:	00 00                	add    %al,(%bx,%si)
  60:	00 00                	add    %al,(%bx,%si)
  62:	00 00                	add    %al,(%bx,%si)
  64:	ff                   	(bad)  
  65:	fa                   	cli    
  66:	90                   	nop
  67:	90                   	nop
  68:	f6 c2 80             	test   $0x80,%dl
  6b:	74 05                	je     0x72
  6d:	f6 c2 70             	test   $0x70,%dl
  70:	74 02                	je     0x74
  72:	b2 80                	mov    $0x80,%dl
  74:	ea 79 7c 00 00       	ljmp   $0x0,$0x7c79
  79:	31 c0                	xor    %ax,%ax
  7b:	8e d8                	mov    %ax,%ds
  7d:	8e d0                	mov    %ax,%ss
  7f:	bc 00 20             	mov    $0x2000,%sp
  82:	fb                   	sti    
  83:	a0 64 7c             	mov    0x7c64,%al
  86:	3c ff                	cmp    $0xff,%al
  88:	74 02                	je     0x8c
  8a:	88 c2                	mov    %al,%dl
  8c:	52                   	push   %dx
  8d:	bb 17 04             	mov    $0x417,%bx
  90:	f6 07 03             	testb  $0x3,(%bx)
  93:	74 06                	je     0x9b
  95:	be 88 7d             	mov    $0x7d88,%si
  98:	e8 17 01             	call   0x1b2
  9b:	be 05 7c             	mov    $0x7c05,%si
  9e:	b4 41                	mov    $0x41,%ah
  a0:	bb aa 55             	mov    $0x55aa,%bx
  a3:	cd 13                	int    $0x13
  a5:	5a                   	pop    %dx
  a6:	52                   	push   %dx
  a7:	72 3d                	jb     0xe6
  a9:	81 fb 55 aa          	cmp    $0xaa55,%bx
  ad:	75 37                	jne    0xe6
  af:	83 e1 01             	and    $0x1,%cx
  b2:	74 32                	je     0xe6
  b4:	31 c0                	xor    %ax,%ax
  b6:	89 44 04             	mov    %ax,0x4(%si)
  b9:	40                   	inc    %ax
  ba:	88 44 ff             	mov    %al,-0x1(%si)
  bd:	89 44 02             	mov    %ax,0x2(%si)
  c0:	c7 04 10 00          	movw   $0x10,(%si)
  c4:	66 8b 1e 5c 7c       	mov    0x7c5c,%ebx
  c9:	66 89 5c 08          	mov    %ebx,0x8(%si)
  cd:	66 8b 1e 60 7c       	mov    0x7c60,%ebx
  d2:	66 89 5c 0c          	mov    %ebx,0xc(%si)
  d6:	c7 44 06 00 70       	movw   $0x7000,0x6(%si)
  db:	b4 42                	mov    $0x42,%ah
  dd:	cd 13                	int    $0x13
  df:	72 05                	jb     0xe6
  e1:	bb 00 70             	mov    $0x7000,%bx
  e4:	eb 76                	jmp    0x15c
  e6:	b4 08                	mov    $0x8,%ah
  e8:	cd 13                	int    $0x13
  ea:	73 0d                	jae    0xf9
  ec:	5a                   	pop    %dx
  ed:	84 d2                	test   %dl,%dl
  ef:	0f 83 d0 00          	jae    0x1c3
  f3:	be 93 7d             	mov    $0x7d93,%si
  f6:	e9 82 00             	jmp    0x17b
  f9:	66 0f b6 c6          	movzbl %dh,%eax
  fd:	88 64 ff             	mov    %ah,-0x1(%si)
 100:	40                   	inc    %ax
 101:	66 89 44 04          	mov    %eax,0x4(%si)
 105:	0f b6 d1             	movzbw %cl,%dx
 108:	c1 e2 02             	shl    $0x2,%dx
 10b:	88 e8                	mov    %ch,%al
 10d:	88 f4                	mov    %dh,%ah
 10f:	40                   	inc    %ax
 110:	89 44 08             	mov    %ax,0x8(%si)
 113:	0f b6 c2             	movzbw %dl,%ax
 116:	c0 e8 02             	shr    $0x2,%al
 119:	66 89 04             	mov    %eax,(%si)
 11c:	66 a1 60 7c          	mov    0x7c60,%eax
 120:	66 09 c0             	or     %eax,%eax
 123:	75 4e                	jne    0x173
 125:	66 a1 5c 7c          	mov    0x7c5c,%eax
 129:	66 31 d2             	xor    %edx,%edx
 12c:	66 f7 34             	divl   (%si)
 12f:	88 d1                	mov    %dl,%cl
 131:	31 d2                	xor    %dx,%dx
 133:	66 f7 74 04          	divl   0x4(%si)
 137:	3b 44 08             	cmp    0x8(%si),%ax
 13a:	7d 37                	jge    0x173
 13c:	fe c1                	inc    %cl
 13e:	88 c5                	mov    %al,%ch
 140:	30 c0                	xor    %al,%al
 142:	c1 e8 02             	shr    $0x2,%ax
 145:	08 c1                	or     %al,%cl
 147:	88 d0                	mov    %dl,%al
 149:	5a                   	pop    %dx
 14a:	88 c6                	mov    %al,%dh
 14c:	bb 00 70             	mov    $0x7000,%bx
 14f:	8e c3                	mov    %bx,%es
 151:	31 db                	xor    %bx,%bx
 153:	b8 01 02             	mov    $0x201,%ax
 156:	cd 13                	int    $0x13
 158:	72 1e                	jb     0x178
 15a:	8c c3                	mov    %es,%bx
 15c:	60                   	pusha  
 15d:	1e                   	push   %ds
 15e:	b9 00 01             	mov    $0x100,%cx
 161:	8e db                	mov    %bx,%ds
 163:	31 f6                	xor    %si,%si
 165:	bf 00 80             	mov    $0x8000,%di
 168:	8e c6                	mov    %si,%es
 16a:	fc                   	cld    
 16b:	f3 a5                	rep movsw %ds:(%si),%es:(%di)
 16d:	1f                   	pop    %ds
 16e:	61                   	popa   
 16f:	ff 26 5a 7c          	jmp    *0x7c5a
 173:	be 8e 7d             	mov    $0x7d8e,%si
 176:	eb 03                	jmp    0x17b
 178:	be 9d 7d             	mov    $0x7d9d,%si
 17b:	e8 34 00             	call   0x1b2
 17e:	be a2 7d             	mov    $0x7da2,%si
 181:	e8 2e 00             	call   0x1b2
 184:	cd 18                	int    $0x18
 186:	eb fe                	jmp    0x186
 188:	47                   	inc    %di
 189:	52                   	push   %dx
 18a:	55                   	push   %bp
 18b:	42                   	inc    %dx
 18c:	20 00                	and    %al,(%bx,%si)
 18e:	47                   	inc    %di
 18f:	65 6f                	outsw  %gs:(%si),(%dx)
 191:	6d                   	insw   (%dx),%es:(%di)
 192:	00 48 61             	add    %cl,0x61(%bx,%si)
 195:	72 64                	jb     0x1fb
 197:	20 44 69             	and    %al,0x69(%si)
 19a:	73 6b                	jae    0x207
 19c:	00 52 65             	add    %dl,0x65(%bp,%si)
 19f:	61                   	popa   
 1a0:	64 00 20             	add    %ah,%fs:(%bx,%si)
 1a3:	45                   	inc    %bp
 1a4:	72 72                	jb     0x218
 1a6:	6f                   	outsw  %ds:(%si),(%dx)
 1a7:	72 0d                	jb     0x1b6
 1a9:	0a 00                	or     (%bx,%si),%al
 1ab:	bb 01 00             	mov    $0x1,%bx
 1ae:	b4 0e                	mov    $0xe,%ah
 1b0:	cd 10                	int    $0x10
 1b2:	ac                   	lods   %ds:(%si),%al
 1b3:	3c 00                	cmp    $0x0,%al
 1b5:	75 f4                	jne    0x1ab
 1b7:	c3                   	ret    

which in this case is a GRUB boostrap code. The complete mbr on that system is also available in /usr/lib/grub/i386-pc/boot.img BTW but that contains the complete MBR including the partition data. Interestingly the objdump command will also decode the entire MBR image including the partition tables; not sure if this is supposed to be that way or if the result beyond 440 bytes is garbage.

Beware that the


At the end of the river the sundown beams

Offline

#5 2019-09-15 15:35:39

eight.bit.al
Member
From: The top of the World
Registered: 2015-10-01
Posts: 370

Re: Disk Editor/Disassembler - (LONG)

twoion wrote:

The bootloader is only the 440 to 446 bytes,

Depending on who's grub, the bootstrap code might be anywhere from 434 to 446 bytes, SUSE 's being the longest at 446. In this case it's code from Arch Linux at the more conventional 440.

not all numbers are converted from little endian properly yet

I take it objdump needs the numbers in big endian to work.

Then you can disassemble the dumped 440 byte boostrap.bin like so

$ objdump -D -b binary -mi386 -Maddr16,data16 boostrap.bin
<snip>

Thanks. You've given me something to work with.

also available in /usr/lib/grub/i386-pc/boot.img

That might be helpful, alas not found in Arch Linux.

Beware that the

Beware the unwashed hack like me!

8bit

“The question isn’t who is going to let me; it’s who is going to stop me.” - Ayn Rand


A mind is a terrible thing not to change.
The problem lies not with new ideas, but in escaping the old ones.

Offline

#6 2019-09-15 16:34:50

eight.bit.al
Member
From: The top of the World
Registered: 2015-10-01
Posts: 370

Re: Disk Editor/Disassembler - (LONG)

Here's where I'm going with this:

The job of the bootstrap code segment in the MBR is look up the active partition from the partition table, and load that code into the memory for execution by the CPU as the next link in the boot chain. It can actually look up a hard-coded partition instead of the active partition; e.g. always load the bootsector of the 3rd partition.

Distrowatch is my friend.  smile I install and learn from/play with about 3 out of 4 distros it lists. Right now it's Endless OS. Additionally, I'm working down the list generated by the search function with Debian as the only search factor.

At least one distro a day and sometimes more depending if I like/dislike the current test distro. I'm spending too much time fixing the problems created by distros not playing nice with each other. So I want to hard code the boot sector to boot to the Debian (Helium/Lithium (real soon now(™))) partition and use the tools I'm more familiar with; os-prober, update-grub, etc., but not install-grub. Backing up and restoring my custom boot sector as needed is a no brainer. Still need to figure out how to deal with distros the insist on formating the swap partition whether told to or not. (told not to or not  wink )

See, there is madness to my method.

First, boot to the Debian partition and install grub from there. I'll have better luck with that than the current Arch Linux boot loader. I may even scrap the Arch partition, but I spent sooooo much time getting it to work, hard to throw away. I might need a new partition scheme anyway; currently one primary partition and the rest in one/the extended partition.

The hunt is on.

8bit

“The power of imagination makes us infinite.” - John Muir

-edit- typo

Last edited by eight.bit.al (2019-09-16 11:42:33)


A mind is a terrible thing not to change.
The problem lies not with new ideas, but in escaping the old ones.

Offline

#7 2019-09-16 09:37:29

earlybird
ほやほや
Registered: 2015-12-16
Posts: 736
Website

Re: Disk Editor/Disassembler - (LONG)

eight.bit.al wrote:

I take it objdump needs the numbers in big endian to work.

No, it just needs the binary image. Since the display in most userland tools like fdisk, gdisk etc is in human-readable big endian (for example the disk ID), converting to BE is just useful for making the output readable and comparable.

Offline

#8 2019-09-16 12:01:40

eight.bit.al
Member
From: The top of the World
Registered: 2015-10-01
Posts: 370

Re: Disk Editor/Disassembler - (LONG)

Thanks for the replies and ideas.  smile

8bit

“Life is trying things to see if they work.” - Ray Bradbury


A mind is a terrible thing not to change.
The problem lies not with new ideas, but in escaping the old ones.

Offline

Board footer

Powered by FluxBB