赞
踩
vs2019
编写注入器程序, 在生成的注入器可用前, 踩了不少坑, 因此记录一下.LoadLibrary
函数, 因而在枚举进程的模块列表时可看到注入的dll.LoadLibrary
, 所以枚举目标进程的模块列表并不能看到注入的dll. (意味着目标进程的PEB没有变化)注:
的地方基本都是踩的坑.OnMouseMove
和InjectDll
OpenProcess
打开目标进程(一参为PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE| PROCESS_VM_READ
, 后面CreateRemoteThread
才能执行)
GetLastError
会返回ERROR_NOT_ALL_ASSIGN
.
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid)
得到用户的debug权限.OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES,&hToken)
获取进程的令牌句柄.AdjustTokenPrivileges
启用特权.csrss.exe
等), 需在驱动中打开, 去掉关键进程的EPROCESS
中的PsIsProtectProcess
标志位, 并关闭dll签名策略
. (参考开源项目blackbone
)VirtualAllocateEx
), 将路径拷贝到该内存中(WriteProcessMemory
)LoadLibraryA
地址(如果dll路径是宽字符串, 则用LoadLibraryW
)CreateRemoteThread
, 在目标进程中执行LoadLibrary
, 进而执行DllMain
函数中的目标代码
VirtualAllocEx
返回的是虚拟地址, 默认情况下CreateRemoteThread
函数的lpStartAddress
参数使用该地址是没问题的. 但是若注入器是32位而被注入程序是64位, 则可能导致CreateRemoteThread
失败而返回NULL. 参考: https://stackoverflow.com/questions/60687458/createremotethread-returns-null-while-trying-to-use-it-to-inject-dllLoadLibrary
函数注入dllDOS头
, PE头
, 区块表
)逐字节写入新开辟的内存.LdrLoadDll
得到shellcode需要的库的内存地址, LdrGetProcedureAddress
得到要导入的函数的内存地址, 然后将这些地址填入导入表.GetProcAddress
获取(使用GetModuleHandleA
获取模块句柄, 传入的模块名不用后缀), 并作为参数传给shellcode.需要使用jmp指令的操作数计算实际函数地址
. 如下获取一个shellcode函数的代码:// shellcode函数 void shellcodeFunc(PMY_PARAMS pParams) { // pParams保存LdrLoadDll等系统api的内存地址 // 用NtAllocateVirtualMemory在目标进程中开辟一块内存(需指定PAGE_EXECUTE_READWRITE权限) // 将dll的文件内容写入开辟的内存 // 修复导入表; 重定位 // 执行dll的入口函数DLLMain } DWORD size = 0, ssss=0; // 获取jmp指令后的双字操作数(即jmp的目的地址偏移) DWORD* jmpAddr = (DWORD*) ((BYTE*) shellcodeFunc + 1); // 加5得到jmp指令的下一条指令地址, 然后加上jmp的目的地址偏移, 得到函数体的实际起始地址 WORD* Memx0 = (WORD*) ((BYTE*) shellcodeFunc + 5 + *jmpAddr); LONG_PTR* Memx = (LONG_PTR*) Memx0; // 用0xCCCCCCCCCCCCCCCC作为函数体结束识别标识. while (*Memx != 0xCCCCCCCCCCCCCCCC) { Memx++; size += 8; } // 将shellcode写入文件 HANDLE hFile = CreateFile(LOADECODE, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, NULL, NULL); if (hFile) { WriteFile(hFile, Memx0, size, &ssss, NULL); CloseHandle(hFile); }
编译时带上/GL
选项(C/C++
-> 优化
-> 全程序优化
, 选择是 (/GL)
).
链接时带上/INCREMENTAL:NO
选项(链接器
-> 常规
-> 启用增量链接
, 选否 (/INCREMENTAL:NO)
). 在微软对这个链接选项的说明文档中, 有如下一句:
May contain jump thunks to handle relocation of functions to new addresses.
也就是说/INCREMENTAL
选项会让程序多一张跳转表, 以处理函数的重定位. 在Debug模式下就会默认开启此选项.
出现地址访问冲突
:
__security_cookie
/GS
选项(属性
-> C/C++
-> 代码生成
-> 安全检查
, 选择禁用安全检查 (/GS-)
), 禁用栈保护.__CheckForDebuggerJustMyCode
, _RTC_CheckStackVars
/JMC
选项(属性
-> C/C++
-> 常规
-> 支持仅我的代码调试
, 选择否
), /RTC
选项(属性
-> C/C++
-> 代码生成
-> 基本运行时检查
, 选择默认值
), 其他的如果是必要使用的动态库函数, 则需要用LoadLibrary
和GetProcAddress
获取, 且要确保目标进程已载入相应dll.LdrLoadDll
: 获取注入的dll依赖的dll的内存地址.LdrGetProcedureAddress
: 获取注入的dll需导入的函数的内存地址.RtlInitAnsiString
RtlAnsiStringToUnicodeString
RtlFreeUnicodeString
: 用以配合上述两个函数, 得到导入函数的内存地址.NtAllocateVirtualMemory
: 分配内存空间, 以写入要注入的dll的文件内容.CreateRemoteThread
创建远程线程, 执行LoadLibrary
注入一个dll, 不同的是注入到进程的是一个合法dll(比如system32目录下的dll).EnumProcessModules
枚举进程模块, GetModuleBaseNameA
得到每个模块的名称, 从而找到注入的dll.malloc
或HeapAlloc
), 然后将找到的dll的PE头部内容读进来.WriteProcessMemory
向该地址写入shellcode.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。