I have used for years this PB code to load a DLL from memory.
LoadDLLfromMemory
is able to load a DLL completely from memory, without storing it on the disk first.
This code is based on the work of Semen and Lothar posted long ago on the PowerBASIC forum.
'+--------------------------------------------------------------------------+
'| |
'| (LoadDLLfromMemory) |
'| |
'| Author Patrice TERRIER |
'| copyright(c) 2007-2014 |
'| www.zapsolution.com |
'| pterrier@zapsolution.com |
'| |
'+--------------------------------------------------------------------------+
'| Project started on : 00-06-2007 (MM-DD-YYYY) |
'| Last revised : 01-02-2014 (MM-DD-YYYY) |
'+--------------------------------------------------------------------------+
#COMPILE EXE "test.exe"
#RESOURCE "mydll.pbr"
macro const = macro
const IMAGE_ORDINAL_FLAG = &H80000000
const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
const IMAGE_NT_OPTIONAL_HDR32_MAGIC = &H10b
const IMAGE_DOS_SIGNATURE = &H5A4D ' MZ
const IMAGE_OS2_SIGNATURE = &H454E ' NE
const IMAGE_OS2_SIGNATURE_LE = &H454C ' LE
const IMAGE_VXD_SIGNATURE = &H454C ' LE
const IMAGE_NT_SIGNATURE = &H00004550 ' PE00
const IMAGE_DIRECTORY_ENTRY_EXPORT = 0 ' // Export Directory
const IMAGE_DIRECTORY_ENTRY_IMPORT = 1 ' // Import Directory
const IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 ' // Resource Directory
const IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 ' // Exception Directory
const IMAGE_DIRECTORY_ENTRY_SECURITY = 4 ' // Security Directory
const IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 ' // Base Relocation Table
const IMAGE_DIRECTORY_ENTRY_DEBUG = 6 ' // Debug Directory
'// IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 // (X86 usage)
const IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 ' // (X86 usage)
const IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 ' // Architecture Specific Data
const IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 ' // RVA of GP
const IMAGE_DIRECTORY_ENTRY_TLS = 9 ' // TLS Directory
const IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 ' // Load Configuration Directory
const IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 ' // Bound Import Directory in headers
const IMAGE_DIRECTORY_ENTRY_IAT = 12 ' // Import Address Table
const IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 ' // Delay Load Import Descriptors
const IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 ' // COM Runtime descriptor
const IMAGE_SIZEOF_SHORT_NAME = 8
const MEM_COMMIT = &H1000???
const PAGE_READONLY = &H02???
const PAGE_READWRITE = &H04???
const PAGE_EXECUTE_READ = &H20???
const PAGE_EXECUTE_READWRITE = &H40???
const DLL_PROCESS_DETACH = 0
const DLL_PROCESS_ATTACH = 1
const MEM_RELEASE = &H8000???
const RT_RCDATA = 10
type IMAGE_EXPORT_DIRECTORY dword fill
Characteristics as dword
TimeDateStamp as dword
MajorVersion as word
MinorVersion as word
nName as dword
nBase as dword
NumberOfFunctions as dword
NumberOfNames as dword
AddressOfFunctions as dword ' // RVA from base of image
AddressOfNames as dword ' // RVA from base of image
AddressOfNameOrdinals as dword ' // RVA from base of image
end type
' // Size = 64 bytes
type IMAGE_DOS_HEADER byte
e_magic as word ' word // Magic number
e_cblp as word ' word // Bytes on last page of file
e_cp as word ' word // Pages in file
e_crlc as word ' word // Relocations
e_cparhdr as word ' word // Size of header in paragraphs
e_minalloc as word ' word // Minimum extra paragraphs needed
e_maxalloc as word ' word // Maximum extra paragraphs needed
e_ss as word ' word // Initial (relative) SS value
e_sp as word ' word // Initial SP value
e_csum as word ' word // Checksum
e_ip as word ' word // Initial IP value
e_cs as word ' word // Initial (relative) CS value
e_lfarlc as word ' word // File address of relocation table
e_ovno as word ' word // Overlay number
e_res(3) as word ' word[4] // Reserved words
e_oemid as word ' word // OEM identifier (for e_oeminfo)
e_oeminfo as word ' word // OEM information; e_oemid specific
e_res2(9) as word ' word[10] // Reserved words
e_lfanew as long ' long // File address of new exe header
end type
type IMAGE_FILE_HEADER dword fill
Machine as word
NumberOfSections as word
TimeDateStamp as dword
PointerToSymbolTable as dword
NumberOfSymbols as dword
SizeOfOptionalHeader as word
Characteristics as word
end type
type IMAGE_DATA_DIRECTORY dword
VirtualAddress as dword
nSize as dword
end type
type IMAGE_OPTIONAL_HEADER dword fill
' Standard fields.
Magic as word
MajorLinkerVersion as byte
MinorLinkerVersion as byte
SizeOfCode as dword
SizeOfInitializedData as dword
SizeOfUninitializedData as dword
AddressOfEntryPoint as dword
BaseOfCode as dword
BaseOfData as dword
' NT additional fields.
ImageBase as dword
SectionAlignment as dword
FileAlignment as dword
MajorOperatingSystemVersion as word
MinorOperatingSystemVersion as word
MajorImageVersion as word
MinorImageVersion as word
MajorSubsystemVersion as word
MinorSubsystemVersion as word
Win32VersionValue as dword
SizeOfImage as dword
SizeOfHeaders as dword
CheckSum as dword
Subsystem as word
DllCharacteristics as word
SizeOfStackReserve as dword
SizeOfStackCommit as dword
SizeOfHeapReserve as dword
SizeOfHeapCommit as dword
LoaderFlags as dword
NumberOfRvaAndSizes as dword
DataDirectory(IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1) as IMAGE_DATA_DIRECTORY
end type
type IMAGE_NT_HEADERS dword
Signature as dword
FileHeader as IMAGE_FILE_HEADER
OptionalHeader as IMAGE_OPTIONAL_HEADER
end type
' // Size = 4 bytes
type SYSTEM_INFO_UNION_STRUCT word
wProcessorArchitecture as word ' word wProcessorArchitecture
wReserved as word ' word wReserved
end type
' // Size = 4 bytes
union SYSTEM_INFO_UNION dword
dwOemId as dword ' dword dwOemId; // Obsolete field...do not use
SYSTEM_INFO_UNION_STRUCT
end union
' // Size = 36 bytes
type SYSTEM_INFO dword fill
SYSTEM_INFO_UNION
dwPageSize as dword ' dwPageSize
lpMinimumApplicationAddress as dword ' lpMinimumApplicationAddress
lpMaximumApplicationAddress as dword ' lpMaximumApplicationAddress
dwActiveProcessorMask as dword ' dwActiveProcessorMask
dwNumberOfProcessors as dword ' dwNumberOfProcessors;
dwProcessorType as dword ' dwProcessorType
dwAllocationGranularity as dword ' dwAllocationGranularity
wProcessorLevel as word ' wProcessorLevel
wProcessorRevision as word ' wProcessorRevision
end type
union MiscUnionISH dword
PhysicalAddress as dword ' dword
VirtualSize as dword ' dword
end union
union IMAGE_SECTION_HEADER_NAME_UNION
Name as string * IMAGE_SIZEOF_SHORT_NAME ' byte
'// for compatibility wirh the PB headers
bName(IMAGE_SIZEOF_SHORT_NAME - 1) as byte
end union
' // Size = 40 bytes
type IMAGE_SECTION_HEADER dword fill
IMAGE_SECTION_HEADER_NAME_UNION
Misc as MiscUnionISH
VirtualAddress as dword
SizeOfRawData as dword
PointerToRawData as dword
PointerToRelocations as dword
PointerToLinenumbers as dword
NumberOfRelocations as word
NumberOfLinenumbers as word
Characteristics as dword
end type
type IMAGE_IMPORT_BY_NAME
Hint as word
ImpName as asciiz * 254
end type
type IMAGE_IMPORT_DESCRIPTOR
OriginalFirstThunk as dword
TimeDateStamp as dword
ForwarderChain as dword
pName as dword
FirstThunk as dword
end type
type IMAGE_BASE_RELOCATION
VirtualAddress as dword
SizeOfBlock as dword
end type
declare sub GetSystemInfo lib "KERNEL32.DLL" ALIAS "GetSystemInfo" (byref lpSystemInfo as SYSTEM_INFO)
declare function VirtualAlloc lib "KERNEL32.DLL" ALIAS "VirtualAlloc" (byval lpAddress as dword, byval dwSize as dword, byval flAllocationType as dword, byval flProtect as dword) as dword
declare function VirtualProtect lib "KERNEL32.DLL" ALIAS "VirtualProtect" (byval lpAddress as dword, byval dwSize as dword, byval flNewProtect as dword, byref lpflOldProtect as dword) as long
declare function VirtualFree lib "KERNEL32.DLL" ALIAS "VirtualFree" (byval lpAddress as dword, byval dwSize as dword, byval dwFreeType as dword) as long
declare sub MoveMemory lib "KERNEL32.DLL" ALIAS "RtlMoveMemory" (pDestination as any, pSource as any, byval cbLength as long)
declare function GetModuleHandle lib "KERNEL32.DLL" ALIAS "GetModuleHandleA" (lpModuleName as asciiz) as dword
declare function FindResource lib "KERNEL32.DLL" ALIAS "FindResourceA" (byval hInstance as dword, lpName as asciiz, lpType as asciiz) as long
declare function SizeofResource lib "KERNEL32.DLL" ALIAS "SizeofResource" (byval hInstance as dword, byval hResInfo as dword) as long
declare function LockResource lib "KERNEL32.DLL" ALIAS "LockResource" (byval hResData as dword) as dword
declare function LoadResource lib "KERNEL32.DLL" ALIAS "LoadResource" (byval hInstance as dword, byval hResInfo as dword) as long
declare function GetProcAddress lib "KERNEL32.DLL" alias "GetProcAddress" (byval hModule as dword, lpProcName as asciiz) as long
declare function LoadLibrary lib "KERNEL32.DLL" alias "LoadLibraryA" (lpLibFileName as asciiz) as long
'===========================================================================
declare function EntryPoint(byval hInstance as dword, byval Reason as dword, byval Reserved as dword) as long
function GetProcAddressDirectly (byval lpImageDosHeader as IMAGE_DOS_HEADER ptr, FuncName as asciiz) as dword
local lpImageNtHeaders as IMAGE_NT_HEADERS ptr
local lpImageExportDirectory as IMAGE_EXPORT_DIRECTORY ptr
local lpNameOrdinals as word ptr
local lpName, lpFunctions as dword ptr
local lpFuncName, lpExpFuncName as asciiz ptr
local i, j as dword
if (@lpImageDosHeader.e_magic <> IMAGE_DOS_SIGNATURE) then
function = 1: exit function ' invalid DOS signature
end if
lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
if (@lpImageNtHeaders.Signature <> IMAGE_NT_SIGNATURE) then
function = 1: exit function ' invalid NT signature
end if
if (@lpImageNtHeaders.FileHeader.SizeOfOptionalHeader <> sizeof(@lpImageNtHeaders.OptionalHeader) or _
@lpImageNtHeaders.OptionalHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC) then
exit function
end if
lpImageExportDirectory = @lpImageNtHeaders.OptionalHeader.DataDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT).VirtualAddress
if (lpImageExportDirectory = 0) then
exit function
end if
lpImageExportDirectory = lpImageExportDirectory + lpImageDosHeader
lpNameOrdinals = @lpImageExportDirectory.AddressOfNameOrdinals + lpImageDosHeader
lpName = @lpImageExportDirectory.AddressOfNames + lpImageDosHeader
lpFunctions = @lpImageExportDirectory.AddressOfFunctions + lpImageDosHeader
lpFuncName = varptr(FuncName)
if (HI(WORD, lpFuncName)) then ' Name
for i = 0 to @lpImageExportDirectory.NumberOfFunctions - 1
if (@lpFunctions[i]) then
for j = 0 to @lpImageExportDirectory.NumberOfNames - 1
if (@lpNameOrdinals[j] = i) then
lpExpFuncName = @lpName[j] + lpImageDosHeader
if (@lpExpFuncName = FuncName) then
function = @lpFunctions[i] + lpImageDosHeader
exit function
end if
end if
next
end if
next
else
for i = 0 to @lpImageExportDirectory.NumberOfFunctions - 1
if (lpFuncName = @lpImageExportDirectory.nBase + i) then
if (@lpFunctions[i]) then
function = @lpFunctions[i] + lpImageDosHeader
end if
exit function
end if
next
end if
end function
function LoadDllfromMemory (byval lpRawDll as dword, byval RawDllSize as dword, lpImageDll as dword) as long
local nError as long
local sSysInfo as SYSTEM_INFO
local lpImageDosHeader as IMAGE_DOS_HEADER ptr
local lpImageNtHeaders as IMAGE_NT_HEADERS ptr
local lpImageSectionHeader as IMAGE_SECTION_HEADER ptr
local lpImageImportDescriptor as IMAGE_IMPORT_DESCRIPTOR ptr
local lpImageImportByName as IMAGE_IMPORT_BY_NAME ptr
local lpImageBaseRelocTable as IMAGE_BASE_RELOCATION ptr
local lpDllName as asciiz ptr
local szDllName as string
local lpTypeOffset as word ptr
local TpOffset as word
local lpFuncNameRef, lpFuncAddr, lpLink as dword ptr
local ImagePages, hDll, fOldProtect, i, j, k, Pg, Pg1, Pg2, Addr1, Addr2 as dword
local Protection() as byte
lpImageDosHeader = lpRawDll
if (RawDllSize < sizeof(IMAGE_DOS_HEADER)) then
nError = 1: goto BailOut
end if
if (@lpImageDosHeader.e_magic <> IMAGE_DOS_SIGNATURE) then
nError = 1: goto BailOut ' invalid DOS signature
end if
if (RawDllSize < @lpImageDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS)) then
nError = 1: goto BailOut
end if
lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
if (@lpImageNtHeaders.Signature <> IMAGE_NT_SIGNATURE) then
nError = 1: goto BailOut ' invalid NT signature
end if
if (@lpImageNtHeaders.FileHeader.SizeOfOptionalHeader <> sizeof(@lpImageNtHeaders.OptionalHeader) or _
@lpImageNtHeaders.OptionalHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC) then
nError = 1: goto BailOut
end if
if (@lpImageNtHeaders.FileHeader.NumberOfSections < 1) then
nError = 1: goto BailOut
end if
for i = 0 to IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1
if (@lpImageNtHeaders.OptionalHeader.DataDirectory(i).VirtualAddress <> 0) then
select case as long i
case IMAGE_DIRECTORY_ENTRY_EXPORT ' Export Directory
case IMAGE_DIRECTORY_ENTRY_IMPORT ' Import Directory
case IMAGE_DIRECTORY_ENTRY_RESOURCE ' Resource Directory
case IMAGE_DIRECTORY_ENTRY_BASERELOC ' Base Relocation Table
case IMAGE_DIRECTORY_ENTRY_IAT ' Import Address Table
case else ' Strange for PB
nError = 2: goto BailOut
end select
end if
next
lpImageSectionHeader = lpImageNtHeaders + sizeof(IMAGE_NT_HEADERS)
k = lpImageSectionHeader - lpImageDosHeader + sizeof(IMAGE_SECTION_HEADER) * @lpImageNtHeaders.FileHeader.NumberOfSections
j = k
for i = 0 to @lpImageNtHeaders.FileHeader.NumberOfSections - 1
j = max(j, @lpImageSectionHeader[i].VirtualAddress + @lpImageSectionHeader[i].SizeOfRawData)
next
GetSystemInfo(sSysInfo)
ImagePages = j \ sSysInfo.dwPageSize
if (j Mod sSysInfo.dwPageSize) then incr ImagePages
lpImageDll = VirtualAlloc(byval 0, cdwd(ImagePages * sSysInfo.dwPageSize), MEM_COMMIT, PAGE_EXECUTE_READWRITE)
if (lpImageDll = 0) then
nError = 5: goto BailOut
end if
MoveMemory(byval lpImageDll, byval lpRawDll, k)
for i = 0 to @lpImageNtHeaders.FileHeader.NumberOfSections - 1
MoveMemory(byval cdwd(lpImageDll + @lpImageSectionHeader[i].VirtualAddress), _
byval cdwd(lpRawDll + @lpImageSectionHeader[i].PointerToRawData), @lpImageSectionHeader[i].SizeOfRawData)
next
'// Switch to new image
lpImageDosHeader = lpImageDll
lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
lpImageSectionHeader = lpImageNtHeaders + sizeof(IMAGE_NT_HEADERS)
'// Start Original code BY Lothar
lpImageImportDescriptor = @lpImageNtHeaders.OptionalHeader.DataDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT).VirtualAddress
if (lpImageImportDescriptor <> 0) then
lpImageImportDescriptor = lpImageImportDescriptor + lpImageDosHeader
while @lpImageImportDescriptor.OriginalFirstThunk <> 0 or @lpImageImportDescriptor.FirstThunk <> 0 ' Dlls
lpDllName = @lpImageImportDescriptor.pName + lpImageDosHeader
szDllName = @lpDllName
hDll = GetModuleHandle(byval strptr(szDllName))
if (hDll = 0) then hDll = LoadLibrary(byval strptr(szDllName))
if (hDll = 0) then
nError = 2: goto BailOut '// Can't find
end if
lpFuncNameRef = @lpImageImportDescriptor.OriginalFirstThunk + lpImageDosHeader
lpFuncAddr = @lpImageImportDescriptor.FirstThunk + lpImageDosHeader
if lpFuncNameRef = lpImageDosheader then lpFuncNameRef = lpFuncAddr
while @lpFuncNameRef <> 0
lpImageImportByName = @lpFuncNameRef + lpImageDosHeader
if (@lpFuncNameRef and IMAGE_ORDINAL_FLAG) then
@lpFuncAddr = GetProcAddress(hDll, byval @lpFuncNameRef and &HFFFF???)
else
@lpFuncAddr = GetProcAddress(hDll, @lpImageImportByName.ImpName)
end if
if (@lpFuncAddr = 0) then
nError = 2: goto BailOut
end if
incr lpFuncAddr
incr lpFuncNameRef
wend
incr lpImageImportDescriptor
loop
end if
'// End Original code BY Lothar
lpImageBaseRelocTable = @lpImageNtHeaders.OptionalHeader.DataDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC).VirtualAddress
if (lpImageBaseRelocTable <> 0) then
lpImageBaseRelocTable = lpImageBaseRelocTable + lpImageDosHeader
while @lpImageBaseRelocTable.VirtualAddress <> 0
lpTypeOffset = lpImageBaseRelocTable + sizeof(IMAGE_BASE_RELOCATION)
while lpTypeOffset < lpImageBaseRelocTable + @lpImageBaseRelocTable.SizeOfBlock
TpOffset = @lpTypeOffset and &HF000??
if (TpOffset = &H3000) then
lpLink = lpImageDosHeader + @lpImageBaseRelocTable.VirtualAddress + (@lpTypeOffset and &HFFF??)
@lpLink = @lpLink - @lpImageNtHeaders.OptionalHeader.ImageBase + lpImageDosHeader
elseif TpOffSet = 0 then
else
nError = 3: goto BailOut ' Uknown type
end if
incr lpTypeOffset
wend
lpImageBaseRelocTable = lpImageBaseRelocTable + @lpImageBaseRelocTable.SizeOfBlock
wend
end if
redim Protection(ImagePages - 1)
for i = 0 to @lpImageNtHeaders.FileHeader.NumberOfSections
if (i = @lpImageNtHeaders.FileHeader.NumberOfSections) then
Addr1 = 0: Addr2 = k: j = &H60000000??? '// PAGE_EXECUTE_READ
else
Addr1 = @lpImageSectionHeader[i].VirtualAddress
Addr2 = @lpImageSectionHeader[i].SizeOfRawData
j = @lpImageSectionHeader[i].Characteristics
end if
Addr2 = Addr1 + Addr2 - 1
Pg1 = Addr1 \ sSysInfo.dwPageSize
Pg2 = Addr2 \ sSysInfo.dwPageSize
for Pg = Pg1 to Pg2
if (j and &H20000000???) then Protection(Pg) = Protection(Pg) or 1 ' Execute
if (j and &H40000000???) then Protection(Pg) = Protection(Pg) or 2 ' Read
if (j and &H80000000???) then Protection(Pg) = Protection(Pg) or 4 ' Write
next
next
Addr1 = lpImageDosHeader
for Pg = 0 to ImagePages - 1
select case as long Protection(Pg)
case 2: fOldProtect = PAGE_READONLY
case 3: fOldProtect = PAGE_EXECUTE_READ
case 6: fOldProtect = PAGE_READWRITE
case else: fOldProtect = PAGE_EXECUTE_READWRITE ' Ignore strange combinations
end select
if (fOldProtect <> PAGE_EXECUTE_READWRITE) then
if (VirtualProtect (byval Addr1, sSysInfo.dwPageSize, fOldProtect, fOldProtect) = 0) then
nError = 4: goto BailOut
end if
end if
Addr1 = Addr1 + sSysInfo.dwPageSize
next
i = @lpImageNtHeaders.OptionalHeader.AddressOfEntryPoint + lpImageDosHeader
CALL DWORD i using EntryPoint (lpImageDosHeader, DLL_PROCESS_ATTACH, 0)
BailOut:
function = nError
end function
function Unload_DLL (byval lpImageDosHeader as IMAGE_DOS_HEADER ptr) as dword
local lpImageNtHeaders as IMAGE_NT_HEADERS ptr
local i as dword
lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
i = @lpImageNtHeaders.OptionalHeader.AddressOfEntryPoint + lpImageDosHeader
CALL DWORD i using EntryPoint (lpImageDosHeader, DLL_PROCESS_DETACH, 0)
function = VirtualFree (byval lpImageDosHeader, 0, MEM_RELEASE)
end function
function Load_DLL(lpName as ASCIIZ) as dword
local nError as long
local hLib, hResource, buffersize, pResourceData, hInstance as DWORD
hInstance = GetModuleHandle("")
hResource = FindResource(hInstance, lpName, byval RT_RCDATA)
if (hResource) then
buffersize = SizeofResource(hInstance, hResource)
if (buffersize > 0) then
pResourceData = LockResource(LoadResource(hInstance, hResource))
if (pResourceData) then
nError = LoadDllfromMemory(byval pResourceData, buffersize, hLib)
if (nError) then hLib = 0
end if
end if
end if
function = hLib
end function
function Load_DLL((lpName as ASCIIZ)) as dword
is used to load the DLL from resource.