dll注入基础教程Word文档格式.docx
- 文档编号:3810415
- 上传时间:2023-05-02
- 格式:DOCX
- 页数:13
- 大小:22.69KB
dll注入基础教程Word文档格式.docx
《dll注入基础教程Word文档格式.docx》由会员分享,可在线阅读,更多相关《dll注入基础教程Word文档格式.docx(13页珍藏版)》请在冰点文库上搜索。
(而且建立窗口时候默认就会引用很多单元)
所以实际上我们可以直接用的
这个理解一下就好了
开始讲线程插入了
一般来说线程插入有2种方法
1.DLL注入
2.直接的远程线程插入
DLL注入编写的时候比较简单,方法也多,但有个缺点进程会多出个模块来,可能被发现
远程线程是直接修改对方内存的方法,虽然隐蔽性好,但是不小心可能会出点问题,比如你让不能有界面的进程弹出个窗口,不能上网的进程开个端口,那就等着系统崩溃吧
今天重点讲DLL注入
首先要知道什么是DLL,dll就是动态链接库,大家应该知道吧?
怎么编写DLL呢?
和写普通程序差不多是一样的
新建一个工程,选DLLWizard
发现了吧?
除了program改成library
剩下几乎是一样的,只是需要程序加载他的入口点
我们先编写个简单的DLL
libraryTestDll;
uses
Windows;
{$R*.res}
procedurefunc_a;
begin
MessageBox(0,'
Ilovedelphi'
'
FunctionformTsetDLL'
0);
end;
procedurefunc_b(MSG:
pchar);
MessageBox(0,MSG,'
func_a;
func_b('
Ilikeittoo!
'
);
end.
就是那个testdll.dpr
看得懂吧,弹出2个信息框
好了保存下,F9运行..出错,哈哈,DLL是不能直接运行的
那怎么办?
编译下(按过F9就不用了,也会编译好)
看见那个DLL了吧?
我们弄个程序加载它的入口点
新建一个普通程序
加一个按钮
按钮事件只要写一句loadlibrary('
testdll.dll'
MainShow.dpr
运行,单击按钮,怎么养?
弹出东西了吧
当然DLL还可以做函数库,资源库等今天暂不讨论
现在DLL懂得写了吧?
就是program改成library而已
你可以写自己的程序了
DLL会写了,现在的问题就是怎么注入了
我们目的只是让对方的程序运行一句loadlibrary('
而已
一切就OK了
通常有这么几种注入方法
1.利用全局消息钩子
Win32下程序一般都要用收发消息
用钩子函数下全局钩子,程序收到任何消息都加载我们的DLL的入口点
当然,用这种方法DLL进入后要判断自己是不是被插进目标进程
是的话,执行代码
不是退出
这个方法很麻烦但通用性很好,只要WINDOWS都可以用(但是有的系统进程消息是勾不住的,所以注入不了)
早期的DLL注入大部分是用这个原理实现的
2.写注册表
写HKEY_LOCAL_MAHINE\Software\Microsoft\WindowsNT\CurrentVersion\Windows\AppInit_DLLs
但是只能是NT下,而且必须是调用过user32.dll(Windows的一个内核,只要有界面的程序都调用它)的程序,在开机后所有程序会自动加载DLL,但是这个dll不能被卸载,而且不能调用某些函数,不然系统会挂掉(有危险性哦)
不推荐使用
3.利用远程线程注入来实现DLL注入
只要能打开句柄,就能成功(强吧?
)而且实现起来比较简单
缺点就是9X内核的有点难度(也不是很困难)
我们今天就讲利用远程线程注入来实现DLL注入吧!
你可能要问,既然刚才说了远程线程可以直接注入一个线程,为什么还要多此一举反过来再调用DLL呢?
这是因为,远程线程技术一般是直接对目标程序的内存进行操作
我们知道不同程序的虚拟内存是不一样的
所以很多函数的地址不一定一样
而程序运行的时候实际上是Call函数地址进行函数调用的
所以我们要注意计算很多偏移之类的
这是非常烦琐的事情
而且像上面说的让不能有界面的进程弹出个窗口,那就不好玩了
而DLL呢?
DLL调用时其实是被映射到进程内存里面
DLL拥有自己的导入表、资源、函数等东西,实际上就是一个完整的程序
映入内存后和执行一个程序效果是一样的
这样我们就不用考虑那些乱七八糟的东西,只要安心的写功能即可
好了,
要明白远程线程首先当然要把程序本地线程搞清楚了
不知道大家编多线程程序的时候是不是都用tthread类?
反正我是不喜欢那个
我们看看Windows给我们的原始API吧(tthread类也是用它写的)
functionCreateThread(
lpThreadAttributes:
Pointer;
//安全指针一般nil就可以了
dwStackSize:
DWORD;
//线程初始化尺寸,一般用0,获得与主线程一样尺寸(不够自己会增加,别担心)
lpStartAddress:
TFNThreadStartRoutine;
//一个指向要执行线程函数的指针,这个函数必须遵守stdcall约定,并且可带一个参数,参数必须是指针类型
lpParameter:
//函数的参数
dwCreationFlags:
//控制创建标志,用0表示线程立刻执行
varlpThreadId:
DWORD)
//返回标识变量我觉得没什么用,反正句柄都有了
:
THandle;
//返回线程的句柄
stdcall;
//标准调用Windows下API一般都是标准调用
大家先看下
看起来似乎比较复杂,等下举个例子
我们把DLL源码里面的func_b拷到刚才那个EXE上
稍微修改下
stdcall;
sleep(10000);
//线程暂停N久(不超过10s)
加上2个按钮
第一个
procedureTForm1.Button2Click(Sender:
TObject);
func_b('
123'
第二个
procedureTForm1.Button3Click(Sender:
vartid:
longword;
//放返回值,不放她不让执行,郁闷
str:
pchar;
//便于获得pointer
='
createthread(nil,
0,
@func_b,
//函数名前面加@是得到函数指针
pointer(str),//虽然str也是指针,但是delphi就是要pointer型的,那就转一下类型
0,tid);
//tid纯属放着占格式的,一般我们用不到
//上面CreateThread看得懂吧,几乎都是默认设置,以后套下去用就是了
实际上都是调用func_b,只是第二个过程用了信新线程
但是效果是不一样的
第一个按钮按下弹出窗口后,程序卡死了(暂停10000)
第二个却不会
为什么呢?
我们可以这样理解
窗口看做一个主线程,执行func_b,弹出窗口,然后主线程挂起,于是卡死了
而第二个过程创建一个新线程,新线程执行func_b,弹出窗口,挂起10000,但是由于主线程没有挂起,所以看起来关掉窗口后没什么事情发生(实际上那个线程还在偷偷执行,直到线程代码运行完,只是它卡死不会影响你)
这个如果明白了那么下面就容易理解了
看看这个函数
functionCreateRemoteThread(
hProcess:
lpThreadAttributes:
dwStackSize:
lpStartAddress:
lpParameter:
dwCreationFlags:
varlpThreadId:
:
除了函数名不一样,下面的参数多了个hProcess:
,剩下的完全一样
呵呵,这个东西就是本节课的关键了
先看函数名就知道是干什么用的了‘创建远程线程’
用法和刚才基本一致
就是hProcess:
THandle是什么呢?
这里要填的是被注入线进程的句柄
什么是句柄?
打个比方,对象是一个门,句柄就是那个把手,通过句柄我们可以对门进行操作
也就是说我们利用句柄来操作某些东西(包括进程、线程等等)
你有没有注意到,CreateThread和CreateRemoteThread都返回一个THandle,也就是线程的句柄
还有loadlibrary也会返回DLL的句柄,我们可以利用他们对相关对象进行操作
那么怎么获得进程句柄呢?
一般采用先得到进程PID再用下面的函数取得句柄
functionOpenProcess(
dwDesiredAccess:
//访问标志一般填写PROCESS_ALL_ACCESS,这样这个句柄可以获得最大操作权限
bInheritHandle:
BOOL;
//可否继承,这个跟子程序有关,无所谓了,填false和true都可以,反正我们自己能操作久可以
dwProcessId:
DWORD):
//要获得句柄的进程ID
THandle;
//返回句柄
有时候会返回0,说明打开句柄失败了
一般是你的权限不够(比如你想对Winlogon这些系统级程序操作)
这时候我们需要提升权限一般Debug权限就可以了(其实操作权限里面最高了)
提升的过程我写好了
直接调用就可以了(修改进程令牌到Debug级别,为什么这样写这里不详细讲了,自己去网上搜索下)
procedureGetDebugPrivs;
var
hToken:
tkp:
TTokenPrivileges;
retval:
dword;
If(OpenProcessToken(GetCurrentProcess,TOKEN_ADJUST_PRIVILEGESorTOKEN_QUERY,hToken))then
LookupPrivilegeValue(nil,'
SeDebugPrivilege'
tkp.Privileges[0].Luid);
tkp.PrivilegeCount:
=1;
tkp.Privileges[0].Attributes:
=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,False,tkp,0,nil,retval);
不会晕吧?
应该记得我刚才提到了要PID,那怎么得到呢?
一般用FindWindow和GetWindowThreadProcessId配合的到
这样写
先varPid:
//储存那个PID
GetWindowThreadProcessId(FindWindow('
Notepad'
nil),@PID);
这样就找到笔记本的PID,再如'
Shell_TrayWnd'
可以找到Explorer的
窗口类名据说可以用SPY++查询,不过这东西我没见过,呵呵
当然还可以枚举进程判断进程名等等
这个先告一段落。
好了,拿Windows的笔记本下手吧
procedureTmyForm.Button4Click(Sender:
varh:
//PID和THandle的类型其实都是longword,改个名字而已,所以可以通用
winexec('
notepad'
1);
//运行笔记本
nil),@h);
//得到Pid存在h
h:
=OpenProcess(PROCESS_ALL_ACCESS,False,h);
//得到handle存在h,后面那个是变量pid,算完放到前面的h是句柄(两个不同的东西,只是类型一样而已)
sleep(2000);
//等2秒
TerminateProcess(h,0);
//关闭笔记本,h是那个句柄,0表示正常退出
运行起来就是打开一个笔记本,大约2s狗关掉它
不知道大家看懂了没有,没有不要紧,只是为了证明我们可以拿到一个课操作的进程句柄
好像万事具备了吧?
那试试远程线程了吧
再建一个按钮
前面的还是这样写,再把那个建立线程的拷过来
改成CreateRemoteThread加上h参数
procedureTmyForm.Button5Click(Sender:
tid:
CreateRemoteThread(h,nil,
0,
@func_b,pointer(str),0,tid);
运行起来
笔记本出来了,对话框也出来了...
可是对话框却不是我们弄的那个,是个报错的
看看写了什么
内存'
0x00000000'
不能为'
writen'
记得我刚才说的么
远程线程是在别的程序里运行一个线程
相当于让里一个函数执行CreateThread
所以,函数的地址不一定是一样的,更何况笔记本里面怎么可能会有func_b这个我们自己写的函数呢
这么一来当然要出错了
这下傻了,那怎么注入我们要的函数呢?
记得我们要讲什么吗?
-利用远程线程进行DLL注入
我们可以把函数写在DLL里面,用远程线程让目标进程加载它
这样函数就执行了
我们只要想办法让对方程序loadlibrary('
那就OK了
看看LoadLibrary的原型
functionLoadLibrary(lpLibFileName:
PAnsiChar):
HMODULE;
你应该发现了它和线程要求的函数格式几乎一样
参数是指针型PAnsiChar就是pchar,一个指向字符串的指针
返回HMODULE,HMODULE实质是longword(改个名字而已)
^_^,那就远程运行它吧
这时候你可能会想,LoadLibrary的地址要怎么得到呢?
要知道,LoadLibrary是一个API(在Kernel32.dll里面),实际上,每个Win32程序都需要里面的函数
所以,大部分程序运行代码前会装入这个DLL,把里面的函数映射到自己的内存了
这么一来,只要是这个DLL里面同一个函数在所有的进程里地址都是一样的
哈哈,这样就容易了
地址我们一般用GetProcAddress
functionGetProcAddress(
hModule:
//模块句柄,DLL被加载后就成立模块,等下告诉大家怎么得到这个
lpProcName:
LPCSTR
//函数在DLL中的导出名LoadLibrary实际上是LoadLibraryA
//这个大家看看DelphiWindows单元的源码就知道了
):
FARPROC;
//返回指针
那些类型看得乱乱的吧,不要管他们,在Delphi上不鼠标停在函数上,类型的原型就出来了
好了
现在是怎么得到那个模块的句柄的问题
用GetModuleHandle
functionGetModuleHandle(
lpModuleName:
PChar)//模块名,DLL被加载后就成立模块,所以就是DLL的文件名了
//返回模块句柄
好了。
知道了这些得到函数地址就容易了
GetProcAddress(GetModuleHandle('
KERNEL32.DLL'
),'
LoadLibraryA'
一句搞定
问题似乎都解决了吧?
先别高兴,不要忘记了,它还带了个参数,就是那个DLL的名字
参数类型是一个指向字符串地址的指针
这个是个大问题,一来你不能保证别人的程序内存里有这个字符串
二来有你也不知道他的位置,这可怎么办呢?
自己写!
我们把那个字符串写到对方内存里
呵呵,很霸道的方法,但的确是个好方法
不废话了,开始
我们首先要在目标进程申请一块内存,以便把那个参数写进去
申请内存用VirtualAllocEx,看看它的原型
functionVirtualAllocEx(
hProcess:
//目标进程句柄,这个不用说了吧
lpAddress:
//分配内存位置,一般用nil,这样会在系统认为最合适的位置分配
dwSize:
//分配的地址范围,也就是大小了
flAllocationType:
//如何分配地址,一般用MEM_COMMIT为指定空间提交物理内存
flProtect:
DWORD//该段内存的保护类型,PAGE_READWRITE表示可读可写
//返回内存地址,哈哈,这就是我们要的那个参数的指针了
好了,分配完内存当然是要把我们的数据写过去了
这时候需要用到WriteProcessMemory来写进程的内存
functionWriteProcessMemory(
//目标进程句柄
constlpBaseAddress:
//要写的内存地址,就填我们那个参数的指针
lpBuffer:
//数据的地址,我们把字符串存这里,让他拷
nSize:
//要拷贝的数据长度
//字符串在Windows定义是以null(就是16进制的0)结尾的
//所以长度就是字符串的长度+1
varlpNumberOfBytesWritten:
DWORD)//返回的什么东西,没什么用
//返回成功或失败
我们来写个完整的代码吧
procedureTmyForm.Button6Click(Sender:
//放句柄,中间顺便暂放下PID
tmp:
//这个专门来占格式收集垃圾
DllName:
Mysize:
//放字符串长度
Parameter:
pointer;
//放那个参数的指针(位置在目标进程内)
DLLName:
Testdll.dll'
=strlen(Dllname)+1;
=VirtualAllocEx(h,nil,Mysize,MEM_COMMIT,PAGE_READWRITE);
WriteProcessMemory(h,Parameter,Pointer(DllName),MySize,tmp);
0,GetProcAddress(GetModuleHandle('
),Parameter,0,tmp);
又看到那两个熟悉的对话框了
哈哈,这么说我们成功了
如把那个DLL换成其他的功能,那就...
今天的东西差不多就讲到这里
你可能想,单纯的远程线程是怎么实现的呢?
刚才我们能往对方内存里拷进去字符串
当然可以把整个函数拷进去
时间关系不详细讲了
我给大家一个函数可以直接用
这个是国外一个叫Aphex的写的
很方便可以直接注入函数
但是要改写入口点
procedureInject(ProcessHandle:
longword;
Ent
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- dll 注入 基础教程