Open jjr90 opened 9 months ago
As a Linux/Mac user, I felt an exe patch would be easier than learning Visual Studio, and it was pretty easy.
Patched hdimount-win10x64 is now able to mount an encrypted DMG with the included Dokany and copy files off of it.
Preface: I'm really glad hdimount exists, as it seems to be the only utility that can mount an encrypted DMG on Windows without extracting an entire copy of everything (a la HFSExplorer, TransMac, dmgwiz, etc.), which isn't always feasible.
I'll give a high-level walkthrough of the patch process, then the exact bytes to patch with a hex editor (e.g. HxD) or dd
(commands included), to save others' time.
Note: There seems to be an additional, 3rd bug in this function that we can't fix this way (namely: the BACKSPACE
char not being emitted in the way the author expected) and a 4th bug that is caused by our changes but is important to note in case someone implements the changes as described. (See section 2.)
Outline: I. Process walkthrough II. Persisting bug III. Exact bytes to patch and MD5 checksum <------ start here if you just want the patch IV. Example usage and workaround for hung process
Tools used:
Steps:
Download the "hdimount-win10x64" binary from the release tag 1.0__c13f231
Find the Windows getpass routine. Because the symbols are stripped, this would be difficult without tools. In Hopper just do an assembly search for "ReadConsoleA" (Find... > String in Assembly) and the only routine that calls it will show up, which will be the unlabeled getpass.
We need to increment passLength
at the end of each loop iteration. To do this, I just wrote over the size check code, because you're not going to buffer-overflow yourself.
Change these assembly lines:
4881FB00010000 cmp rbx, 0x100
72C0 jb 0x140012193
To read this instead:
48FFC3 inc rbx
90 nop
90 nop
90 nop
90 nop
EBC0 jmp 0x140012193
You see we are now incrementing rbx
insteading of comparing it to 0x100
(256, the size of the pass
buffer).
The change of byte '72' to 'EB' for the jump instruction turned it into an unconditional jump, instead of the original jb
(jump-if-cmp-below), basically removing the test for the now-nonexistent comparison (cmp).
RETURN
byte, since your password probably doesn't end with a carriage return. To do this, I just wrote over the printf("\n")
code, since that's just a cosmetic detail, and we'd rather be able to mount the image.Change this assembly line (near the bottom of the function):
E8B1B8FFFF call 0x14000da90
To read this instead:
C6441D0000 mov byte [rbp+rbx], 0x0
rbp
is the address of the pass
buffer, and rbx
is the passLength
.
So we are storing 0x0
(null) at the address obtained by adding those two values.
The desired instruction is the same length as the original call
that we're overwriting, so there's no nop
padding needed.
The lea
that was loading an argument prior to that instruction is now useless but it's safe to leave it there.
To get backspaces to work now, you would IN THEORY now need to remove the zero-test from the passLength
decrement, i.e. to decrement it unconditionally upon a BACKSPACE
char. This is because we're now unconditionally incrementing it at the end of each iteration. (Run a few test-cases in your head, you'll see what I mean.)
HOWEVER, even if we patch that, backspace still doesn't work properly. So I'm guessing the BACKSPACE
char is not being emitted in the way the author expected.
But that's okay - you just have to type your password correctly without errors.
Overwrite the 7 bytes at file offset 71115 (0x115cb),
changing 81FB0001000072
to be FFC390909090EB
instead.
Overwrite the 5 bytes at file offset 71130 (0x115da),
changing E8B1B8FFFF
to be C6441D0000
instead.
If you did it correctly, the MD5 checksum of the resulting exe should be 840727596238e203ce7d0714f367f43f
.
Linux/Mac commands:
printf '\xff\xc3\x90\x90\x90\x90\xeb' | dd of=hdimount-win10x64.exe bs=1 seek=71115 conv=notrunc
printf '\xc6\x44\x1d\x00\x00' | dd of=hdimount-win10x64.exe bs=1 seek=71130 conv=notrunc
Linux check: md5sum hdimount-win10x64.exe
Mac check: md5 -r hdimount-win10x64.exe
Uninstall any existing Dokany, and install the DokanySetup that's in the hdimount source tree "msvc" folder.
Now if you open Windows Command as Administrator and run:
hdimount encrypted.dmg g:
It will ask for your password. If you enter the wrong password, there will be feedback. If you enter the right password, there will be no feedback-- but refresh your disks window, and the mounted disk image should be there as drive G, "DOKANY".
I couldn't figure out how to unmount the disk in the GUI, and Control-C was not working in the Command window.
Beware using mountvol G: /D
to unmount it and simply force-closing the window, because the hdimount process will still be running in the background, doing nothing.
Instead, use the -f
flag when mounting the image:
hdimount -f encrypted.dmg g:
Then if you do Control-C, the process will still hang there, but the disk will have unmounted (refresh your disks window).
Then you can close the window and the process will close (not be stuck in the background).
I was able to install the DokanSetup.exe from the "msvc" subdir to launch hdimount on Windows.
However, for the same exact DMG file that Linux hdimount can mount correctly, the Windows hdimount is saying "Error: Not encrypted or wrong password."
I think I might've found the issue.
In the Windows version of
getpass
, thepassLength
is never updated, so it stays at 0 and keeps writing the console input to index 0. Not only that, but it looks like it never NULL-terminates the string afterward (update: thus the password retains theRETURN
at the end.) https://github.com/jief666/hdimount/blob/b1a31ae3ef9f12ce125840d84d3b6494c05ec639/src/CrossPlatform.cpp#L37 [snipped]