以前写的那个dll注入在技术对抗中是比较容易检测的,因为你要加载dll,对方只要遍历一下dll,或者dll个数检测一下,有问题直接退出程序,而汇编代码注入就不一样,这是直接把代码写到内存中,检测内存难度可比检测dll要难的,而且你也可以利用内存对齐把代码写到因对齐而空闲的内存中,反正这些靠自己去想吧,原理就是在内存中注入代码,至于在哪个内存地址写就看自己想象了
注入原理:利用win32 api的VirtualAllocEx在目标进程开辟一块内存空间,用api—VirtualProtect修改一下内存属性,把自己的二进制代码利用api–WriteProcessMemory写进目标进程中,在利用api—CreateRemoteThread在目标进程中创建一个线程去执行这段内存的代码
**注意点:**一般进程都会加载ntdll,kernel32.dll也有可能加载,user32.dll,我选择的是调用user32.dll中的messagebox,所以要如果要所有的目标程序都支持你的注入程序(注入程序具有通用性)就要在注入的时候,让目标程序加载一下user32.dll,
遍历一下目标进程的模块(有api遍历模块),查找是否有user32.dll,如果没有让目标进程加载user32.dll
目标进程加载user32.dll思路有两种:一种就是我上次写的dll注入,就在目标进程开辟一块内存,内存中存放user32.dll字符串做参数,然后获取kerner32.dll中的LoadLibrary函数地址,利用创建远程线程去执行LoadLibrary函数(如果不成功说明自己kernel32.dll和目标的kernel32版本不一样,或者api地址不一样,这时候就要做地址重定位,利用偏移去定位地址)
第二种就是我们现在写的这种,用注入汇编代码调用api,这种方式看下列代码就清楚了
两种写法注入
1用纯32位汇编写代码
用radAsm创建一个窗口程序,点击按钮进行注入代码,代码如下
injection.inc
include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include shell32.inc
includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib shell32.lib
DlgProc PROTO :HWND,:UINT,:WPARAM,:LPARAM
IDD_DIALOG1 equ 101
IDB_INJECT EQU 1001
.const
g_WindowName db "计算器", 0
g_ClassName db "SciCalc", 0
g_User32 db "user32.dll", 0
g_MessageBox db "MessageBoxA", 0
;#########################################################################
.data?
hInstance dd ?
injection.asm
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive
include Inject.inc
.code
InjectCode:
;loadlibrary "user32.dll"
;参数入栈,参数从右往左入栈
push 00646c72h
push 6f57206fh
push 6c6c6568h
mov ebx, esp
push 00000065h
push 6c746954h
mov ecx, esp
push MB_OK
push ecx
push ebx
push NULL
LABEL1:
mov eax, 12345678h ;12345678只是为了开辟一块内存好放api实际的地址,注入代码前会把这片内存进行api地址重定位
call eax
add esp, 20
retn 4 ;创建的远程线程的函数的参数默认值0平栈
Inject proc
LOCAL @hWnd:HWND
LOCAL @dwPID:DWORD
LOCAL @hProcess:HANDLE
LOCAL @pBuff:PVOID
LOCAL @dwNumber:DWORD
LOCAL @hThread:DWORD
LOCAL @hUser32:DWORD
LOCAL @pfnMsgBox:DWORD
LOCAL @dwOld:DWORD
;远程线程注入
;1.FindWindow
invoke FindWindow, offset g_ClassName, NULL
.if eax == NULL
ret
.endif
mov @hWnd, eax
;2.获取进程pid
invoke GetWindowThreadProcessId, @hWnd, addr @dwPID
;3.打开进程
invoke OpenProcess,PROCESS_ALL_ACCESS, FALSE, @dwPID
.if eax == NULL
ret
.endif
mov @hProcess, eax
;4.远程申请内存
invoke VirtualAllocEx,@hProcess, NULL, 1000h, MEM_COMMIT, PAGE_EXECUTE_READWRITE
.if eax == NULL
jmp SAFE_EXIT
.endif
mov @pBuff, eax
;修改内存保护属性,这是修改自己内存属性的版本,EX版本是修改目标内存属性的
invoke VirtualProtectEx,@hProcess,offset InjectCode, 1000h, PAGE_EXECUTE_READWRITE, addr @dwOld
;这里可以加遍历模块列表进行判断是否有user32.dll,没有就加载,我只是写一个简单的demo
;API地址重定位
invoke GetModuleHandle, offset g_User32
mov @hUser32, eax
invoke GetProcAddress,@hUser32, offset g_MessageBox
mov @pfnMsgBox, eax
mov esi, @hUser32
sub eax, esi ;offset
add eax, esi ;+base
mov ebx, offset LABEL1
mov dword ptr [ebx+1], eax
;5.写入代码
invoke WriteProcessMemory,@hProcess,
@pBuff,
offset InjectCode,
offset Inject - offset InjectCode,
addr @dwNumber
.if !eax
jmp SAFE_EXIT
.endif
;6.创建远程线程
invoke CreateRemoteThread,@hProcess, NULL, 0, @pBuff, NULL, 0, NULL
.if eax == NULL
jmp SAFE_EXIT
.endif
mov @hThread, eax
SAFE_EXIT:
.if @hThread != NULL
invoke CloseHandle, @hThread
.endif
.if @hProcess != NULL
invoke CloseHandle,@hProcess
.endif
ret
Inject endp
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke InitCommonControls
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
invoke ExitProcess,0
;########################################################################
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
mov eax,uMsg
.if eax==WM_INITDIALOG
.elseif eax==WM_COMMAND
mov eax, wParam
.if ax == IDB_INJECT;如果点击按钮就调用函数
invoke Inject
.endif
.elseif eax==WM_CLOSE
invoke EndDialog,hWin,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
end start
2 把注入代码用汇编写,然后生成动态库,把动态库的代码注入到目标进程
1 用radAsm生成汇编代码的dll
1.1创建一个dll工程
1.2在工程----工程选项-----链接中加上/entry:_DllMainCRTStartup@12
**注意:**加上dllmain入口点才能编译,而_DllMainCRTStartup@12在msvcrt.lib库中,但是radAsm的msvcrt.lib库是旧的,没有_DllMainCRTStartup@12,所以自己要去vc中找一个新的msvcrt.lib库去D:\RadASM\masm32\lib目录中进行替换,如果是静态库需要用libc.lib
**说明:**如果不是dll,必须指明入口点,如果不指明的话,程序不会在调用入口初始化程序,这样的话有些api是用不了的,因为这些api必须要初始化后功能才是正确的
代码如下
injectDll.Asm
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive
include windows.inc
includelib msvcrt.lib
include kernel32.inc
includelib kernel32.lib
.code
InjectCode proc
push 00646c72h
push 6f57206fh
push 6c6c6568h
mov ebx, esp
push 00000065h
push 6c746954h
mov ecx, esp
push MB_OK
push ecx
push ebx
push NULL
LABEL1:
mov eax, 12345678h
call eax
add esp, 20
retn 4
InjectCode endp
GetCodeSize proc
mov eax, GetCodeSize - InjectCode
ret
GetCodeSize endp
end
injectDll.Def
EXPORTS
InjectCode
GetCodeSize
编译连接生成dll,我另外生成了一个obj用来确定需要重定位地址偏移位置
2在vs中写注入流程代码,核心代码如下
extern "C" int _stdcall GetCodeSize();//这个是obj的函数,obj可以直接加载到vs工程中,函数声明一下就可以使用,这也是c调汇编比较简单的方式
void CMFCInjectionDlg::OnBnClickedInjection()
{
//首先得到点击的位置
POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
if (pos == NULL)
{
MessageBox("请至少选择一项或者遍历进程", "选择", MB_ICONEXCLAMATION);
return;
}
//得到行号,通过POSITION转化
int nId = (int)m_listCtrl.GetNextSelectedItem(pos);
//得到列中的内容(0表示第一列,同理1,2,3...表示第二,三,四...列)
string str = m_listCtrl.GetItemText(nId, 0);
DWORD dwProcessId = atoi(str.c_str());
//1打开进程
HANDLE hProces = OpenProcess(
PROCESS_ALL_ACCESS,
FALSE,
dwProcessId);
// 2 在目标进程申请内存,写入注入代码
LPVOID pDllPathOfDstProc =
VirtualAllocEx(
hProces,
NULL, //系统自动分配地址
0x1000, //申请内存大小
MEM_COMMIT, //物理映射
PAGE_READWRITE //可读可写
);
if (pDllPathOfDstProc == NULL)
{
AfxMessageBox(_T("申请内存失败"));
return;
}
//获取目标进程的函数地址
HMODULE hUser = GetModuleHandle("user32.dll");
LPVOID pMessageBoxA = GetProcAddress(hUser, "MessageBoxA");
if (pMessageBoxA == NULL)
{
AfxMessageBox(_T("获取MessageBoxA地址失败"));
return;
}
int pBase = (int)hUser;
int pDest = (int)pMessageBoxA;
int pOffset = pDest - pBase;
int pModifValue = pBase + pOffset;//获取api实际地址
//重定位地址
int nOffset = GetCodeSize();//获取要重定位位置偏移
nOffset -= 4;//在这个位置修改
HMODULE hModule = LoadLibrary("injectDll.dll");
LPVOID pLoadLibrary = GetProcAddress(hModule, "InjectCode");
if (pLoadLibrary == NULL)
{
AfxMessageBox(_T("获取InjectCode地址失败"));
return;
}
DWORD dwOld2;
VirtualProtect(pLoadLibrary, 0x1000, PAGE_EXECUTE_READWRITE, &dwOld2); //修改内存属性
int nAdder = (int)pLoadLibrary + nOffset;
int* pAdder = (int*)nAdder;
*pAdder = pModifValue;//重定位api地址
//获取要注入代码的大小
typedef int(*PFN_GetCodeSize)(void);
PFN_GetCodeSize pfnGetCodeSize = (PFN_GetCodeSize)GetProcAddress(hModule, "GetCodeSize");
if (pLoadLibrary == NULL)
{
AfxMessageBox(_T("获取MessageBoxA地址失败"));
return;
}
DWORD nSize = pfnGetCodeSize();
//修改目标内存属性
DWORD dwOld;
VirtualProtectEx(hProces ,pDllPathOfDstProc, 0x1000, PAGE_EXECUTE_READWRITE, &dwOld);
DWORD dwBytestToWrite = 0;
BOOL bRet = WriteProcessMemory(
hProces,
pDllPathOfDstProc,//写入内存的首地址
pLoadLibrary,
nSize,
&dwBytestToWrite);
if (!bRet)
{
AfxMessageBox(_T("写入路径失败"));
return;
}
// 3) 创建远程线程,目标进程创建了一个线程,执行目标线程的线程回调函数
HANDLE m_hRemoteThread = CreateRemoteThread(
hProces,
NULL,
0,
(LPTHREAD_START_ROUTINE)pDllPathOfDstProc, //在目标进程中的回调函数的地址
NULL,//此时内存存放的是dll,这时候调用pLoadLibrary线程回调函数就会把dll当做参数传进去,就把dll加载进去了,
0,
NULL);
if (m_hRemoteThread == NULL)
{
AfxMessageBox(_T("远程线程创建失败"));
}
}