原始链接

I had recently spent some time playing around with the simple to use DelphiDetours package from Mahdi Safsafi

https://github.com/MahdiSafsafi/DDetours

One missing feature is the ability to inject a DLL into an external process. This is something that I wanted to do for a project that I am currently working on, so I started doing some reading. To me, the topic always seemed to be a Black Art, and something left for the assembly language developers to use, but it turns out to be much more widely accessible and fairly easy to do.

I added a few routines to my dxLib open source project on GitHub to assist with the DLL injection task:

https://github.com/darianmiller/dxLib

I also put a new repository online yesterday, initially containing Delphi projects to create an example custom DLL, a basic victim process, and an example DLL Injector. This repo can be found at:

https://github.com/darianmiller/dxInjectionDetours

The screen shot below demonstrates a successful detour of a simple MessageBox call utilizing the projects from the dxInjectionDetours repo:

 
 
Sample Windows API method intercepted

Define which ProcessID to target

First, I wanted to offer a list of running processes, so the user can simply select a DLL injection target from a list. There's a new dxLib_ProcessList unit with a simple TdxProcessEntryList class which can be populated with a call to its SnapShotActiveProcesses method:

//https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
function TdxProcessEntryList.SnapshotActiveProcesses(const pLookupFullPathName:Boolean=False):Boolean;

The list is generated using the Kernel32.dll API method: CreateToolhelp32Snapshot and related helper routines. It's a quick snapshot of active processes (along with heaps, modules and threads used...which I may extend later.) You can use the related Process32First and Process32Next API methods to build a list of PROCESSENTRY32 items that contain interesting entries like ProcessID, ParentProcessID, and ExeFile name. One potential problem is the ExeFile provided is just the file name without the full path.

You actually do not need the full path name to inject a DLL, but I thought it was a nice feature to add to the demo so I added an optional parameter to utilize another Kernel32 API method QueryFullProcessImageName which returns the full path name of a given process. There are two versions of this method available, one ANSI and one Unicode.

Since I'm using this in a loop, to cache the calls to GetModuleHandle/GetProcAddress, there was a utility class created (TdxProcessNameToId) and a simple method to return the full path name for a given ProcessID:

//requires Vista or later
function TdxProcessNameToId.GetFileNameByProcessID(const pTargetProcessID:DWORD):string;
 

Note that this method calls OpenProcess with the PROCESS_QUERY_LIMITED_INFORMATION access right, which provides a subset of information. This reduced access grants further calls to QueryFullProcessImageName (along with GetExitCodeProcess, GetPriorityClass, and IsProcessInJob.)

Note: OpenProcess will fail on some system processes.

Since some system processes will generate errors, the SnapshotActiveProcesses method simply skips over them. There typically no good reasons to be sniffing into these system processes anyway. (If you need that, then you should get out of User Land and get yourself into Kernel mode.)

We now have a simple routine to easily get a full list of active processes for a user to select from, which will provide a specific ProcessID to target.

Create a custom DLL to inject

The interesting part of this task involves creating the DLL which contains our API hook, intercept, detour, or the terminology of your choice. Using the DelphiDetours package, it's really simple to do.

Remember to 'match your bitness' - create a 32-bit DLL to inject into a 32-bit process, and a 64-bit DLL for a 64-bit process.

There is a long history of DLL support in Delphi. Unfortunately, there's not a lot of documentation provided for some areas. For example, if you look up the DLLProc global variable, you likely won't find much. Fortunately, there is a succinct StackOverflow response by Sertac Akyuz which explains basic usage:

Unfortunately when begin is executed in your dll code, the OS has already called DllMain in your library. So
when your DllProc := DllMain; statement executes it is already too late.
The Delphi compiler does not allow user code to execute when the dll is attached to a process.
The suggested workaround (if you can call that a workaround) is to call your own DllMain
function yourself in a unit initalization section or in the library code:
 

We set this DLLProc system variable and call it ourselves with DLL_PROCESS_ATTACH. This is the .DPR source for an example DLL:

library dxDetours_InterceptAPI_MessageBox;

uses
WinApi.Windows,
dxDetours_InterceptAPI_MessageBox_CustomImplementation in '..\..\Source\dxDetours_InterceptAPI_MessageBox_CustomImplementation.pas'; {$R *.RES} procedure DllEntry(pReason:DWORD);
begin
case pReason of
DLL_PROCESS_ATTACH: CreateIntercepts();
DLL_PROCESS_DETACH: RemoveIntercepts();
DLL_THREAD_ATTACH:;
DLL_THREAD_DETACH:;
end;
end; begin
DllProc:= @DllEntry;
DllEntry(DLL_PROCESS_ATTACH); end.
 

We then need to define CreateIntercepts() and RemoveIntercepts() in our custom implementation unit as in the example below. DelphiDetours does most of the heavy lifting. We just need to define which API calls to intercept and what to replace them with.

In this example, we're going to replace the two variations of MessageBox with our own custom implementation. For this very simple example, we will call the intercepted (original version) MessageBox with our custom message and caption whenever the target process calls MessageBox:

unit dxDetours_InterceptAPI_MessageBox_CustomImplementation;

interface
uses
Windows,
DDetours; procedure CreateIntercepts();
procedure RemoveIntercepts(); var
//definitions of methods to be replaced - ensure exact match to target methods
InterceptMessageBoxA: function(hWnd: hWnd; lpText, lpCaption: LPCSTR; uType: UINT): Integer; stdcall = nil;
InterceptMessageBoxW: function(hWnd: hWnd; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall = nil; implementation function MyCustomMessageBoxA(hWnd: hWnd; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall;
begin
Result := InterceptMessageBoxA(hWnd, 'My custom message', 'ANSI Version Hooked', MB_OK or MB_ICONEXCLAMATION);
end; function MyCustomMessageBoxW(hWnd: hWnd; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall;
begin
Result := InterceptMessageBoxW(hWnd, 'My custom message', 'UNICODE Version Hooked', MB_OK or MB_ICONEXCLAMATION);
end; procedure CreateIntercepts();
begin
BeginHooks;
@InterceptMessageBoxA := InterceptCreate(@MessageBoxA, @MyCustomMessageBoxA);
@InterceptMessageBoxW := InterceptCreate(@MessageBoxW, @MyCustomMessageBoxW);
EndHooks;
end; procedure RemoveIntercepts();
begin
if Assigned(InterceptMessageBoxA) then
begin
BeginUnHooks;
InterceptRemove(@InterceptMessageBoxA);
InterceptRemove(@InterceptMessageBoxW);
InterceptMessageBoxA := nil;
InterceptMessageBoxW := nil;
EndUnHooks;
end;
end; end.
 

Thanks to DelphiDetours, this isn't a Black Art at all (at least in our code...DelphiDetours is certainly full of magic.) The major caveat is to ensure that the API method definition exactly matches the original. It's best to utilize the Delphi-provided definitions whenever possible (like those found in the Windows.pas file.)

We now have a DLL ready to inject into a target process!

Inject your DLL into an active process

Now the fun part - actually injecting this custom DLL into a target process. (Note that there are ways to prep a process to load one or more DLLs automatically on startup which could be detailed in a future blog post.)

Unfortunately, the DelphiDetours package doesn't provide the injection capability. I've added a new unit to dxLib so handle this task: dxLib_WinInjection. This has a single utility method, InjectDLL as defined below:

function InjectDLL(const pTargetProcessID:DWORD; const pSourceDLLFullPathName:string; const pSuppressOSError:Boolean=True):Boolean;
 

You just need the ProcessID and the filename (including full path) of the DLL to inject and it returns success or failure.

This method calls OpenProcess with extended access rights (PROCESS_CREATE_THREAD or PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or PROCESS_VM_WRITE or PROCESS_VM_READ) It then allocates enough memory (via VirtualAllocEx which returns the memory location) to write the DLL full pathname (via WriteProcessMemory) to the process memory space. It then calls CreateRemoteThread with a function pointer to LoadLibrary (ANSI or Unicode variant, depending on your Delphi version) and the memory location of the DLL to be loaded. When this occurs, the interception(s) defined in the custom DLL get put into place.

Now, there is a bit of Black Art in this code, as the remote thread starts with a LoadLibrary call in Kernel32.dll. This will only work because LoadLibrary exists in every process at the same virtual memory address (otherwise we would have to look-up the location of LoadLibrary in the target process' memory space.) We start a new thread executing LoadLibrary with the DLL full filename. This nice blog post from Brandon Arvanaghi goes into more detail. As his post suggests, there is an alternative way of injecting a DLL and that is to write enough space to the external process to hold the entire contents of the DLL instead of just the filename. (Perhaps we'll attempt that task for fun in a future blog post.)

I confess to reading different pieces of sample code a few times before understanding what was going on. Most samples that I found were cryptic without any comments and it seemed odd to be writing the DLL filename to some random memory location - until it finally clicked. Perhaps one way to look at it is to compare it to a simple post-it note. We write the full pathname of the DLL to a post-it note and stick it inside the memory space of the target process. This note is only used by when calling the LoadLibrary routine in a new thread, and then it's discarded/ignored.

What's next?

Next up is to inject a custom DLL into every active process. In the ancient versions of Windows, it was much easier (and a very important reason why viruses were much more rampant back then!) These days, this is going to take a system driver and it's beyond the scope of our simple InjectDLL call. DelphiDetours is a nice package, but it's also missing this functionality. However, there is hope as we can fall back to a famous Delphi classic tool: madCodeHook from Mathias Rauen.

Mathias is no longer offering a freeware version of this package. It's also not available for sale without some sort of background check. Apparently, this powerful tool has been used in the past by malware authors. I've contacted the author directly and was provided a purchase link. (We also connected on LinkedIn. If you are interested in this sort of topic, feel free to reach out to me as well: https://www.linkedin.com/in/darianm/) I plan on purchasing madCodeHook soon and implementing a system wide hook using the driver-based approach. That fun will hopefully be documented in a future blog post. I wanted to get the current task working before tackling the much larger driver-based approach.

Further Information

When looking at intercepting Windows APIs, the industry reference is the Detours package directly from Microsoft. This was available historically as a fairly expensive commercial package but is now available on GitHub under a fee MIT open source license.

https://github.com/microsoft/Detours

There is also apparently some Injection code in the venerable JEDI Code Library for Delphi, which also may be looked at in a future blog post.

https://github.com/project-jedi/jcl

Finally, I used a few tools for composing this blog post. The first is a free source code beautifier available online called Hilite.me by Alexander Kojevnikov. Simply paste code into the Source Code box, pick the Language, optionally pick the Style (I selected vs) and click on the Highlight! button. It will generate the HTML in another edit box, as well as display a preview. It's super-easy and works great.

http://hilite.me/

For the short-animated demo below, I used a free tool called ScreenToGif by Nicke Manarin.

https://www.screentogif.com/

https://github.com/NickeManarin/ScreenToGif

 
 
DLL injection demo

DLL Injection with Delphi(转载)的更多相关文章

  1. Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术

    catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...

  2. DLL Injection and Hooking

    DLL Injection and Hooking http://securityxploded.com/dll-injection-and-hooking.php Three Ways to Inj ...

  3. .net DLL 注册 regasm delphi调用

    .net DLL 注册 regasm regasm regasm myTest.dll regasm.exe 打开vs2005自带的工具“Visual Studio 2005命令提示”,输入上述命令 ...

  4. C# 调用C++ DLL 的类型转换(转载版)

    最近在做视频监控相关的demo开发,实现语言是C#,但视频监控的SDK是C++开发的,所以涉及到C#调用C++的dll库.很多结构体.参数在使用时都要先进行转换,由非托管类型转换成托管类型后才能使用. ...

  5. CommMonitor8.0 串口过滤驱动 SDK DLL版本 C#/Delphi调用DEMO

    CommMonitor8.0 SDK DLL 版本,此版本是直接调用DLL. Delphi调用定义: constCommMOnitor8x = ‘CommMOnitor8x.dll’; typeTOn ...

  6. WPF使用Log4net.dll库的demo(转载加个人观点)

    原文地址:http://blog.csdn.net/linraise/article/details/50547149 配置文件解析地址:http://blog.csdn.net/pfe_nova/a ...

  7. The current state of generics in Delphi( 转载)

    The current state of generics in Delphi   To avoid duplication of generated code, the compiler build ...

  8. Qt532_QWebView做成DLL供VC/Delphi使用_Bug

    Qt5.3.2 vs2010 OpenGL ,VC6.0,Delphi7 1.自己继承 类QWebView,制作成DLL 供 VC6/Delphi7 使用 2.测试下来,DLL供VC6使用: 加载&q ...

  9. 【QTP专题】01_安装时报DLL无法注册(转载)

    安装QTP过程中报很多DLL注册失败,全部忽略后安装完成,结果打开QTP录制的脚本无法保存,(点击保存按钮没反应) 1.问题分析: 问题a 使用精减版的操作系统 问题b  需要IE 6.0 及以上版本 ...

随机推荐

  1. 【洛谷P3835】 【模板】可持久化平衡树

    可持久化非旋转treap,真的是又好写又好调 ~ code: #include <cstdio> #include <cstdlib> #include <algorit ...

  2. electron/nodejs实现调用golang函数

    https://www.jianshu.com/p/a3be0d206d4c 思路 golang 支持编译成c shared library, 也就是系统中常见的.so(windows下是dll)后缀 ...

  3. docker概述和安装及基本操作

    一:概述 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化,容器是完全使用 ...

  4. ASP.NET Core 简介

    .NET Core 是 .NET Framework 的新一代版本,是微软开发的第一个具有跨平台 ( Windows.Mac OSX .Linux ) 能力的应用程序开发框 ASP.NET Core ...

  5. 【LG2605】[ZJOI2010]基站选址

    [LG2605][ZJOI2010]基站选址 题面 洛谷 题解 先考虑一下暴力怎么写,设\(f_{i,j}\)表示当前\(dp\)到\(i\),且强制选\(i\),目前共放置\(j\)个的方案数. 那 ...

  6. [技术博客]微信小程序审核的注意事项及企业版小程序的申请流程

    关于小程序审核及企业版小程序申请的一些问题 微信小程序是一个非常方便的平台.由于微信小程序可以通过微信直接进入,不需要下载,且可使用微信账号直接登录,因此具有巨大的流量优势.但是,也正是因为微信流量巨 ...

  7. 64位Winows2008下连接Access数据库 Jet4.0不支持解决代替方案

    如何在windows2008 64位的机子上连接Access数据库 用以前的连接Access数据库的方式Provider=Microsoft.Jet.OLEDB.4.0在32位机子上可以使用,在64位 ...

  8. POJ-数据结构-优先队列模板

    优先队列模板 优先队列是用堆实现的,所以优先队列中的push().pop()操作的时间复杂度都是O(nlogn). 优先队列的初始化需要三个参数,元素类型.容器类型.比较算子. 需要熟悉的优先队列操作 ...

  9. Delphi 开发微信公众平台 (二)- 用户管理

    一.用户标签管理 开发者可以使用用户标签管理的相关接口,实现对公众号的标签进行创建.查询.修改.删除等操作,也可以对用户进行打标签.取消标签等操作. 1.创建标签 /// <summary> ...

  10. cmdb知识总结

    cmdb面试 1.paramiko模块的作用与原理 2.cmdb是什么 3.为什么要开发CMDB? 4.你们公司有多少台服务器?物理机?虚拟机? 5.你的CMDB是如何实现的? 6.CMDB都用到了哪 ...