Copy and run functions from memory

Just starting out? Need help? Post your questions and find answers here.
Psych
Enthusiast
Enthusiast
Posts: 239
Joined: Thu Dec 18, 2008 3:35 pm
Location: Wales, UK

Copy and run functions from memory

Post by Psych »

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?
----------------------------------------------------------------------------
Commenting your own code is admitting you don't understand it.
----------------------------------------------------------------------------
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Probably the closest thing to it would be to use Import and just import the functions you want.
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

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.
I may look like a mule, but I'm not a complete ass.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

It is not possible because there is really no such thing as a function in a dll. There are only function entry points, which tells you where the functions start. But you don't know where they end and which other functions they may call.
localmotion34
Enthusiast
Enthusiast
Posts: 665
Joined: Fri Sep 12, 2003 10:40 pm
Location: Tallahassee, Florida

Post by localmotion34 »

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.

Code: Select all

!.WHILE status != dwPassedOut
! Invoke AllocateDrink, dwBeerAmount
!MOV Mug, Beer
!Invoke Drink, Mug, dwBeerAmount
!.endw
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Trond wrote:...and which other functions they may call.
It handles this too?
BERESHEIT
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

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.
No, this is not the same at all.

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.
from entry point to return
There is your problem. There is absolutely no guarantee that the function has only one 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

You can't extract SillyFunction because if you run from the function entry point to the first ret, then you don't get the entire function (path3). There is absolutely no way whatsoever to know which ret belongs to which function. Never ever.
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.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

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

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

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:

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)
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.
Post Reply