三平台DLL(so)注入方法汇总 - Windows Linux OS X

最近一直在研究注入,写一篇全的,也给自己做个总结

DLL注入其实就是让别的进程的address space去读相关的动态库然后执行代码。他通常是别的用户可以让某个应用程序做一些他的原作者本身不想达到的目的。比如,注入代码可以hook相关的system calls或者读取相关的密码在password框

Windows注入方法:

* 在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs所列出来的dll都会被那些要读取User32.dll进程所加载,时间点是在初始化这个DLL的时候。从Windows Vista开始,AppInit_DLLs默认情况下被关闭。从Windows 7开始,AppInit_DLl机制支持代码签名。从Windows 8开始,整个AppInit_DLL当Secure Boot被打开的时候会完全被关闭,不管任何的代码签名或者安全设置

* 在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs列出来的dll会被调用Win32 API - CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW, WinExec的进程所加载

* 那些操作CreateRemoteThread或者其他注入技术的Process可以被注入程序当他开始被start的时候: 1) 打开target process的句柄,这个可以通过spawn这个process或者寻找一些由这个进程所产生出来的一些东西 - 一个可猜测的window title, 获取一系列的running process然后查询这个target的filename 2)在目标进程进行相关内存的创建,并且把要注入的dll的名字写到里面。这个步骤可以被省略当合适的dll name已经存在在那个target process里,比如当你确定这个目标进程会去读取比如user32.dll, gdi32.dll, kernel32.dll或者任何其他的dll以32.dll结尾的时候,直接读取"32.dll"会成为可能,这个技术是过去防止被注入的一个有效防御做法 3) 直接在目标进程创建一个线程thread,并且把这个thread的start address设置成LoadLibrary这个API的地址,并且把argument设置成上传给这个target的一个string. 也就是说我们可以直接写要被执行的代码本身并且开启线程而不需要在那个target里通过LoadLibrary等来写 4)操作系统随之会去调用这个注入dll的初始化代码。需要注意的是如果没有预防,如果target process关注了DLL_THREAD_ATTACH,这个通知会被发送给这个process因为每个load的module都是一个单独的线程开始

* 直接通过SetWindowsHookeEx设置的windows hook

* 通过SuspendThread / NtSuspendThread去suspend所有的threads,然后利用SetThreadContext / NtSetThreadContext来设置这些存在线程的上下文,来运行注入代码

* 开发Windows中使用LoadLibrary[Ex]不需要完全被限制的关于dll路径的其他限制

* 操作系统级别的shims

* 替换应用程序指定的dll给那些有同名函数导出的dll

Unix-like注入方法:

通过Unix环境下的dynamic linker建立在ld.so(BSD),ld-linux.so(Linux),可以通过指定LD_PRELOAD这个环境变量,任何的libraries都可以被放进这个变量里,这样可以给全局或者单独的设置给一个单一进程。比如你可以这么写

LD_PRELAOD="./test.so" prog

表示在运行prog的时候会优先加载test.so里的函数和符号,我们可以用nm -D来查看比如我们自己写的random.so(里面实现了rand()这个函数),加上LD_PRELOAD=./random.so ldd -r prog,我们可以看到他还依赖我们写的这个random.so.

/etc/ld.so.preload其实也是干这个事情 http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

可以用LD_DEBUG=libs来debug reslove libs的动态行为,你也可以直接LD_PRELOAD=xxx ldd executable_name来进行验证

有时候我们会有需求就是想在hack的函数里去调用原来的那个函数,这个时候我们可以include <dlfcn.h>然后在头文件之上加上#define _GNU_SOURCE目的是为了我们在之后的symbol获取的地方可以用RTLD_NEXT这个宏。这个宏的意思就是dlsym(RTLD_NEXT, “open”)会让其找到下一个的open函数,也就是说当前是我们这个fake open,下一个就是真正的原函数,这个技法是需要通过dlopen & dlsym来实现的。这样我们可以通过hack open的方法来得到某个应用程序会打开哪些文件,只需要fake一个open函数在这个open函数里面加上print然后返回真正的open即可 https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/

在OS X下,我们可以这么写

DYLD_INSERT_LIBRARIES=“./test.dylib” DYLD_FORCE_FLAT_NAMESPACE=1 prog

另外Unix-like systems其实也可以用debugger-based的技术来进行相关注入

举报
评论 0