hi all, i know well that you're tired to see my stupid help requests about mbr, thunks, QT_Thunks and so on. This will be my last and than i will let HD mbr reading for better times :( Ok, if you don't have better things to do, i try to explain the issue... The idea suggested by a friend was to read HD mbr under win95 using asm32. First, with some help, i've found a Micro$$$oft article that suggest to use DeviceIoControl function (normally used in NT), calling to VWIN32.VXD, that seems could simulate int13.
DeviceIoControl() is called using a handle to a VxD rather than a handle to a disk drive or disk partition. Obtain a handle to VWIN32.VXD by using CreateFile( "\\\\.\\VWIN32", ... ). Use this handle in calls to DeviceIoControl() to perform volume locking (Int 21h Function 440Dh, Subfunctions 4Ah and 4Bh), and then to perform BIOS calls (Int 13h), Absolute Disk Reads/Writes (Int 25h and 26h), or MS-DOS IOCTL functions (Int 21h Function 440Dh).
But i've seen that this method don't work with HD, it work only for floppies :( So i've found another M$ article that says PRB: DeviceIoControl Int 13h Does Not Support Hard Disks (first seems is possible, now it's no more possible) So they suggest to code a 32 bit dll that call a 16bit dll that perform a call to DPMI service 31h that can simulate int13h with a C code example..... I've try also this way but it's too complicated for me now. I can't debug the errors trough the dll calling.... This method seems a big mess to realize. So this is the story. If someone can give me an idea for an easier way to read MBR i will be very very grateful. many thanks to all that has read this heavy message ( & sorry for bad english :( ).
Posted on 2001-05-21 12:40:00 by angelo
Fdisk works under Windows so there must be a way to read the MBR using the Win32 api. Sorry i don't know how
Posted on 2001-05-23 05:05:00 by karim
fdisk is a dos app, and thus uses dos calls. Which are fine under win32. However, sure there must be a way to do it natively in win32, as programs such as partitionmagic (at least newer versions) and defrag can operate from within windows. It would, however, be very much like microsoft to not speak very loudly of how to do this...
Posted on 2001-05-23 05:10:00 by f0dder
You need to read CreateFile, that explains opening the logical partition, or phyisical device...

.data
File1   db "\\.\C:", 0             ;Open Logical partition C:
File2   db "\\.\PHYSICALDRIVE1",0  ;Open the second logical drive (start at 0)

.code
  invoke ADDR File, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, \
         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
;You must use FILE_SHARE_WRITE when opening a logical drive!
Also remember to CloseHandle again afterwards (and always look both ways before crossing the road :D ). Mirno
Posted on 2001-05-23 05:42:00 by Mirno
Is it possible to make the same interrupts calls as the ones used by fdisk ?
Posted on 2001-05-23 10:03:00 by karim
thanks to have resurrected my message from the grave. I've always tought that apps like partitionmagic read MBR in easy way. But microsoft suggest to use a call to a 16bit code from where you can call DPMI service. That mean that you can use interrupts as in dos but staying in protected mode, as windows32 is. The key to switch to a 16 bit code seems to be undocumneted function QT_Thunk (in Kernel32.dll). This function permit to jump to 16 bit code that use logical addressing (SEG(16):OFF(16)). But it is a too difficult way for me. There must be a better way ....
Posted on 2001-05-23 16:20:00 by angelo
Try to translate the C program into Assembly. You have to make two DLLs and a win32 executable. The first dll is a 16-bit dll. It defines a function that calls the DPMI interrupt to read the MBR. The second dll is a 32-bit dll. In this dll, you need to define a function that will call the function defined in the 16-bit dll. The last step is to make an executable that will call the function defined in the 32-bit dll. To summarize : 1. Write the source file for the 16-bit dll 2. Write the source file for the 32-bit dll 3. Write the thunk script of the msdn article 4. Use the thunk compiler (you can find it in the 98 ddk) to compile the thunk script. It will generate an asm file. 5. Compile the asm file twice (with -DIS_16 and -DIS_32) 6. Link the 16-bit thunk object code with the 16-bit dll object code -> you have the 16-bit dll 7. Link the 32-bit thunk object code with the 32-bit dll object code -> you have the 32-bit dll 8. Write the asm source file of your application 9. Link it with the 32-bit dll. It's interesting. The MASM manual explains how to build a 16-bit dll, so technically it is possible to code everything in asm.
Posted on 2001-05-25 03:25:00 by karim
I've tried to translate the program in the msdn article but I had problems with the 16-bit. I can't use the 32-bit rc compiler to set the dll version to 40. I've tried to modify the dll header but without success. The dll initialization function always failed. However, I've found 2 dlls in the windows\system directory (cvrt32.dll and cvrt16.dll) that exports the function ReadPhysicalSector_16 and ReadPhysicalSector_32, the same function as the ones described in the msdn article. Here is a program example that uses the dll to read the mbr :

.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc


.DATA

dll32			BYTE		"Cvt32.dll", 0
funcName		BYTE		"_ReadPhysicalSector_32@12", 0

MsgError1		BYTE		"Can't load dll", 0
MsgError2		BYTE		"Can't load proc", 0
MsgError3		BYTE		"ReadPhysicalSector failed", 0

align 4
buffer			BYTE		512 DUP (0)

.CODE

start :
	
	INVOKE	LoadLibrary, addr dll32
	.IF ( eax )
		INVOKE	GetProcAddress, eax, addr funcName
		.IF ( eax )
			;// call the function in the dll
			push	512						;// buffer size
			push	byte ptr offset buffer	;// buffer address
			push	080h					;// hard drive 0
			call	eax
			.IF ( eax )
				;// ...
				;// here you can use a debugger to see the content of the buffer
				;// for example, you can see that the buffer ends with the mbr signature (55AA)
				;// ....
				xor		eax, eax
			.ELSE
				;// error msg
				mov		eax, offset MsgError3
			.ENDIF
		.ELSE
			mov		eax, offset MsgError2
		.ENDIF
	.ELSE
		mov		eax, offset MsgError1
	.ENDIF

	.IF ( eax )
		INVOKE	MessageBox, NULL, eax, NULL, MB_OK
	.ENDIF

	INVOKE	ExitProcess, NULL

END start

It works but the cvrt16 and cvrt32 dll were probably made using a 16-bit C compiler. If someone knows how to make a 16-bit dll in asm, please explain it in the message board. Thanks !
Posted on 2001-05-26 18:59:00 by karim
karim, i was trying the 16 bit dll. The dll is transated from M$ C example. I think there could be some error but i can't debug:

                locals
                jumps
                model small, windows pascal
                .386p
                
extrn           GlobalDosAlloc          :FAR
extrn           GlobalDosFree           :FAR
extrn           UnlockSegment           :FAR
extrn           LocalInit               :FAR

; The MAKEWORD macro creates an unsigned 16-bit integer by concatenating two given unsigned character values.

mkw             MACRO   lowerbyte, higherbyte

                xor     eax,eax
                xor     ebx,ebx
                mov     bl,higherbyte
                shl     bx,8
                mov     al,lowerbyte
                or      ax,bx

                ENDM

; The MAKELONG (mkl) macro creates an unsigned 32-bit value by concatenating two given 16-bit values. 

mkl             MACRO   loworder, hiorder

                xor     eax,eax
                mov     ebx,hiorder
                shl     ebx,16     
                mov     eax,loworder
                or      eax,ebx

                ENDM

; The HIWORD (hwd) macro retrieves the high-order word from the given 32-bit value. 

hwd             MACRO   bigword         ;; Retrieves the high word from double word argument

                mov     eax,bigword
                shr     eax,16          ;; Shift 16 for high word to set to high word
                
                ENDM

; The LOWORD (lwd) macro retrieves the low-order word from the given 32-bit value. 

lwd             MACRO   bigword         ;; Retrieves the low word from double word argument

                mov     eax,bigword
                and     eax,0FFFFh      ;; Set to low word 

                ENDM

tagRMCS         STRUC

reg_edi         dd      ?
reg_esi         dd      ?
reg_ebp         dd      ?
reg_ebx         dd      ?
reg_edx         dd      ?
reg_ecx         dd      ?
reg_eax         dd      ?
wFlags          dw      ?
reg_es          dw      ?
reg_ds          dw      ?
reg_fs          dw      ?
reg_gs          dw      ?
reg_ip          dw      ?
reg_cs          dw      ?
reg_sp          dw      ?
reg_ss          dw      ?
tagRMCS         ENDS

.const

SECTOR_SIZE     equ     512             ; // Size, in bytes, of a disk sector
CARRY_FLAG      equ     0001h
TRUE            equ     01h
NULL            equ     00h
FALSE           equ     00h

.data

hInstance       dw      ?
Address32       dd      ?
CodeS           dd      ?

callStruct      tagRMCS 

.code

LibEntry        proc    hinst:word, hdataseg:word, heapsize:word, cmdline:dword

                
                mov     eax, TRUE
                ret

LibEntry        endp

;/*--------------------------------------------------------------------
;  ReadPhysicalSector1()
;
;  Calls DPMI to call the BIOS Int 13h Read Track function to read the
;  first physical sector of a physical drive. This function is used to
;  read partition tables, for example.
;
;  Parameters
;     bDrive
;        The Int 13h device unit,
;           0x00 for floppy drive 0
;           0x00 for floppy drive 1
;           0x80 for physical hard disk 0
;           0x81 for physical hard disk 1
;           etc.
;
;     lpBuffer
;        Pointer to a buffer that receives the sector data.  The buffer
;        must be at least SECTOR_SIZE bytes long.
;
;     cbBuffSize
;        Actual size of lpBuffer.
;
;  Return Value
;     Returns TRUE if the first sector was read into the buffer pointed
;     to by lpBuffer, or FALSE otherwise.
;
;  Assumptions
;     Assumes that sectors are at least SECTOR_SIZE bytes long.
;--------------------------------------------------------------------*/ 

ReadPhysicalSector1     Proc    bDrive:BYTE, lpBuffer:DWORD, cbBuffSize:DWORD

        LOCAL   fResult :DWORD, callStructOff :DWORD, gdaBuffer :DWORD, RMlpBuffer :DWORD, PMlpBuffer :DWORD
Posted on 2001-05-28 01:13:00 by angelo
Yes, I'm interested. When you translate the exemple from the MSDN, remember that it is splitted into 3 parts : a 16-bit, a 32-bit dll and win32 executable. The 32-bit dll and win32 exe are easy to make. The problem is the 16-bit dll. According to MASM manual, a 16-bit dll must export a WEP procedure and an entry point. Functions in the dll must use a special prolog and epilog. The MSDN says that the dll's version number must be marked to 40. I can make the 16-bit dll but I can't mark it 40. When I link the 32-bit dll implicitely, the win32 program crashes. When I load the dll explicitly with LoadLibrary, the function failed and I must restart the computer if I want overwrite the 16-bit dll file. I don't know how to solve the problem. In the windows\system directory, there are a lot of 32-bit dll that calls 16-bit dll. It must be possible to make the same thing in asm.
Posted on 2001-05-28 03:19:00 by karim
karim, what i'm trying to do is DO NOT use 32bit dll passage. This to reduce the operation in 2 steps (more easy). From this article it seems possible use QT_Thunk to jump directly to 16bit dll withou creating a 32 bit one :) I've already created the 32bit app that should call the 16bit dll and it works ! But then a have an error in the 16bit dll and i cant debug :( Now i'mlocked here :( On my application the steps to call 16 bit dll are (don't look @ error handling):


@st1:           call    LoadLibrary16, offset DiskPartyDll16            ; loading my 16bit library   
                cmp     eax,32
                jg      @st2
                mov     Error,1                                         ; error handling
                mov     eax,4  
                jmp     ErrorHandling

@st2:           mov     hInst16,eax 
                call    FreeLibrary16, hInst16                          ; free library 
                cmp     eax,0
                jg      @st3
                mov     Error,1                                         ; error handling
                mov     eax,5  
                jmp     ErrorHandling

@st3:           call    GetProcAddress16, hInst16, offset RPS1          ; loading 16 bit address !!  
                cmp     eax,0
                jg      @st4
                mov     Error,1                                         ; error handling
                mov     eax,6  
                jmp     ErrorHandling

@st4:           mov     eax, offset MBRbuffer
                mov     MbrAddress,eax

                push    80h             ; drive
                push    MbrAddress      ; offset MBRBUFFER
                push    512             ; size of buffer/MBR
                mov     edx,eax                                         ; 16:16 bit address
                call    QT_Thunk
And the call works. if you prefer i can send my entire app. by email.
Posted on 2001-05-28 07:00:00 by angelo
oops, becouse now i'm in my lunch time @job, i've added some lines that was missing without trying to compile. Sorry, I've overwrote eax, where is stored the address to call. Please replace @st4 code with these using edi ...

@st4:           mov     edi, offset MBRbuffer
                mov     MbrAddress,edi

                push    80h             ; drive
                push    MbrAddress      ; offset MBRBUFFER
                push    512             ; size of buffer/MBR
                mov     edx,eax                                         ; 16:16 bit address
                call    QT_Thunk
Posted on 2001-05-28 07:14:00 by angelo
As the article said, you can't pass 32-bit pointers directly. You have to use the thunk mechanism to convert 32-bit pointers to 16:16 pointers that can be used in 16-bit code. This may be the reason why your program doesn't work.
Posted on 2001-05-28 08:00:00 by karim
karim, i've seen that i'm going on the problem as a monkey. I can't get out if i translate examples from C or if I copy them from other asm examples without understant how real-mode and proteced mode works. 1) Think it's better i understand well some points as logical addressing (16:16), 32to16bit stack pointer change ... and all other points involved on this mess. 2) Now I've installed softICE. Before i was using OllyDBG, good 32bit debugger but it couldn't debug 16bit code. SoftICE is very good becouse let me watch inside 16bit dll. I've soon seen that the error of my 16bit dll is in a 'POP ES' instruction. Probably i can't pop a 16bit value becouse stack is 32bit. But, how i've said, i've to study, to understand a lot. Thanks for your interests and helps. When i will have learned all these mysterious points I will come back out on the forum with a new topic (hope with the working code to read MBR) but i think the way is hard. Thanks for all (i apologize for my bad english, hope you have understood the meaning of the message). Angelo
Posted on 2001-05-28 16:39:00 by angelo
It seems like a lot of work just to read sector 1 on track 0 head 0 -- but this time M$ has an excuse: all disk i/o goes through ROM BIOS, even now, and that code is (as far as I know) still in 16-bit mode. PS (edit): on my Win95 jalopy, DEFRAG.EXE and several important .cpl files are 16-bit NE's, not PE's. Could someone see if this is true on NT too? This message was edited by Larry Hammick, on 5/28/2001 8:30:26 PM
Posted on 2001-05-28 20:12:00 by Larry Hammick
Hm. I thought that win9x had it's own 32bit code handling the harddrives directly? I know that a hell of a lot of stuff thunks down to 16bit protected mode (like, the GDI stuff). And a few things might thunk down to 16bit realmode (bios stuff). But unless you system is in "compatibility mode" because of some flaky old driver, harddrive access should be 32bit. HM. Looks like defrag.exe is also NE on windows 98. Perhaps it's just plain easier getting this form of access from 16bit protected mode? As for the "simulaterealmodeint" approach, that's plain disgusting :). I think there's a way to do it with DeviceIoCTL's to the correct VXD, but... *blank*
Posted on 2001-05-31 06:44:00 by f0dder
Well, f0dder, I might be all wet about bios for HD; I am somewhat out of my depth here. But look at eisa.vxd -- it is too small to do much, and maybe it just translates 32-bit calls into 16-bit or bios calls. Anyway, if someone succeeds in reading an absolute sector, please let me know. DOS was so much simpler...
Posted on 2001-05-31 18:24:00 by Larry Hammick