I'm sure I've seen this mentioned around here, but I just searched and couldn't find it.
Is is possible to load a procedure from a DLL into memory and run that procedure? And get it's return values? Basically, I want to copy a few functions from a lib, then close the lib, but access the functions using prototypes.
I'm thinking I can use the GetFunction to get the address while the library is open, but what about the size?
Copy and run functions from memory
Copy and run functions from memory
----------------------------------------------------------------------------
Commenting your own code is admitting you don't understand it.
----------------------------------------------------------------------------
Commenting your own code is admitting you don't understand it.
----------------------------------------------------------------------------
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
Yes, if the dll function in question accesses static or global variables, for example, then it will invariably attempt to access memory within that part of the address space into which the dll is mapped and given over to one of it's data sections. Such a function, whether copied elsewhere into memory, or not, will thus require access to that memory and thus require the dll to remain mapped into the process' memory space.
With the dll thus in memory, you may as well call the aforementioned function as per normal; with prototypes/CallFunction() etc. or through imports as suggested by netmaestro. There are loads of examples of doing this in these forums.
With the dll thus in memory, you may as well call the aforementioned function as per normal; with prototypes/CallFunction() etc. or through imports as suggested by netmaestro. There are loads of examples of doing this in these forums.
I may look like a mule, but I'm not a complete ass.
-
- Enthusiast
- Posts: 665
- Joined: Fri Sep 12, 2003 10:40 pm
- Location: Tallahassee, Florida
It actually is possible. There is a tool for the reverser community called Function Replacer 1.0 by Execution.
It replaces one DLL function with another, from entry point to return. So mapping a DLL function to memory is possible, because this tool does it and then realigns the DLL.
It replaces one DLL function with another, from entry point to return. So mapping a DLL function to memory is possible, because this tool does it and then realigns the DLL.
Code: Select all
!.WHILE status != dwPassedOut
! Invoke AllocateDrink, dwBeerAmount
!MOV Mug, Beer
!Invoke Drink, Mug, dwBeerAmount
!.endw
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
No, this is not the same at all.localmotion34 wrote:It actually is possible. There is a tool for the reverser community called Function Replacer 1.0 by Execution.
It replaces one DLL function with another, from entry point to return. So mapping a DLL function to memory is possible, because this tool does it and then realigns the DLL.
It is possible to replace a dll function with another, since you only change the export table to point to the new function (or insert a jump to the new function at the entry point of the old function).
It is not possible to extract the old function, ever.
There is your problem. There is absolutely no guarantee that the function has only one return.from entry point to return
Example asm:
Code: Select all
SillyFunction:
dostuff
jne path1
path2:
ret
path1:
dostuff
jne path2
jmp path3
BogoFunction:
dostuff
jmp path2
path3:
dostuff
ret
In fact, a ret can belong to any number of function. In this case the ret in path2 is also used by BogoFunction. Two functions shares the same code (usually as a compiler optimization or done in hand-written assembly). By physically replacing SillyFunction you'll break BogoFunction. Thus, the replacement must be done by altering the export table or inserting a jump to the new function at the start of SillyFunction. It is not possible to know where SillyFunction extends to, ever. Simply because in asm, there is no concept of functions, and code from various functions can be interleaved (done by compilers for alignment or cache purposes) or even shared, and there is absolutely no way to know.
Are you taking about intercepting a function in a .dll and replacing it with your own? Hooking and detouring?
A generic example from the working example by Rings > http://purebasic.fr/english/viewtopic.php?t=22678
Or maybe more in line with your question regarding using prototypes, heres something I use for getting the Process ID (PID) of a running executable (which is in memory), you can see the use of getting the library and calling its functions and setting up the prototypes for those functions:
Or maybe more generically:
And to show an example of use in a current project I'm using to show FPS differences between Graphics cards in the same environment in Windows Vista:
Does this help at all or am I not understanding what your after? (I'm probably not understanding - I can be that way sometimes
)
Hope it helps.
A generic example from the working example by Rings > http://purebasic.fr/english/viewtopic.php?t=22678
Code: Select all
Global Dim Backup.b(5)
Procedure MyFunction(a.l,b.l,c.l,d.l)
< do something in my own function here>
ProcedureReturn
EndProcedure
Procedure Hook(Libname.s,FuncName.s,NewFunctionAddress)
dwAddr =GetProcAddress_(GetModuleHandle_(LibName), FuncName)
OriginalAdress=dwAddr
Result=ReadProcessMemory_(GetCurrentProcess_(), dwAddr, @Backup(0), 6, @readbytes)
Dim a.b(6)
a(0)=$e9
a(5)=$C3
dwCalc = NewFunctionAddress - dwAddr - 5
CopyMemory(@dwCalc,@a(1),4)
Result = WriteProcessMemory_(GetCurrentProcess_(), dwAddr, @a(0), 6, @written);
EndProcedure
Procedure UnHook(Libname.s,FuncName.s)
dwAddr = GetProcAddress_(GetModuleHandle_(LibName), FuncName)
Result= WriteProcessMemory_(GetCurrentProcess_(), dwAddr, @Backup(0), 6, @written);
EndProcedure
; Hook("<the dll here>, "<the function in the dll to hook>, "< call our own function instead >"
Hook("Some.dll", "Dll_Function", @MyFunction());
UnHook("Some.dll", "Dll_Function")
Or maybe more in line with your question regarding using prototypes, heres something I use for getting the Process ID (PID) of a running executable (which is in memory), you can see the use of getting the library and calling its functions and setting up the prototypes for those functions:
Code: Select all
Prototype.i PFNCreateToolhelp32Snapshot(dwFlags.i, th32ProcessID.i) ;
Prototype.b PFNProcess32First(hSnapshot.i, *lppe.PROCESSENTRY32) ;
Prototype.b PFNProcess32Next(hSnapshot.i, *lppe.PROCESSENTRY32) ;
Procedure GetPidByName(p_name$)
Protected hDLL.i, process_name$
Protected PEntry.PROCESSENTRY32, hTool32.i
Protected pCreateToolhelp32Snapshot.PFNCreateToolhelp32Snapshot
Protected pProcess32First.PFNProcess32First
Protected pProcess32Next.PFNProcess32Next
Protected pid.i
hDLL = OpenLibrary(#PB_Any,"kernel32.dll")
If hDLL
pCreateToolhelp32Snapshot = GetFunction(hDLL,"CreateToolhelp32Snapshot")
pProcess32First = GetFunction(hDLL,"Process32First")
pProcess32Next = GetFunction(hDLL,"Process32Next")
Else
ProcedureReturn 0
EndIf
PEntry\dwSize = SizeOf(PROCESSENTRY32)
hTool32 = pCreateToolhelp32Snapshot(#TH32CS_SNAPPROCESS, 0)
pProcess32First(hTool32, @PEntry)
process_name$ = Space(#MAX_PATH)
CopyMemory(@PEntry\szExeFile,@process_name$,#MAX_PATH)
If UCase(process_name$) = UCase(p_name$)
ProcedureReturn PEntry\th32ProcessID
EndIf
While pProcess32Next(hTool32, @PEntry) > 0
process_name$ = Space(#MAX_PATH)
CopyMemory(@PEntry\szExeFile,@process_name$,#MAX_PATH)
If UCase(process_name$) = UCase(p_name$)
ProcedureReturn PEntry\th32ProcessID
EndIf
Wend
CloseLibrary(hDLL)
ProcedureReturn 0
EndProcedure
Or maybe more generically:
Code: Select all
Lib_Mine = LoadLibrary_("My.dll")
If Lib_Mine
*h_Lib_Mine_Func1 = GetProcAddress_(Lib_Mine, "DLL_First_Function")
*h_Lib_Mine_Func2 = GetProcAddress_(Lib_Mine, "DLL_Second_Function")
*h_Lib_Mine_Func3 = GetProcAddress_(Lib_Mine, "DLL_Third_Function")
*h_Lib_Mine_Func4 = GetProcAddress_(Lib_Mine, "DLL_Fourth_Function")
FreeLibrary_(Lib_Mine)
EndIf
CallFunctionFast(*h_Lib_Mine_Func1,a, b)
CallFunctionFast(*h_Lib_Mine_Func2,a, b)
CallFunctionFast(*h_Lib_Mine_Func3,a, b)
CallFunctionFast(*h_Lib_Mine_Func4,a, b)
or....maybe like this:
Lib_Mine = LoadLibrary_("My.dll")
If Lib_Mine
*h_Lib_Mine_Func1 = GetProcAddress_(Lib_Mine, "DLL_First_Function")
*h_Lib_Mine_Func2 = GetProcAddress_(Lib_Mine, "DLL_Second_Function")
*h_Lib_Mine_Func3 = GetProcAddress_(Lib_Mine, "DLL_Third_Function")
*h_Lib_Mine_Func4 = GetProcAddress_(Lib_Mine, "DLL_Fourth_Function")
CallFunctionFast(*h_Lib_Mine_Func1,a, b)
CallFunctionFast(*h_Lib_Mine_Func2,a, b)
CallFunctionFast(*h_Lib_Mine_Func3,a, b)
CallFunctionFast(*h_Lib_Mine_Func4,a, b)
FreeLibrary_(Lib_Mine)
EndIf
Code: Select all
; gpcomms.dll - proxy dll project from > http://www.mikoweb.eu/index.php?node=28
; injector part from hipy001 at > http://www.purebasic.fr/english/viewtopic.php?t=37607
; other parts in PureBasic form and credit goes to their original authors
Prototype.i PFNCreateToolhelp32Snapshot(dwFlags.i, th32ProcessID.i) ;
Prototype.b PFNProcess32First(hSnapshot.i, *lppe.PROCESSENTRY32) ;
Prototype.b PFNProcess32Next(hSnapshot.i, *lppe.PROCESSENTRY32) ;
Procedure GPSI_ShowFPS(bShowFPS.i)
Lib_gpcomms = LoadLibrary_("gpcomms.dll")
If Lib_gpcomms
*h_GPSI_ShowFPS = GetProcAddress_(Lib_gpcomms, "GPSI_ShowFPS")
ret.i = CallFunctionFast(*h_GPSI_ShowFPS, bShowFPS)
FreeLibrary_(Lib_gpcomms)
EndIf
ProcedureReturn ret
EndProcedure
Procedure.b CheckRunningExe(FileName.s)
Protected snap.i , Proc32.PROCESSENTRY32 , dll_kernel32.i
FileName = GetFilePart(FileName)
dll_kernel32 = OpenLibrary(#PB_Any, "kernel32.dll")
If dll_kernel32
snap = CallFunction(dll_kernel32, "CreateToolhelp32Snapshot",$2, 0)
If snap
Proc32\dwSize = SizeOf(PROCESSENTRY32)
If CallFunction(dll_kernel32, "Process32First", snap, @Proc32)
While CallFunction(dll_kernel32, "Process32Next", snap, @Proc32)
If PeekS(@Proc32\szExeFile)=FileName
CloseHandle_(snap)
CloseLibrary(dll_kernel32)
ProcedureReturn #True
EndIf
Wend
EndIf
CloseHandle_(snap)
EndIf
CloseLibrary(dll_kernel32)
EndIf
ProcedureReturn #False
EndProcedure
Procedure Elevated_Cmd(app_dir.s, app_name.s)
AppVerb$ = "runas"
AppName$ = app_name
AppDir$ = app_dir
shExecInfo.SHELLEXECUTEINFO
shExecInfo\cbSize=SizeOf(SHELLEXECUTEINFO)
shExecInfo\fMask=#Null
shExecInfo\hwnd=#Null
shExecInfo\lpVerb=@AppVerb$
shExecInfo\lpFile=@AppName$
shExecInfo\lpDirectory=@AppDir$
shExecInfo\nShow=#SW_NORMAL
exe.i = ShellExecuteEx_(shExecInfo)
Delay(3000)
If CheckRunningExe(app_name) = #True
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure GetPidByName(p_name$)
Protected hDLL.i, process_name$
Protected PEntry.PROCESSENTRY32, hTool32.i
Protected pCreateToolhelp32Snapshot.PFNCreateToolhelp32Snapshot
Protected pProcess32First.PFNProcess32First
Protected pProcess32Next.PFNProcess32Next
Protected pid.i
hDLL = OpenLibrary(#PB_Any,"kernel32.dll")
If hDLL
pCreateToolhelp32Snapshot = GetFunction(hDLL,"CreateToolhelp32Snapshot")
pProcess32First = GetFunction(hDLL,"Process32First")
pProcess32Next = GetFunction(hDLL,"Process32Next")
Else
ProcedureReturn 0
EndIf
PEntry\dwSize = SizeOf(PROCESSENTRY32)
hTool32 = pCreateToolhelp32Snapshot(#TH32CS_SNAPPROCESS, 0)
pProcess32First(hTool32, @PEntry)
process_name$ = Space(#MAX_PATH)
CopyMemory(@PEntry\szExeFile,@process_name$,#MAX_PATH)
If UCase(process_name$) = UCase(p_name$)
ProcedureReturn PEntry\th32ProcessID
EndIf
While pProcess32Next(hTool32, @PEntry) > 0
process_name$ = Space(#MAX_PATH)
CopyMemory(@PEntry\szExeFile,@process_name$,#MAX_PATH)
If UCase(process_name$) = UCase(p_name$)
ProcedureReturn PEntry\th32ProcessID
EndIf
Wend
CloseLibrary(hDLL)
ProcedureReturn 0
EndProcedure
Procedure InjectLibA(dwProcessId.i, pszLibFile$)
hProcess.i
hThread.i
lzLibFileRemote.i
lSize.i
endSize.i
lsThreadRtn.i
hProcess = OpenProcess_(#PROCESS_QUERY_INFORMATION | #PROCESS_CREATE_THREAD | #PROCESS_VM_OPERATION | #PROCESS_VM_WRITE, 0, dwProcessId)
If hProcess = 0 : Goto ErrHandle : EndIf
lSize = 1 + Len(pszLibFile$)
endSize = lSize
lzLibFileRemote = VirtualAllocEx_(hProcess, #Null, endSize, #MEM_COMMIT, #PAGE_READWRITE)
If lzLibFileRemote = 0 : Goto ErrHandle : EndIf
If (WriteProcessMemory_(hProcess, lzLibFileRemote, pszLibFile$, endSize, #Null) = 0) : Goto ErrHandle : EndIf
OpenLibrary(0, "Kernel32.dll") : lsThreadRtn = GetFunction(0, "LoadLibraryA") : CloseLibrary(0)
If lsThreadRtn = 0 : Goto ErrHandle : EndIf
hThread = CreateRemoteThread_(hProcess, #Null, #Null, lsThreadRtn, lzLibFileRemote, #Null, #Null)
If (hThread = 0) : Goto ErrHandle : EndIf
WaitForSingleObject_(hThread, #INFINITE)
If lzLibFileRemote<>0
VirtualFreeEx_(hProcess, lzLibFileRemote, 0, #MEM_RELEASE)
MessageRequester("Inject Status", "Injection Suceeded", 0)
Else
VirtualFreeEx_(hProcess, lzLibFileRemote, 0, #MEM_RELEASE)
MessageRequester("Inject Status", "Injection Failed !!!", 0)
EndIf
End
ErrHandle:
CloseHandle_(hThread)
CloseHandle_(hProcess)
MessageRequester("Inject Status", "Injection Failed !!!", 0)
EndProcedure
path_game$ = "C:\Program Files\SomeApp-Game\My_App_Game\"
file_game$ = "MyApp.exe"
apprun = Elevated_Cmd(path_game$, file_game$)
Delay(10000)
dll_dir$ = GetCurrentDirectory() + "d3d9.dll"
val_pid.i = GetPidByName(file_game$)
Delay(10)
File_dll$ = dll_dir$
Delay(10)
InjectLibA(val_pid, dll_dir$)
Delay(10000)
Debug GPSI_ShowFPS(1)

Hope it helps.