How to call a function from a dll that is injected into a process? Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

How to call a function from a dll that is injected into a process?

18 Sep 2021, 19:14

I have the inject function inside of my dll and I'm calling it from a ahk script:

Code: Select all

Global DLL_LIB

Inject(TargetPID) {  
   
   Path = %A_ScriptDir%\Dllx64.dll
   DLL_LIB := DllCall("LoadLibrary", "Str", Path, "Ptr") 
   if (!DLL_LIB) {
      MsgBox LoadLibrary failed with: %A_LastError%.`n,*
      return
   }
   
   FileAppend, A_LastError: %A_LastError%.`n,*

   Response := DllCall("Dllx64.dll\Inject", "Str", Path, "Int", TargetPID, "WStr")
   FileAppend, ErrorLevel: %ErrorLevel% `n,*
   FileAppend, Response: %Response%`n,*

   ; DllCall("FreeLibrary", "Ptr", DLL_LIB)
   ;=====================================================

}
Im not calling the FreeLibrary, however when i try to call another function, it does not call from the dll that is injected into the target:

Code: Select all

Test() {
   Response := DllCall("Dllx64.dll\Test", "Str")
   FileAppend, ErrorLevel: %ErrorLevel% `n,*
   FileAppend, Response: %Response%`n,*
   DllCall("FreeLibrary", "Ptr", DLL_LIB)  
}
What must be happening is:
I call the Inject function it injects into the target and exit.
When i try to call another function, the lib load on DLL_LIB doesn't exist anymore.

I think my question is similar to this one: https://autohotkey.com/board/topic/89148-need-help-finding-a-dll-base-address/
However I couldn't figure how the guy solved her issue.

Do i need to get the address of the dll injected into the target and call the function or load a new library from this address?
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to call a function from a dll that is injected into a process?

19 Sep 2021, 06:30

its not clear what u want to achieve(assuming even u urself know to begin with)
is the ahk script meant to invoke a function exported by ur dll in the address space of another process, or is the dll, thats loaded into ur ahk script, meant to invoke a function exported by ur dll in the address space of another process?
in any case, post full source of everything
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to call a function from a dll that is injected into a process?

19 Sep 2021, 09:08

is the ahk script meant to invoke a function exported by ur dll in the address space of another process
Yes I'm trying to load a function in a dll that is injected in another process other than the ahk.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to call a function from a dll that is injected into a process?

19 Sep 2021, 09:49

swagfag wrote:
19 Sep 2021, 06:30
post full source of everything
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to call a function from a dll that is injected into a process?

19 Sep 2021, 10:07

AutoHotkey script:

Code: Select all

Global DLL_LIB

; Call the inject function inside of the dll.
Inject(TargetPID) {  
   
   Path = %A_ScriptDir%\Dllx64.dll
   DLL_LIB := DllCall("LoadLibrary", "Str", Path, "Ptr") 
   if (!DLL_LIB) {
      MsgBox LoadLibrary failed with: %A_LastError%.`n,*
      return
   }
   
   FileAppend, A_LastError: %A_LastError%.`n,*

   Response := DllCall("Dllx64.dll\Inject", "Str", Path, "Int", TargetPID, "WStr", "", "WStr")
   FileAppend, ErrorLevel: %ErrorLevel% `n,*
   FileAppend, Response: %Response%`n,*

   ; DllCall("FreeLibrary", "Ptr", DLL_LIB)
   ;=====================================================

}

Dll functions:

Code: Select all

extern "C" __declspec(dllexport) LPCWSTR Inject(LPTSTR dllToInject, DWORD processId, LPCWSTR Data)
{
	
	NTSTATUS nt = RhInjectLibrary(
		processId,             
		0,                    
		EASYHOOK_INJECT_DEFAULT,
		NULL,                  
		dllToInject,		    // 64-bit
		const_cast<void*>(reinterpret_cast<const void*>(Data.c_str())),   
		sizeof(decltype(Data)::value_type) * Data.size()				 
	);

	if (nt != 0) {

		LPCWSTR err = RtlGetLastErrorString();
		
		std::wstringstream ss;

		ss << L"RhInjectLibrary failed with error code = "
		   << nt << L"\nRtlGetLastErrorString: " << err;

		OutputDebugString(ss.str().c_str());
		return ss.str().c_str();
	}
	else
		OutputDebugString(L"DLL injected successfully!");

	return L"DLL injected successfully.";
}


Test function im trying to call while the dll is currently injected in a different process than AutoHotkey.

Code: Select all

extern "C" __declspec(dllexport) LPCWSTR Test()
{
	DWORD PID = GetCurrentProcessId();

	std::wstringstream ss;
	ss << L"\nPID: " << PID;
	OutputDebugStringW(ss.str().c_str());

	return std::to_wstring(PID).c_str();
}

It does inject and call the 'Test' function successfully, however its not calling the 'Test' function from the dll that is injected in the target pid.

The PID being returned here:

Code: Select all


; Try to call the Test function in the dll that is injected in another process than AutoHotkey one.
Test() {
   Response := DllCall("Dllx64.dll\Test", "WStr")
   FileAppend, ErrorLevel: %ErrorLevel% `n,*
   FileAppend, Response: %Response%`n,*
   ;DllCall("FreeLibrary", "Ptr", DLL_LIB)  
}
Is from the AutoHotkey script who called the inject function.

Im trying to 'communicate' with the Test function that is injected in the target pid.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to call a function from a dll that is injected into a process?

21 Sep 2021, 18:24

  1. easyhook has RhCreateStealthRemoteThread. u can create another exported function(that ure gonna call from AHK in a single, normal DllCall) in ur dll that uses RhCreateStealthRemoteThread to invoke ur exported and injected Test function
  2. reimplement RhCreateStealthRemoteThread(or whichever other method of invocation u prefer) in AHK code. then, invoke ur exported and injected Test function
example, here's B for AHK UNICODEx64 and injecting into 64bit notepad(injecting done with ProcessHacker) implemented using a plain CreateRemoteThread:

Code: Select all

#include "Windows.h"
#include <sstream>

extern "C" __declspec(dllexport) DWORD WINAPI show_msgbox(LPVOID lpParameter)
{
    std::wstringstream wss;
    wss << "PID: "  << GetCurrentProcessId();
    return MessageBoxW(nullptr, wss.str().c_str(), L"msgbox_x64.dll!show_msgbox", MB_OK);
}

Code: Select all

#NoEnv
#Requires AutoHotkey v1.1

dllName := "msgbox_x64.dll"
hOurModule := DllCall("LoadLibrary", "Str", dllName, "Ptr") ; load into our address space, get module address
pOurMsgbox := DllCall("GetProcAddress", "Ptr", hOurModule, "AStr", "show_msgbox", "Ptr") ; find function and get its address
offset := pOurMsgbox - hOurModule ; compute the function's relative offset
; base addr of SAME injected dll inside target process + relative offset = address of the func in the target process
pInjectedMsgbox := getModuleBaseAddress(injecteePID, dllName) + offset 

; hProcess handle needed later for CreateRemoteThread
WinGet injecteePID, PID, ahk_exe notepad.exe
hInjecteeProcess := DllCall("OpenProcess", "UInt", 0x1FFFFF, "Int", false, "UInt", injecteePID, "Ptr") ; PROCESS_ALL_ACCESS

; the function to call has to have the THREADPROC signature for CreateRemoteThread to be able to invoke it properly
hInvokingThread := DllCall("CreateRemoteThread", "Ptr", hInjecteeProcess, "Ptr", 0, "Ptr", 0, "Ptr", pInjectedMsgbox, "Ptr", 0, "UInt", 0, "UInt*", 0, "Ptr") 

getModuleBaseAddress(pid, moduleName) { ; unicode x64 ONLY
	hSnap := DllCall("CreateToolhelp32Snapshot", "UInt", 0x18, "UInt", pid, "Ptr") ; TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32

	if (hSnap = -1) ; INVALID_HANDLE_VALUE
		throw Exception("createSnapshot failed", -1, A_LastError)

	VarSetCapacity(me32, 1080) ; MODULEENTRY32W x64
	NumPut(1080, me32, "UInt") ; cbSize

	if DllCall("Module32FirstW", "Ptr", hSnap, "Ptr", &me32)
	{
		Loop
		{
			if (moduleName = StrGet(&me32 + 48, "UTF-16")) ; szModule x64
			{
				modBaseAddr := NumGet(me32, 24, "Ptr") ; modBaseAddr x64
				break
			}
		} until !DllCall("Module32NextW", "Ptr", hSnap, "Ptr", &me32)
	}

	DllCall("CloseHandle", "Ptr", hSnap)

	if (modBaseAddr = "")
		throw Exception("no such module found in PID=" pid, -1, moduleName)

	return modBaseAddr
}
rewrite with error handling in ur own code
Im trying to 'communicate' with the Test function that is injected in the target pid.
explain what u need to "communicate", how, what application, what data, what bitness, why
depending on the answer, the solutions are numerous
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to call a function from a dll that is injected into a process?

22 Sep 2021, 11:00

Amazing! It called the function inside of the dll injected into the process, what about when you need to pass a parameter to the function?

Code: Select all

extern "C" __declspec(dllexport) LPCWSTR Test(DWORD  pid)
{
	//DWORD pid= GetCurrentProcessId();

	std::wstringstream ss;
	ss << L"\npid: " << pid;
	OutputDebugStringW(ss.str().c_str());

	return std::to_wstring(pid).c_str();
}


I turned it into a function, in case someone else one day need:

Code: Select all

CallFunctionOnInjectedDLL(dllPath,  dllName, "UninstallHooks", pid)
return



; dllPath: 
;                 Full path to the dll.
; dllName: 
;                 Only the dll name.
; FunctionToCall: 
;                 The function inside of the dll we are trying to call
;                 it should contain extern "C" __declspec(dllexport).
; processPID:
;                 PID of the process which the dll is currently injected.
CallFunctionOnInjectedDLL(dllPath, dllName, FunctionToCall, processPID) {

   ; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=94817&p=421638#p421638
   
   ; Load into our address space, get module address.
   hOurModule := DllCall("LoadLibrary", "Str", dllPath, "Ptr") 
   if (!hOurModule) {
      MsgBox Failed to get hOurModule`nA_LastError: %A_LastError%
      return
   }



   ; Find the function and get its address.
   FunctionAddress := DllCall("GetProcAddress", "Ptr", hOurModule, "AStr", FunctionToCall, "Ptr")
   if (!hOurModule) {
      MsgBox Failed to get FunctionAddress`nA_LastError: %A_LastError%
      return
   }



   ; Compute the function's relative offset.
   ; Base addr of SAME injected dll inside target process + relative offset = address of the func in the target process.
   offset := FunctionAddress - hOurModule 



   pInjectedFunction := GetModuleBaseAddress(processPID, dllName) + offset 
   if (!pInjectedFunction) {
      MsgBox Failed to get pInjectedFunction`nA_LastError: %A_LastError%
      return
   }



   ; hProcess handle needed later for CreateRemoteThread.
   hInjectedProcess := DllCall("OpenProcess", "UInt", 0x1FFFFF, "Int", false, "UInt", processPID, "Ptr") ; PROCESS_ALL_ACCESS
   if (!hInjectedProcess) {
      MsgBox Failed to get hInjectedProcess`nA_LastError: %A_LastError%
      return
   }



   ; The function to call has to have the THREADPROC signature for CreateRemoteThread to be able to invoke it properly.
   hInvokingThread := DllCall("CreateRemoteThread", "Ptr", hInjectedProcess, "Ptr", 0, "Ptr", 0, "Ptr", pInjectedFunction, "Ptr", 0, "UInt", 0, "UInt*", 0, "Ptr") 
   if (!hInvokingThread) {
      MsgBox Failed to get hInvokingThread`nA_LastError: %A_LastError%
      return
   }



}




GetModuleBaseAddress(pid, moduleName) { ; unicode x64 ONLY
	hSnap := DllCall("CreateToolhelp32Snapshot", "UInt", 0x18, "UInt", pid, "Ptr") ; TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32

	if (hSnap = -1) ; INVALID_HANDLE_VALUE
		throw Exception("createSnapshot failed", -1, A_LastError)

	VarSetCapacity(me32, 1080) ; MODULEENTRY32W x64
	NumPut(1080, me32, "UInt") ; cbSize

	if DllCall("Module32FirstW", "Ptr", hSnap, "Ptr", &me32)
	{
		Loop
		{
			if (moduleName = StrGet(&me32 + 48, "UTF-16")) ; szModule x64
			{
				modBaseAddr := NumGet(me32, 24, "Ptr") ; modBaseAddr x64
				break
			}
		} until !DllCall("Module32NextW", "Ptr", hSnap, "Ptr", &me32)
	}

	DllCall("CloseHandle", "Ptr", hSnap)

	if (modBaseAddr = "")
		throw Exception("no such module found in PID=" pid, -1, moduleName)

	return modBaseAddr
}
Last edited by fabricio234 on 22 Sep 2021, 11:58, edited 2 times in total.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to call a function from a dll that is injected into a process?

22 Sep 2021, 11:48

fabricio234 wrote:
22 Sep 2021, 11:00
Amazing! It called the function inside of the dll injected into the process
and it probably also messed up the injected process' memory, since the signature of LPCWSTR (possibly __cdecl, depending on the project settings ure compiling with) Test()
completely doesnt match that of LPTHREAD_START_ROUTINE, which i warned u about already: DWORD __stdcall ThreadProc(LPVOID)
so ur stuff is probably gonna crash given enough time
is also possible to receive the return of the function called?
yeah well kinda. if ure invoking with CreateRemoteThread(), the return value is the thread's exit code(a single DWORD/UInt). u can loop GetExitCodeThread() until u get a result back. of course, the result would have to fit inside 4 bytes

if u want to return strings, ull have to setup some sort of IPC(eg a File Mapping, Pipes, WM_COPYDATA). and if ure gonna setup IPC, u dont need CreateRemoteThread for invoking. instead u can spin a new thread with a msgloop from DllMain when u inject the dll, and post/sendmessages to and fro. that thread will then be responsible for invoking whatever functions u need with whatever parameters u supplied and returning the results.
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to call a function from a dll that is injected into a process?

22 Sep 2021, 12:00

I already created an IPC method with a window procedure and a msgloop in a secondary thread, which i can send/receive data using wm_copydata like you have suggested.
Its easier just call the function with the correct parameters other than sending it in a wm_copydata msg and converting the data to call the function with it.

I could 'pass' value to the function which:

Code: Select all

hInvokingThread := DllCall("CreateRemoteThread", "Ptr", hInjectedProcess, "Ptr", 0, "Ptr", 0, "Ptr", pInjectedFunction, "Int", 99, "Ptr", 0, "UInt", 0, "UInt*", 0, "Ptr")
Is it any wrong?


Code: Select all

HANDLE CreateRemoteThread(
   HANDLE                 hProcess,
   LPSECURITY_ATTRIBUTES  lpThreadAttributes,
   SIZE_T                 dwStackSize,
   LPTHREAD_START_ROUTINE lpStartAddress,
   LPVOID                 lpParameter,
   DWORD                dwCreationFlags,
   LPDWORD             lpThreadId
   );
and it probably also messed up the injected process' memory, since the signature of LPCWSTR (possibly __cdecl, depending on the project settings ure compiling with) Test()
completely doesnt match that of LPTHREAD_START_ROUTINE, which i warned u about already: DWORD __stdcall ThreadProc(LPVOID)
I dont understand what you mean about LPTHREAD_START_ROUTINE and DWORD __stdcall ThreadProc(LPVOID).
Last edited by fabricio234 on 22 Sep 2021, 12:37, edited 1 time in total.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to call a function from a dll that is injected into a process?

22 Sep 2021, 12:36

fabricio234 wrote:
22 Sep 2021, 12:00
I already created an IPC method with a window procedure and a msgloop in a secondary thread, which i can send/receive data using wm_copydata like you have suggested.
then u can extend that loop to also handle a bunch of <msg codes i made up>(RegisterWindowMessage) and forgo CreateRemoteThread entirely
I could 'pass' value to the function which:

Code: Select all

hInvokingThread := DllCall("CreateRemoteThread", "Ptr", hInjectedProcess, "Ptr", 0, "Ptr", 0, "Ptr", pInjectedFunction, "Int", 99, "Ptr", 0, "UInt", 0, "UInt*", 0, "Ptr")
Is it any wrong?
yea u can pass up to 4/8 bytes worth of data to the thread. the only thing wrong is the incorrectly defined Int parameter(should be Ptr, as in LPVOID lpParameter), which u changed from the already correctly written source for some reason
I dont understand what you mean about LPTHREAD_START_ROUTINE and DWORD __stdcall ThreadProc(LPVOID).
read the documentation
https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms686736(v=vs.85)

can u tell whats different between these declarations?

Code: Select all

LPCWSTR             (mystery calling convention)     Test      (                       );
  DWORD     WINAPI(__stdcall calling convention)     ThreadProc(_In_ LPVOID lpParameter);
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to call a function from a dll that is injected into a process?

22 Sep 2021, 13:03

yea u can pass up to 4/8 bytes worth of data to the thread. the only thing wrong is the incorrectly defined Int parameter(should be Ptr, as in LPVOID lpParameter), which u changed from the already correctly written source for some reason
I re-read the docs again, i understand now why it should be Ptr.
lpParameter

A pointer to a variable to be passed to the thread function.
And if the function expect more than one parameter? would need a struct?

Code: Select all

struct my_pair {
    LPCWSTR  (*foo)(int x, LPCWSTR foo);
    int x;
};

my_pair *obj = new my_pair;
obj->foo = Test;
obj->x = 1;

CreateRemoteThread(...,obj,...);


can u tell whats different between these declarations?
Do you mean to define Test as:

Code: Select all

extern "C" __declspec(dllexport) LPCWSTR __stdcall Test(int x, LPCWSTR foo)
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to call a function from a dll that is injected into a process?  Topic is solved

22 Sep 2021, 13:19

fabricio234 wrote:
22 Sep 2021, 13:03
And if the function expect more than one parameter? would need a struct?
it can't expect more than 1 parameter, because that would contradict the microsoft defined function signature for the thread callback. u can pass a pointer to a struct, but that struct would have to be allocated in the address space of the target process first and also populated there


Do you mean to define Test as:

Code: Select all

extern "C" __declspec(dllexport) LPCWSTR __stdcall Test(int x)
no, the return type should be a DWORD, the calling convention should be __stdcall(thats what the WINAPI macro is aliased to) and the function should define 1 parameter - a LPVOID. dont u know what "same/matches" means?
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to call a function from a dll that is injected into a process?

22 Sep 2021, 13:29

I see, im gonna read more about, btw it helped a lot thank you again swagfag!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], morkovka18, Shifted_Right and 190 guests