I think they might be depending on the value of the weird sector elsewhere. like they're loading it SOMEWHERE, maybe they overwrite some code?
I need a comparative DOS CPU tracer.
Like, load two copies of the same EXE, and run until the execution diverges
THEY CHECKSUM THE FIRST 16KB OF EXECUTABLE RAM?
I patched the EXE to have the right value, but then they checksum it, and now the value is wrong!
@foone@digipres.club so just make the last instructions in the checksum code return the value they want, or NOP the branch that would say otherwise.
insert pop-team-epic "you are motherfucker" here
they checksum memory MORE THAN ONCE!?
they checksum memory MORE THAN ONCE!?
@foone@digipres.club I am behind seven checksums, I am uncrackable.
I patched out the checksumming and I think I've got it.
okay, so, the copy protection:
1. It checks for a sector that should not exist: Track 38, sector 113.
It's on a single-sided double density floppy (160kb), so there's supposed to be 8 sectors per track. But as we saw in this post
//digipres.club/@foone/115011910054706753
this disk DOES have a sector 113.
so step one to bypassing the copy protection is hack that function to return "yes the sector exists".
EXCEPT THAT WON'T WORK. For two reasons. The first I'll get to later. The second is that the actual value of that sector matters, it gets read into memory and the bytes at 0x7-0x8 are checked later.
so I make sure those 2 bytes in memory are set. Easy!
so step one to bypassing the copy protection is hack that function to return "yes the sector exists".
EXCEPT THAT WON'T WORK. For two reasons. The first I'll get to later. The second is that the actual value of that sector matters, it gets read into memory and the bytes at 0x7-0x8 are checked later.
so I make sure those 2 bytes in memory are set. Easy!
the next phase of copy protection checks another sector: track 39, sector #25.
Another sector that doesn't exist, but it does, and it's 128 bytes long, and... they're doing evil things to DOS to make this readable. They switch the DOS format in memory, reset the disk IO system, and try to successfully read a 128byte sector. Somehow, apparently, this works?
the next phase of copy protection checks another sector: track 39, sector #25.
Another sector that doesn't exist, but it does, and it's 128 bytes long, and... they're doing evil things to DOS to make this readable. They switch the DOS format in memory, reset the disk IO system, and try to successfully read a 128byte sector. Somehow, apparently, this works?
Here's what track 39 looks like.
Here's what track 39 looks like.
I think there's another layer of protection that I bypassed on accident
I think there's another layer of protection that I bypassed on accident
trying to figure out this possible third layer of protection before I continue.
today's scores- emulators crashed:
1. DOSBOX: 2 times
2. MartyPC 1 time
3. 86Box: 0 times
trying to figure out this possible third layer of protection before I continue.
today's scores- emulators crashed:
1. DOSBOX: 2 times
2. MartyPC 1 time
3. 86Box: 0 times
okay, tracked it down: it's just coming from the same sector 113 as loaded in step one. Anyway, if this isn't loaded properly, we'll trigger a "Diskette/Version out of phase" error. I fix this by just never checking if it's loaded: I NOP'd that part out
okay, tracked it down: it's just coming from the same sector 113 as loaded in step one. Anyway, if this isn't loaded properly, we'll trigger a "Diskette/Version out of phase" error. I fix this by just never checking if it's loaded: I NOP'd that part out
but if you apply these two patches, it'll STILL not work!
But it won't work silently. It'll pretend to work.
but if you apply these two patches, it'll STILL not work!
But it won't work silently. It'll pretend to work.
But at the end of the game, it'll change your tombstone, and not save your score:
It'll call you "Software Pirate" and say you were killed by the "Copy Protection Mafia"
But at the end of the game, it'll change your tombstone, and not save your score:
It'll call you "Software Pirate" and say you were killed by the "Copy Protection Mafia"
that's because the checksum failed. it checksums the the code segment, starting at 1000:0082 and going to 1000:4082 (16 kilobytes)
that's because the checksum failed. it checksums the the code segment, starting at 1000:0082 and going to 1000:4082 (16 kilobytes)
fun fact: this code is self modifying! not for copy-protection reasons, but for generic-interrupt reasons.
x86 doesn't have an INT r8 instruction, only INT imm8. So to generically call an interrupt, you have to either:
1. do a lookup to a bunch of INT 00h, INT 01h, INT 02h, INT 03h instructions, OR...
2. just rewrite your own code at runtime. overwrite the second byte of the "INT 00" instruction and bam, dynamic interrupts
fun fact: this code is self modifying! not for copy-protection reasons, but for generic-interrupt reasons.
x86 doesn't have an INT r8 instruction, only INT imm8. So to generically call an interrupt, you have to either:
1. do a lookup to a bunch of INT 00h, INT 01h, INT 02h, INT 03h instructions, OR...
2. just rewrite your own code at runtime. overwrite the second byte of the "INT 00" instruction and bam, dynamic interrupts
but luckily for everyone, the call_interrupt function is at 1000:e3b6 so it's outside the checksummed 16kb
but luckily for everyone, the call_interrupt function is at 1000:e3b6 so it's outside the checksummed 16kb
so, step 3: The checksums.
I hack out the checksum function so that when it's called, it just writes the "correct" answer into the return value.
I do it at the point where checksum_memory() is implemented, not where it's called, as there's two visible calls to the checksumming function, there may be more. this way it'll always return the right value.
(assuming they always checksum the same part of memory! a fun trick would be doing different chunks of RAM... but not here)
so, step 3: The checksums.
I hack out the checksum function so that when it's called, it just writes the "correct" answer into the return value.
I do it at the point where checksum_memory() is implemented, not where it's called, as there's two visible calls to the checksumming function, there may be more. this way it'll always return the right value.
(assuming they always checksum the same part of memory! a fun trick would be doing different chunks of RAM... but not here)
so I have a hack that works: I don't think I trust it though. I'm going to change it so the right memory gets into RAM at the right places, just to make sure there's no additional side effects.
There's random values in this sector, after all: what if the game is using them to multiple enemy damage or something?
so I have a hack that works: I don't think I trust it though. I'm going to change it so the right memory gets into RAM at the right places, just to make sure there's no additional side effects.
There's random values in this sector, after all: what if the game is using them to multiple enemy damage or something?
rather than hack my way into having a disk that'll work when mounted in DOSBox, I'm just going to make it work properly if the files are copied to DOS? I'll stick the information from those sectors into a file, and swap the raw-sector interrupts out for a simple DOS read-file-data routine.
in fact, I might be able to steal one from elsewhere in the EXE
rather than hack my way into having a disk that'll work when mounted in DOSBox, I'm just going to make it work properly if the files are copied to DOS? I'll stick the information from those sectors into a file, and swap the raw-sector interrupts out for a simple DOS read-file-data routine.
in fact, I might be able to steal one from elsewhere in the EXE
weird. I can't find any DOS file interrupts. I know this is for DOS 2.x, but it's gotta load files somehow... there's a high score file!
weird. I can't find any DOS file interrupts. I know this is for DOS 2.x, but it's gotta load files somehow... there's a high score file!
oh wait I searched on "int 21h". but there's a generic interrupt mechanism here, I just described it. I'm an idiot
oh wait I searched on "int 21h". but there's a generic interrupt mechanism here, I just described it. I'm an idiot
btw, the game (Rogue, if I didn't mention that before) is written in C, and uses Lattice C 2.00.
Ghidra mostly decompiles it properly. The function arguments sometimes confuse it: Lattice seems to add some padding between arguments sometimes? I'm not sure why exactly.
btw, the game (Rogue, if I didn't mention that before) is written in C, and uses Lattice C 2.00.
Ghidra mostly decompiles it properly. The function arguments sometimes confuse it: Lattice seems to add some padding between arguments sometimes? I'm not sure why exactly.
this game is so old it doesn't support directories
@foone@digipres.club The only version I have (and had back then) is cracked so it's interesting to hear how they got there.
@foone@digipres.club I am here reading your posts. I hear and understand.
@foone@digipres.club I was thinking about that just the other day, trying to remember how far back those go, like, whether I could remember using folders or the like on the Commodore 64.