...read the ROM-BIOS?

Author: Nico Bendlin
Homepage: http://www.bendlins.de/nico/

Category: System


// 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, niland (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),
      nilnil, False, HIGH_PRIORITY_CLASS, nilnil, 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, niland (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.

 

printed from
www.swissdelphicenter.ch
developers knowledge base