// An Example of this unit is availabe as Demo Download. // Ein Beispiel zur Anwendung dieser Unit kann als Demo heruntergeladen werden.
//////////////////////////////////////////////////////////////////////////////// // // BIOS Helper for Delphi // // BIOS related utilities for Win9x and WinNT(i386) // //////////////////////////////////////////////////////////////////////////////// // // The Original Code is: // BiosHelp.pas, released 2001-09-02. // // The Initial Developer of the Original Code is Nico Bendlin. // // Portions created by Nico Bendlin are // Copyright (C) 2001-2003 Nico Bendlin. All Rights Reserved. // // Contributor(s): // Nico Bendlin<nicode@gmx.net> // // The contents of this file are subject to the Mozilla Public License Version // 1.1 (the "License"); you may not use this file except in compliance with the // License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for // the specific language governing rights and limitations under the License. // // Alternatively, the contents of this file may be used under the terms of // either the GNU General Public License Version 2 or later (the "GPL"), or // the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), // in which case the provisions of the GPL or the LGPL are applicable instead // of those above. If you wish to allow use of your version of this file only // under the terms of either the GPL or the LGPL, and not to allow others to // use your version of this file under the terms of the MPL, indicate your // decision by deleting the provisions above and replace them with the notice // and other provisions required by the GPL or the LGPL. If you do not delete // the provisions above, a recipient may use your version of this file under // the terms of any one of the MPL, the GPL or the LGPL. // //////////////////////////////////////////////////////////////////////////////// // // Revision: // // 2003-02-15 2.00 [NicoDE] // - generic dump method completely rewritten // - default range is now E000:0000-F000:FFFF // ////////////////////////////////////////////////////////////////////////////////
{$IFDEF CONDITIONALEXPRESSIONS} {$DEFINE DELPHI6UP} {$IF NOT DEFINED(VER140)} {$DEFINE DELPHI7UP} {$IFEND} {$ENDIF}
unit BiosHelp {$IFDEF DELPHI6UP} platform {$ENDIF};
{$MINENUMSIZE 4} {$WEAKPACKAGEUNIT} {$IFDEF DELPHI7UP} {$WARN UNSAFE_TYPE OFF} {$WARN UNSAFE_CODE OFF} {$ENDIF}
interface
uses Windows;
const RomBiosDumpBase = $000E0000; RomBiosDumpEnd = $000FFFFF; RomBiosDumpSize = RomBiosDumpEnd - RomBiosDumpBase + 1;
type PRomBiosDump = ^TRomBiosDump; TRomBiosDump = array [RomBiosDumpBase..RomBiosDumpEnd] of Byte;
type TRomDumpMethod = (rdmAutomatic, // Autodetect OS type and use proper method rdmGeneric, // Use 16-bit EXE program to dump the BIOS rdmMemory, // Dump from process's address space (Win9x) rdmPhysical // Dump from physical memory object (WinNT) );
function DumpRomBios(out Dump: TRomBiosDump; Method: TRomDumpMethod = rdmAutomatic; Timeout: DWORD = 5000): Boolean; function DumpRomBiosEx(RomBase: Pointer; RomSize: Cardinal; out Dump; Method: TRomDumpMethod = rdmAutomatic; Timeout: DWORD = 5000): Boolean;
procedure ReadRomDumpBuffer(const Dump: TRomBiosDump; Addr: Pointer; var Buffer; Size: Cardinal); procedure ReadRomDumpBufferEx(const Dump; Base, Addr: Pointer; var Buffer; Size: Cardinal);
function GetRomDumpAddr(const Dump: TRomBiosDump; Addr: Pointer): Pointer; function GetRomDumpAddrEx(const Dump; Base, Addr: Pointer): Pointer;
implementation
//////////////////////////////////////////////////////////////////////////////// // // DumpRomBios16 (rdmGeneric) // // Creates an 16-bit EXE program in TEMP and runs it redirected to an file. // // WARNING: One day 16-bit code will not run on future Windows. // WARNING: You are dumping the BIOS inside the MS-DOS 'emulator'. //
function _RomDumpCode(RomBase: Pointer; RomSize: Cardinal; out Code: Pointer; out Size: Cardinal): Boolean; const BlockSize = $1000; type // ; RomDump (dumps mem to STDOUT) PRomDumpCode = ^TRomDumpCode; // ; BlockSize MUST be multiple of 10h. TRomDumpCode = packed record // _header: TImageDosHeader; // _notice: array[0..$4F] of AnsiChar; // @@note: db 'RomDump 2.0', ... init: packed record // @@init: _mov_44: array[0..2] of Byte; // mov ax, 4400h _mov_bx: array[0..2] of Byte; // mov bx, 0001h _dos_21: array[0..1] of Byte; // int 21h _jcf_18: array[0..1] of Byte; // jc @@code _and_dx: array[0..3] of Byte; // and dx, 0082h _cmp_dx: array[0..3] of Byte; // cmp dx, 0082h _jne_0E: array[0..1] of Byte; // jne @@code _psh_cs: Byte; // push cs _pop_ds: Byte; // push ds _mov_dx: array[0..2] of Byte; // mov dx, offset @@note _mov_09: array[0..1] of Byte; // mov ah, 09h _int_21: array[0..1] of Byte; // int 21h _mov_4C: array[0..2] of Byte; // mov ax, 4C01h _int_20: array[0..1] of Byte; // int 21h end; // code: packed record // @@code: _mov_cx: Byte; BlockCount: Word; // mov cx, <BlockCount> _mov_dx: Byte; DatSegment: Word; // mov dx, <DatSegment> _jcx_1C: array[0..1] of Byte; // jcxz @@rest end; // loop: packed record // @@loop: _psh_cx: Byte; // push cx _psh_dx: Byte; // push dx _mov_ds: array[0..1] of Byte; // mov ds, dx _mov_dx: Byte; DatOffset: Word; // mov dx, <DatOffset> _mov_cx: array[0..2] of Byte; // mov cx, <BlockSize> _mov_bx: array[0..2] of Byte; // mov bx, 0001h _mov_ax: array[0..2] of Byte; // mov ax, 4000h _int_21: array[0..1] of Byte; // int 21h _pop_dx: Byte; // pop dx _pop_cx: Byte; // pop cx _jcf_1C: array[0..1] of Byte; // jc @@exit _add_dx: array[0..3] of Byte; // add dx, <BlockSize/10h> _lop_E4: array[0..1] of Byte; // loop @@loop end; // rest: packed record // @@rest: _mov_ds: array[0..1] of Byte; // mov ds, dx _mov_dx: Byte; DatOffset: Word; // mov dx, <DatOffset> _mov_cx: Byte; LenghtMod: Word; // mov cx, <LenghtMod> _mov_bx: array[0..2] of Byte; // mov bx, 0001h _mov_ax: array[0..2] of Byte; // mov ax, 4000h _jcx_06: array[0..1] of Byte; // jcxz @@exit _int_21: array[0..1] of Byte; // int 21h _jcf_02: array[0..1] of Byte; // jc @@exit _mov_al: array[0..1] of Byte; // mov al, 00h end; // Exit: packed record // @@exit: _mov_ah: array[0..1] of Byte; // mov ah, 4Ch _int_21: array[0..1] of Byte; // int 21h end; // end; const RomDumpCodeSize = SizeOf(TRomDumpCode) - SizeOf(TImageDosHeader); RomDumpCode: TRomDumpCode = (_header: (e_magic: IMAGE_DOS_SIGNATURE; e_cblp: Word(RomDumpCodeSize) and $1FF; e_cp: Word((RomDumpCodeSize - 1) shr 9) + 1; e_crlc: $0000; e_cparhdr: SizeOf(TImageDosHeader) shr 4; e_minalloc: $0000; e_maxalloc: $FFFF; e_ss: $0000; e_sp: $1000; e_csum: $0000; e_ip: SizeOf(RomDumpCode._notice); e_cs: $0000; e_lfarlc: SizeOf(TImageDosHeader); e_ovno: $0000; e_res: ($0000, $0000, $0000, $0000); e_oemid: $0000; e_oeminfo: $0000; e_res2: ($0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000); _lfanew: $00000000 ); _notice: #13#10+ 'RomDump 2.0'#13#10 + 'Copyright (c) 2003 Nico Bendlin'#13#10 + #13#10+ 'Usage: RomDump > filename'#13#10 + #13#10$'; init: (_mov_44: ($B8, $00, $44); _mov_bx: ($BB, $01, $00); _dos_21: ($CD, $21); _jcf_18: ($72, $18); _and_dx: ($81, $E2, $82, $00); _cmp_dx: ($81, $FA, $82, $00); _jne_0E: ($75, $0E); _psh_cs: $0E; _pop_ds: $1F; _mov_dx: ($BA, $00, $00); _mov_09: ($B4, $09); _int_21: ($CD, $21); _mov_4C: ($B8, $01, $4C); _int_20: ($CD, $21); ); code: (_mov_cx: $B9; BlockCount: $0010; _mov_dx: $BA; DatSegment: $F000; _jcx_1C: ($E3, $1C) ); loop: (_psh_cx: $51; _psh_dx: $52; _mov_ds: ($8E, $DA); _mov_dx: $BA; DatOffset: $0000; _mov_cx: ($B9, Lo(BlockSize), Hi(BlockSize)); _mov_bx: ($BB, $01, $00); _mov_ax: ($B8, $00, $40); _int_21: ($CD, $21); _pop_dx: $5A; _pop_cx: $59; _jcf_1C: ($72, $1C); _add_dx: ($81, $C2, Lo(BlockSize shr 4), Hi(BlockSize shr 4)); _lop_E4: ($E2, $E4) ); rest: (_mov_ds: ($8E, $DA); _mov_dx: $BA; DatOffset: $0000; _mov_cx: $B9; LenghtMod: $0000; _mov_bx: ($BB, $01, $00); _mov_ax: ($B8, $00, $40); _jcx_06: ($E3, $06); _int_21: ($CD, $21); _jcf_02: ($72, $02); _mov_al: ($B0, $00) ); Exit: (_mov_ah: ($B4, $4C); _int_21: ($CD, $21) ) ); begin Result := False; if (RomSize > 0) and (RomSize <= $100000) and (Cardinal(RomBase) < $100000) and (Cardinal(RomBase) + RomSize <= $100000) then begin Size := SizeOf(TRomDumpCode); Code := Pointer(LocalAlloc(LPTR, Size)); if Code <> nil then try PRomDumpCode(Code)^ := RomDumpCode; with PRomDumpCode(Code)^ do begin code.BlockCount := Word(RomSize div BlockSize); code.DatSegment := Word(Cardinal(RomBase) shr 4); loop.DatOffset := Word(Cardinal(RomBase)) and $000F; rest.DatOffset := loop.DatOffset; rest.LenghtMod := Word(RomSize mod BlockSize); end; Result := True; except LocalFree(HLOCAL(Code)); Code := nil; Size := 0; end; end; end;
function _SaveRomDumpCodeToFile(RomBase: Pointer; RomSize: Cardinal; const FileName: string): Boolean; var Code: Pointer; Size: Cardinal; Hand: THandle; Num: DWORD; begin Result := False; if _RomDumpCode(RomBase, RomSize, Code, Size) then try Hand := CreateFile(PChar(FileName), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if Hand <> INVALID_HANDLE_VALUE then try Result := WriteFile(Hand, Code^, Size, Num, nil) and (Num = Size); if not Result then DeleteFile(PChar(FileName)); finally CloseHandle(Hand); end; finally LocalFree(HLOCAL(Code)); end; end;
function _ExecuteRomDumpCode(const Code, Dump: string; Timeout: DWORD): Boolean; var ComSpec: string; StartInfo: TStartupInfo; ProcInfo: TProcessInformation; ErrorMode: Cardinal; begin Result := False; SetLength(ComSpec, MAX_PATH + 1); SetLength(ComSpec, GetEnvironmentVariable('ComSpec', PChar(@ComSpec[1]), MAX_PATH)); if Length(ComSpec) <= 0 then Exit; FillChar(StartInfo, SizeOf(TStartupInfo), 0); StartInfo.cb := SizeOf(TStartupInfo); StartInfo.dwFlags := STARTF_USESHOWWINDOW; StartInfo.wShowWindow := SW_HIDE; ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOGPFAULTERRORBOX or SEM_NOALIGNMENTFAULTEXCEPT or SEM_NOOPENFILEERRORBOX); try if CreateProcess(nil, PChar(ComSpec + ' /C ' + Code + ' > ' + Dump), nil, nil, False, HIGH_PRIORITY_CLASS, nil, nil, StartInfo, ProcInfo) then try Result := (WaitForSingleObject(ProcInfo.hProcess, Timeout) <> WAIT_TIMEOUT); if not Result then TerminateProcess(ProcInfo.hProcess, STATUS_TIMEOUT); finally CloseHandle(ProcInfo.hThread); CloseHandle(ProcInfo.hProcess); end; finally SetErrorMode(ErrorMode); end; end;
function DumpRomBios16(RomBase: Pointer; RomSize: Cardinal; var Dump; Timeout: DWORD): Boolean; var Tmp: array [0..MAX_PATH] of Char; Dmp: array [0..MAX_PATH] of Char; Exe: array [0..MAX_PATH] of Char; Hnd: THandle; Num: DWORD; begin Result := False; if GetTempPath(MAX_PATH, Tmp) > 0 then GetShortPathName(Tmp, Tmp, MAX_PATH) else lstrcpy(Tmp, '.'); if GetTempFileName(Tmp, 'rom', 0, Dmp) > 0 then try lstrcpy(Exe, Dmp); lstrcat(Exe, '.exe'); // Win9x requires .EXE extention if _SaveRomDumpCodeToFile(RomBase, RomSize, Exe) then try if _ExecuteRomDumpCode(Exe, Dmp, Timeout) then begin Hnd := CreateFile(Dmp, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if Hnd <> INVALID_HANDLE_VALUE then try Result := ReadFile(Hnd, Dump, RomSize, Num, nil) and (Num = RomSize); finally CloseHandle(Hnd); end; end; finally DeleteFile(Exe); end; finally DeleteFile(Dmp); end; end;
//////////////////////////////////////////////////////////////////////////////// // // DumpRomBios9x (rdmMemory) // // Win9x maps the BIOS into every process - therefore it's directly accessed. //
function DumpRomBios9x(RomBase: Pointer; RomSize: Cardinal; var Dump): Boolean; begin Result := False; try Move(RomBase^, Dump, RomSize); Result := True; except // ignore exeptions end end;
//////////////////////////////////////////////////////////////////////////////// // // DumpRomBiosNt (rdmPhysical) // // On WinNT the BIOS is accessable through section '\Device\PhysicalMemory'. // This object can only be opened by members of local 'Adminstrators' group. // ZwOpenSection and RtlNtStatusToDosError are documented in newer MSDN/DDK. //
type NTSTATUS = Integer;
PUnicodeString = ^TUnicodeString; TUnicodeString = packed record Length: Word; MaximumLength: Word; Buffer: PWideChar; end;
PObjectAttributes = ^TObjectAttributes; TObjectAttributes = record Length: ULONG; RootDirectory: THandle; ObjectName: PUnicodeString; Attributes: ULONG; SecurityDescriptor: PSecurityDescriptor; SecurityQualityOfService: PSecurityQualityOfService; end;
TFNZwOpenSection = function(out Section: THandle; Access: ACCESS_MASK; Attributes: PObjectAttributes): NTSTATUS; stdcall; TFNRtlNtStatusToDosError = function(Status: NTSTATUS): DWORD; stdcall;
const PhysMemDevName = '\Device\PhysicalMemory'; PhysMemName: TUnicodeString = (Length: Length(PhysMemDevName) * SizeOf(WideChar); MaximumLength: Length(PhysMemDevName) * SizeOf(WideChar) + SizeOf(WideChar); Buffer: PhysMemDevName; ); PhysMemMask: ACCESS_MASK = SECTION_MAP_READ; PhysMemAttr: TObjectAttributes = (Length: SizeOf(TObjectAttributes); RootDirectory: 0; ObjectName: @PhysMemName; Attributes: $00000040; // OBJ_CASE_INSENSITIVE SecurityDescriptor: nil; SecurityQualityOfService: nil; );
var ZwOpenSection: TFNZwOpenSection; RtlNtStatusToDosError: TFNRtlNtStatusToDosError;
function DumpRomBiosNt(RomBase: Pointer; RomSize: Cardinal; var Dump): Boolean; var HMod: HMODULE; Stat: NTSTATUS; Sect: THandle; View: Pointer; begin Result := False; HMod := GetModuleHandle('ntdll.dll'); if HMod = 0 then SetLastError(ERROR_CALL_NOT_IMPLEMENTED) else begin if not Assigned(ZwOpenSection) then ZwOpenSection := GetProcAddress(HMod, 'ZwOpenSection'); if not Assigned(RtlNtStatusToDosError) then RtlNtStatusToDosError := GetProcAddress(HMod, 'RtlNtStatusToDosError'); if not Assigned(ZwOpenSection) or not Assigned(RtlNtStatusToDosError) then SetLastError(ERROR_CALL_NOT_IMPLEMENTED) else begin Stat := ZwOpenSection(Sect, PhysMemMask, @PhysMemAttr); if Stat >= 0 then try View := MapViewOfFile(Sect, PhysMemMask, 0, Cardinal(RomBase), RomSize); if View <> nil then try Move(View^, Dump, RomSize); Result := True; finally UnmapViewOfFile(View); end; finally CloseHandle(Sect); end else SetLastError(RtlNtStatusToDosError(Stat)); end; end; end;
//////////////////////////////////////////////////////////////////////////////// // // DumpRomBios(Ex) // // Public functions to call OS-dependent implementations. //
function DumpRomBios(out Dump: TRomBiosDump; Method: TRomDumpMethod = rdmAutomatic; Timeout: DWORD = 5000): Boolean; begin Result := DumpRomBiosEx(Pointer(RomBiosDumpBase), RomBiosDumpSize, Dump, Method, Timeout); end;
function DumpRomBiosEx(RomBase: Pointer; RomSize: Cardinal; out Dump; Method: TRomDumpMethod = rdmAutomatic; Timeout: DWORD = 5000): Boolean; begin Result := False; case Method of rdmAutomatic: if (GetVersion() and $80000000) <> 0 then Result := DumpRomBios9x(RomBase, RomSize, Dump) else begin Result := DumpRomBiosNt(RomBase, RomSize, Dump); if not Result then DumpRomBios16(RomBase, RomSize, Dump, DWORD(Timeout)); end; rdmGeneric: Result := DumpRomBios16(RomBase, RomSize, Dump, DWORD(Timeout)); rdmMemory: Result := DumpRomBios9x(RomBase, RomSize, Dump); rdmPhysical: Result := DumpRomBiosNt(RomBase, RomSize, Dump); else SetLastError(ERROR_INVALID_PARAMETER); end; end;
//////////////////////////////////////////////////////////////////////////////// // // ReadRomDumpBuffer(Ex) / GetRomDumpAddr(Ex) // // Utilities to simplify the access to dumps. //
procedure ReadRomDumpBuffer(const Dump: TRomBiosDump; Addr: Pointer; var Buffer; Size: Cardinal); begin Move(Dump[Cardinal(Addr)], Buffer, Size); end;
procedure ReadRomDumpBufferEx(const Dump; Base, Addr: Pointer; var Buffer; Size: Cardinal); begin Move(Pointer(Cardinal(@Dump) + Cardinal(Addr) - Cardinal(Base))^, Buffer, Size); end;
function GetRomDumpAddr(const Dump: TRomBiosDump; Addr: Pointer): Pointer; begin Result := @Dump[Cardinal(Addr)]; end;
function GetRomDumpAddrEx(const Dump; Base, Addr: Pointer): Pointer; begin Result := Pointer(Cardinal(@Dump) + Cardinal(Addr) - Cardinal(Base)); end;
end.
|