DLL Code injection to third party process using QueueUserAPC - dll-injection

I want to inject my dll in to 64 bit application and I have tried the logic explained in the given link using QueueUserAPC. I am getting success message for every API but when I see in ProcessExplorer I am not able to see my dll in the process.
Below is my code :
bool FindProcess(PCWSTR exeName, DWORD& pid, vector<DWORD>& tids) {
auto hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return false;
pid = 0;
PROCESSENTRY32 pe = { sizeof(pe) };
if (::Process32First(hSnapshot, &pe)) {
do {
if (_wcsicmp(pe.szExeFile, exeName) == 0) {
pid = pe.th32ProcessID;
THREADENTRY32 te = { sizeof(te) };
if (::Thread32First(hSnapshot, &te)) {
do {
if (te.th32OwnerProcessID == pid) {
tids.push_back(te.th32ThreadID);
}
} while (::Thread32Next(hSnapshot, &te));
}
break;
}
} while (::Process32Next(hSnapshot, &pe));
}
::CloseHandle(hSnapshot);
return pid > 0 && !tids.empty();}
void main(){
DWORD pid;
vector<DWORD> tids;
if (FindProcess(L"DataGrid.exe", pid, tids))
{
printf("OpenProcess\n");
HANDLE hProcess = ::OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
printf("VirtualAllocEx\n");
auto p = ::VirtualAllocEx(hProcess, nullptr, 1 << 12, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
wchar_t buffer[] = L"C:\\Users\\sbhuma\\Documents\\Visual Studio 2015\\Projects\\GalaxyHook\\Debug\\GalaxyHook.dll";
printf("WriteProcessMemory\n");
::WriteProcessMemory(hProcess, p, buffer, sizeof(buffer), nullptr);
for (const auto& tid : tids)
{
printf("OpenThread\n");
HANDLE hThread = ::OpenThread(THREAD_SET_CONTEXT, FALSE, tid);
if (hThread)
{
printf("GetProcAddress\n");
DWORD word = ::QueueUserAPC((PAPCFUNC)::GetProcAddress(GetModuleHandle(L"kernel32"), "LoadLibraryW"), hThread, (ULONG_PTR)p);
if (word)
{
printf("insdie if\n");
}
printf("End of IF\n");
}
}
printf("VirtualFreeEx\n");
::VirtualFreeEx(hProcess, p, 0, MEM_RELEASE | MEM_DECOMMIT);
}}
Any help related to inject the dll in to 64 bit application is helpful as I am new to this topic.
Regards,
Sowmya.

First of all, ensure you’re building your injector app as 64 bit.
One possible reason is you’re releasing the buffer too early. QueueUserAPC doesn’t wait; it enqueues the call and returns immediately. Can be your injector process ends running that for loop, calls VirtualFreeEx, then your target process receives the APC, tries to load your DLL but the name buffer is already released by then, so LoadLibrary fails. To verify, comment out the call to VirtualFreeEx. If your DLL will load OK, one way to fix the memory leak is use a named event, CreateEvent in injector app before any calls to QueueUserAPC(), OpenEvent, SetEvent and CloseHandle in DllMain(DLL_PROCESS_ATTACH) of the DLL you’re injecting, WaitForSingleObject in injector app before VirtualFreeEx, I recommend using a timeout for the wait, CloseHandle at the end. As a side effect, your injector app will be able to find out, and report somewhere, whether the injection was successful.
Another possible reason is your target app never enters alertable state. Not all apps use APC, there’re multiple alternative methods to implement asynchronous stuff in Windows. So, not all apps ever call these SleepEx / WaitForMultipleObjectsEx functions. Such app will never receive that APC. If that’s the case, you should use another method of DLL injection. DataGrid.exe name hints your target app is probably a GUI app. You can EnumWindows or FindWindow to find its top-level window, GetWindowThreadProcessId to get thread ID who owns that window, SetWindowsHookEx to inject your DLL into the target process.

Related

How to force a libusb event so that libusb_handle_events() returns

Suppose I have a libusb program that just uses the hotplug API. You register a callback and then apparently have to call libusb_handle_events() in a loop which then calls your hotplug callback.
int LIBUSB_CALL hotplugCallback(libusb_context* ctx,
libusb_device* device,
libusb_hotplug_event event,
void* user_data)
{
cout << "Device plugged in or unplugged";
}
void main()
{
libusb_init(nullptr);
libusb_hotplug_register_callback(nullptr,
static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
LIBUSB_HOTPLUG_NO_FLAGS,
LIBUSB_HOTPLUG_MATCH_ANY,
LIBUSB_HOTPLUG_MATCH_ANY,
LIBUSB_HOTPLUG_MATCH_ANY,
&hotplugCallback,
this,
&hotplugCallbackHandle);
for (;;)
{
if (libusb_handle_events_completed(nullptr, nullptr) != LIBUSB_SUCCESS)
return 1;
}
return 0;
}
The question is, without timeout hacks how can I exit this event loop cleanly? I can't find any functions that force libusb_handle_events() (or libusb_handle_events_completed()) to return. In theory they could just never return.
Sorry if this is late.
The question could have been phrased better but I'm assuming (from your comment updates) that your actual program resembles something a little closer to this:
int LIBUSB_CALL hotplugCallback(libusb_context *ctx,
libusb_device *device,
libusb_hotplug_event event,
void *user_data) {
cout << "Device plugged in or unplugged";
}
void SomeClass::someFunction() {
libusb_init(nullptr);
libusb_hotplug_register_callback(nullptr,
static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
LIBUSB_HOTPLUG_NO_FLAGS,
LIBUSB_HOTPLUG_MATCH_ANY,
LIBUSB_HOTPLUG_MATCH_ANY,
LIBUSB_HOTPLUG_MATCH_ANY,
&hotplugCallback,
this,
&hotplugCallbackHandle);
this->thread = std::thread([this]() {
while (this->handlingEvents) {
int error = libusb_handle_events_completed(context, nullptr);
}
});
}
Let's say your object is being deallocated and, no matter what is happening on the USB bus, you don't care and you want to clean up your thread.
You negate this->handlingEvents and you call thread.join() and the thread hangs for 60 seconds and then execution resumes.
This is done because the default behavior of libusb_handle_events_completed calls libusb_handle_events_timeout_completed and passes in a 60 second timeout interval with plans to make it infinite.
The way you force libusb_handle_events_completed to return is you call libusb_hotplug_deregister_callback which wakes up libusb_handle_events(), causing the function to return.
There is more info about this behavior in the docs.
So your destructor (or wherever you want to stop listening immediately) for the class could look something like this:
SomeClass::~SomeClass() {
this->handlingEvents = false;
libusb_hotplug_deregister_callback(context, hotplugCallbackHandle);
if (this->thread.joinable()) this->thread.join();
libusb_exit(this->context);
}
In the function:
int libusb_handle_events_completed(libusb_context* ctx, int* completed)
You can change the value of the completed to "1" so the function will return without blocking
According to their docs:
If the parameter completed is not NULL then after obtaining the event
handling lock this function will return immediately if the integer
pointed to is not 0. This allows for race free waiting for the
completion of a specific transfer.
There is no functions in libusb that force libusb_handle_events() to return.
It's recommended to use libusb_handle_events() in a dedicated thread so your main thread will not be blocked by this call. Even though, if you need to manipulate the call of the event handler you can put the call in a while(condition) and change the condition state in your main thread.
Libusb documentation details this here.

Testing Distributed Object Connections in OSX with Objective C

On Mac OSX with Objective C and Distributed Objects (IPC API), how can a server determine if the client has closed down their application and thus the NSConnection pointer has been broken? I mean, I might be in a while loop, reading a lot of data to send back from the server to the client, and I can test for stuff if I just knew what the trick was to determine if the client has suddenly disconnected.
Add this to your server's vended object class and then check it when doing something in any kind of loop for long periods of time. Replace "servicetest" phrase in the code below with the name of your actual process. You can run your client app and then check ps -ef at command line to see what your process is, if uncertain. It'll be the one with DerivedData if running temporarily in XCode.
A little warning, though. This routine is kind of heavy and you shouldn't call it with every loop iteration. Instead, check like every 500 iterations of your while loop you might be in, for instance. You could also have a background timer that runs and checks. Or you could use rand to determine 1 in 5 odds and run it then.
Note that this routine only runs properly with Distributed Objects on the same system, as in application to application IPC, rather than Distributed Objects over a LAN.
So, when I detected that a client suddenly disconnected, I do a break on my while loop (where I was reading slow process data) and then did a pclose() on that process because there was no sense running it anymore.
/* requires these includes
#include <libproc.h>
#include <sys/sysctl.h>
#include <string>
*/
- (bool)_clientSuddenlyDisconnected;
{
int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
pid_t pids[1024];
bzero(pids, 1024);
proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
for (int i = 0; i < numberOfProcesses; ++i) {
if (pids[i] == 0) { continue; }
char pathBuffer[PROC_PIDPATHINFO_MAXSIZE];
bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE);
proc_pidpath(pids[i], pathBuffer, sizeof(pathBuffer));
if (strlen(pathBuffer) > 0) {
std::string sTest(pathBuffer);
sTest = "/" + sTest + "$$$";
if (sTest.find("/servicetest$$$") != std::string::npos) {
return false;
}
}
}
return true;
}

urldownloadtofile OR InternetOpenUrl not working from DLL

I am trying to use UrlDownloadToFile OR InternetOpenUrl to visit some link on the Internet from an DLL file. While it works on an .exe file, but it didn't work from a DLL file, and even no any error information. It just failed without any information.
Anyone could give me any help? Thanks in advance.
My code are as below:
extern "C" __declspec(dllexport) int __stdcall Hello()
{
HINTERNET hInt=NULL, hUrl=NULL;
hInt = InternetOpenA("Test Agent", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
hUrl = InternetOpenUrlA(hInt, "http://www.google.com.au", NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_CACHE_WRITE, 0);
if (hUrl)
{
InternetCloseHandle(hUrl);
}
InternetCloseHandle(hInt);
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Hello();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
It failed at:
hUrl = InternetOpenUrlA(hInt, "http://www.google.com.au", NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_CACHE_WRITE, 0);
Or:
typedef HRESULT (__stdcall * lpURLDownloadToFile) (LPUNKNOWN pCaller, LPCTSTR szURL, LPCTSTR szFileName, DWORD dwReserved, LPBINDSTATUSCALLBACK lpfnCB);
extern "C" __declspec(dllexport) int __stdcall Hello()
{
HMODULE hModule = LoadLibraryW(L"urlmon.dll");
lpURLDownloadToFile urlD = (lpURLDownloadToFile)GetProcAddress( hModule, "URLDownloadToFileW" );
HRESULT aa = urlD(NULL, L"http://www.google.com.au", L"C:\\Test\\a.html", 0, NULL);
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Hello();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
It failed at:
HRESULT aa = urlD(NULL, L"http://www.google.com.au", L"C:\\Test\\a.html", 0, NULL);
Update:
If I insert a line like "MessageBox" just above where it fails, the MessageBox will be displayed successfully.
If I insert a line like "MessageBox" just after where it fails, the MessageBox will not be displayed. And there is no any error code returned if I tried to display "hUrl" or "aa".
Don't do that in DllMain. See DllMain entry point
The entry-point function should perform only simple initialization or
termination tasks. It must not call the LoadLibrary or LoadLibraryEx
function (or a function that calls these functions), because this may
create dependency loops in the DLL load order. This can result in a
DLL being used before the system has executed its initialization code.
EDIT, as you want to inject the Dll and run code in it.
One way to make an injected Dll to start executing code, without relying on DllMain is as follow:
Use a message hook, limited to one thread.
Find a HWND belonging to the process you want to inject. For Internet Explorer, you may enumerate the Top Level Windows, using EnumWindows A main IE Windows has a class IEframe. Use EnumChildWindows to find a window of class Internet_Explorer Server. Use GetWindowThreadProcessId to get the associated TID (Thread Identifier).
Use SetWindowsHookEx to install a thread hook. You may choose a WH_CALLWNDPROC
or a WH_GETMESSAGE hook. Pass the TID obtained in 1. Your DLL is injected, and is processing messages.
Use PostMessage or SendMessage or PostThreadMessage, depending on the hook type you choose in 2, with a registered message. See RegisterWindowMessage
In your hook function, filter for the registered message (obtain it's value in your DllMain and store it in a global variable) and when this message is received, call your custom code.
Warning: this technique is well suited for short living code, you are in a hook function. That's maybe not the place for downloading from the Internet.
Another way is to create a thread in DllMain, using _beginthreadex
In the thread, you may have a classic RegisterClass / CreateWindow / GetMessage/ DispatchMessage sequence, and your EXE will be able to Send/Post messages to your Dll.
Warning: depending on how you injected the Dll, you must be very carreful about premature unloading of your Dll. If the thread still runs, there may be crashes. Being clean and stable with theses things is not easy. But, if you are just playing/experimenting, don't bother and just LoadLibrary (not in DllMain) you own library a second time, it will stay here forever.
You can mix the two techniques, that is: use a thread message hook to inject the Dll and start a thread in the hook function.

Bizarre hook behavior 32/64 bit

I'm using a local hook (WH_KEYBOARD) with ms word (OpusApp). Well, as far as I know a 32bit app with a 32bit DLL must work only with 32bit target applications. The weird thing is that the program only works with 64bits apps!!! That is it, only with 64bits APPS! For example, it works with IE 64 but not with IE 32!
The app and dll are 32bit compiled with radstudio XE2, I confirmed the version into PE header.
In 32bit OSs, the app and dll doesn´t work.
I found no solutions on net and see no starting point to solve this weird problem.
The DLL code:
// Exported functions
extern "C" __declspec(dllexport)bool __stdcall InstallMouseHook(unsigned long, void *);
extern "C" __declspec(dllexport)bool __stdcall RemoveMouseHook();
// Callback Procedure Declaration
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam);
// Global variables
HHOOK HookHandle;
HINSTANCE DllInstance;
typedef void (__stdcall *CALLIT)(int,WPARAM,LPARAM);
CALLIT callIt = NULL;
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
DllInstance=hinst;
return 1;
}
bool __stdcall InstallMouseHook(unsigned long pid, void *function)
{
callIt = ( CALLIT ) function;
if (function == NULL) {
ShowMessage("function is null!");
} else if (callIt == NULL) {
ShowMessage("callIt is null!");
}
HookHandle=SetWindowsHookEx(WH_KEYBOARD ,reinterpret_cast<HOOKPROC> (HookProc),DllInstance,pid);
if (HookHandle==NULL)return false;
else return true;
}
bool __stdcall RemoveMouseHook()
{
if(UnhookWindowsHookEx(HookHandle)==0)
{
return false;
}
else return true;
}
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code<0) {
return CallNextHookEx(HookHandle,code,wParam,lParam);
}
if (callIt != NULL) {
callIt(code,wParam,lParam);
} else {
ShowMessage("HookProc - no function to execute OR 32/64 bits problem!");
}
//Call the next hook in the chain
return CallNextHookEx(HookHandle,code,wParam,lParam);
}
The EXE calling code:
void __fastcall TfrmMouseHook::btnHookAppDllClick(TObject *Sender)
{
HWND hWindow;
unsigned long pid;
String s = "MouseHookDLL.dll";
DllHandle=LoadLibrary(s.w_str());
MOUSEHOOKFCT_2 InstHook=reinterpret_cast<MOUSEHOOKFCT_2> (GetProcAddress(DllHandle,"InstallMouseHook"));
hWindow = FindWindow(ComboBox1->Text.w_str(),NULL);
if (!hWindow) {
msg("hWindow fail");
return;
}
pid = GetWindowThreadProcessId(hWindow ,0);
if (!pid) {
msg("pid fail");
return;
}
if(!InstHook(pid, (void *) callIt )) {
msg("Unable to install hook!");
} else {
msg(" #### hook INSTALLED! ####");
}
}
CALLIT callIt(code,wParam,lParam) {
frmMouseHook->msg("hook callit: code="+IntToStr(code) +" wparam="+IntToStr(wParam)+" lparam="+IntToStr(lParam) );
}
Call IT is a function pointer to a hooker app function.
Any ideas will be very wellcome!
It is physically impossible for a 32-bit app to install a 32-bit hook DLL and have it executed in 64-bit processes. A 32-bit DLL simply cannot be injected into a 64-bit process. Period. MSDN says this in multiple places, including in the SetWindowsHookEx() documentation:
SetWindowsHookEx can be used to inject a DLL into another process. A
32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL
cannot be injected into a 32-bit process. If an application requires
the use of hooks in other processes, it is required that a 32-bit
application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit
processes, and a 64-bit application call SetWindowsHookEx to inject a
64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have
different names.
Because hooks run in the context of an application, they must match
the "bitness" of the application. If a 32-bit application installs a
global hook on 64-bit Windows, the 32-bit hook is injected into each
32-bit process (the usual security boundaries apply). In a 64-bit
process, the threads are still marked as "hooked." However, because a
32-bit application must run the hook code, the system executes the
hook in the hooking app's context; specifically, on the thread that
called SetWindowsHookEx. This means that the hooking application must
continue to pump messages or it might block the normal functioning of
the 64-bit processes.
If a 64-bit application installs a global hook on 64-bit Windows, the
64-bit hook is injected into each 64-bit process, while all 32-bit
processes use a callback to the hooking application.
The fact that you say your app and DLL do not work on 32-bit OS versions suggests your hooking code is flawed to begin with. But you have not shown enough code to diagnose that one way or the other.
What happens? Besides MSDN or anyone else says, there are some bug in XE6, compiling DLL in newer versions of IDE make this behavior disappears, in fact, the new DLL crash and hook nothing.
As Remy noted, by test I passed a function pointer to the DLL, one wrong thing to do but, when added with the wrong thing done by Embarcadero, sort of functioned.
By now, and I know people will get mad, I put both methods (the wrong and the correct hooks) in the same DLL and in my application and... get crazy... could hook into 32 and 64 bits app with only one DLL.
Don't believe? Install XE6 and try!
And Works in windows 10 as well in windows 7.

How to catch application titlebar change?

We are running on a Windows Client Platform (generally WinXP) in niche industry program that runs in a 640x480 window back to an AS/400 server. To reduce errors I want to watch for when the title bar of the program changes. Then I need to capture the keyboard entries to validate. I'll then make sure each of the entries is valid since the archaic program does no validation. I'll can then do a pop-up then warning the end-user if errors occur and to reduce/eliminate the exception reports.
My question is how can I capture the event of the application title bar change = 'string' that I need? API call? Aiming to do this in VB unless another would be notable cleaner.
WinEvents should work well here. These are lightweight events that get fired when certain UI changes take place - eg names of objects change - which includes Titlebar text changes. One benefit to this type of hook is that you can set it up to deliver the notifications back to your own process, so you don't need to deal with hooking or IPC. (It also works against both 32-bit and 64-bit processes.)
This is easiest to do in plain C/C++; but can be done in .Net (VB, C#) if you add the appropriate [DllImport]'s.
#include <windows.h>
#include <stdio.h>
#define WM_NAMECHANGED WM_APP
HWND g_hwndTarget; // window we're listening to
void CALLBACK WinEventProc(
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime
)
{
// Check this is the window we want. Titlebar name changes result in these
// two values (obtained by looking at some titlebar changes with the
// Accessible Event Watcher tool in the Windows SDK)
if(hwnd == g_hwndTarget && idObject == OBJID_WINDOW && idChild == CHILDID_SELF)
{
// Do minimal work here, just hand off event to mainline.
// If you do anything here that has a message loop - eg display a dialog or
// messagebox, you can get reentrancy.
PostThreadMessage(GetCurrentThreadId(), WM_NAMECHANGED, 0, 0);
}
return;
}
void ReportName(HWND hwnd)
{
WCHAR szName[128];
GetWindowText(hwnd, szName, ARRAYSIZE(szName));
wprintf(L"hwnd 0x%08lx has title: %s\n", HandleToLong(hwnd), szName);
}
int main()
{
wprintf(L"Place mouse pointer over window titlebar to report name changes for and hit return...\n");
getchar();
POINT pt;
GetCursorPos(&pt);
g_hwndTarget = WindowFromPoint(pt);
ReportName(g_hwndTarget);
// Note: this doesn't work for console windows, which are managed by CSRSS.EXE. Simplest (though not efficient) workaround for those
// is to use threadId=0 and filter by hwnd in the callback.
DWORD threadId = GetWindowThreadProcessId(g_hwndTarget, NULL);
// This says: call the callback when any UI elements in the specified thread change
// name. _OUTOFCONTEXT means deliver the notifications in this process, don't hook.
HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE, NULL, WinEventProc, 0, threadId, WINEVENT_OUTOFCONTEXT);
// TODO: add error checking as appropriate.
wprintf(L"Waiting...\n");
// Thread needs to have a message loop for SetWinEventHook to work for out-of-context messages.
UINT count = 10;
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_NAMECHANGED)
{
ReportName(g_hwndTarget);
if(--count == 0)
{
break;
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWinEvent(hook);
return 0;
}
Things to watch for: you might get false-positives; and if the name changes rapidly, by the time you get the first event, the name may be at the second value, so you may appear to see two events for the second value. Neither of these should be an issue if you're just using this as a trigger to check for a specified value, however.
I am assuming that you do not own the code for the target application. In this case, there's no easy "call me back when the title changes" event. You then have 2 options to do what you need, which I will outline below.
Easy but not airtight
Have your application get the main window of the target application (this should be easy enough) and poll its title every 100msec or so. When you detect a change, act accordingly.
Difficult but correct
Hook into the target application using e.g. a global CBT hook. Once your code runs in their process subclass their main window, causing all window messages to go through your code first. When your code sees a WM_SETTEXT message going to the main window, you can actively notify your "other" application on the spot using your choice of IPC. If all you need to do is just shout "hey!" to your other application, do so with an auto-reset event (it will be easiest). Of course all this points heavily to unmanaged code.
If the easy solution is not good enough and the difficult one is too much, you can try using an automation library like White (I 've never used it, so I can't really say more than that).