高手请飘过~~~~~~~~~~~~~~~~~~~~~~~~~~~
前一篇文章,写到了利用远程线程注入DLL,其原理是利用LoadLibaray把DLL加载进去,这种方式注入简单,但是会在对方进程加载一个模块。人家用工具一看,多了个模块,就知道被搞了。。。
今天写下用远程线程注入shellcode, 注入shellcode的难点是,注入的代码不能访问绝对地址。比如不能存在对全局变量的访问,不能调用API,因为你的代码注入到人家进程空间,相应的绝对地址,很可能不是一个全局变量,或者你要调用的API。关于shellcode更多的知识,需要自己去查找更多资料,我也只懂点皮毛,就不多说了。
利用远程线程注入shellcode和注入dll的优缺点建议大家看这篇文章:
https://www.cnblogs.com/BoyXiao/archive/2011/08/11/2134367.html
注入shellcode,原理很简单,在目标进程开辟一段空间,分别将shellcode写过去,这个写过去的shellcode就是远程线程要执行的代码,再将shellcode需要的参数写过去。那么写过去的这两段数据分别是CreateRemoteThread的第四个第五个参数。
代码如下:代码中有几个函数,很常用,我放到DLL中了,所以这里你看不到实现,如果需要完整代码可以QQ844255657,或者百度一下,一个是提权代码,一个是根据进程名获取PID,网上一把大。另外计算shellcode的大小时,我是利用两个函数地址相减,获得shellcode的大小,我在IDA中看了下,这两个函数正好相邻,所以我就用两个地址相减。这不知道是不是巧合。需要特别之处的是,程序是64位的。不知道32位的是不是编译后两个函数不会相邻了。这个有懂得希望留言。另外在说下shellcode就是函数:DWORD WINAPI RemoteThreadProc(LPVOID lpParameter) 。 怕新手不懂多啰嗦一句。
// InjectCodeByRemoteThread.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include <Psapi.h>
#include "D:\\VS\\DllToInject\\DllToInject\\Utils.h"
#pragma comment(lib, "D:\\VS\\DllToInject\\x64\\Release\\UtilDll.lib")
typedef int (* FUN_MessageBox)(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
);
typedef struct tagRemoteMsgBoxData{
FUN_MessageBox pfn;
CHAR lpText[MAX_PATH];
CHAR lpCaption[MAX_PATH];
}REMOTE_MSG_BOX_DATA, *PREMOTE_MSG_BOX_DATA;
typedef DWORD(WINAPI * FUN_RemoteThreadProc)(LPVOID lpParameter);
DWORD WINAPI RemoteThreadProc(LPVOID lpParameter){
PREMOTE_MSG_BOX_DATA pRemoteData = (PREMOTE_MSG_BOX_DATA)lpParameter;
pRemoteData->pfn(NULL, pRemoteData->lpText, pRemoteData->lpCaption, MB_OK);
return 1;
}
void FillRemoteMsgBoxData(PREMOTE_MSG_BOX_DATA pRemoteMsgBox){
HMODULE hUser32 = LoadLibrary("User32.dll");
pRemoteMsgBox->pfn = (FUN_MessageBox)GetProcAddress(hUser32, "MessageBoxA");
strcpy(pRemoteMsgBox->lpText, "this is content");
strcpy(pRemoteMsgBox->lpCaption, "this is title");
FreeLibrary(hUser32);
}
int _tmain(int argc, _TCHAR* argv[])
{
//两个函数地址的差值就是shellcode代码的大小,可能会有一些多的对齐数据,但是不影响
DWORD funcSize = (DWORD)FillRemoteMsgBoxData - (DWORD)RemoteThreadProc;
printf("shellcode size =%d\n", funcSize);
REMOTE_MSG_BOX_DATA remoteData = { 0 };
FillRemoteMsgBoxData(&remoteData);
if (!AdjustProcessTokenPrivilege()){
MyOutputDebugString("提权失败\n");
}
DWORD dwPid = GetPidByName("explorer.exe");
if (dwPid == 0){
MyOutputDebugString("GetPidByName failed\n");
return -1;
}
MyOutputDebugString("explorer.exe pid=%d\n", dwPid);
HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
MyOutputDebugString("打开进程失败!!!!");
return -1;
}
//1.在远程进程中分配内存,可读可写可执行
LPVOID pszRemoteBuffer = (char *)VirtualAllocEx(hProcess, NULL, USN_PAGE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pszRemoteBuffer == NULL)
{
MyOutputDebugString("申请远程空间失败");
return -1;
}
//2.在远程申请的内存空间中写入shellcode
SIZE_T dwWriten = 0;
if (!WriteProcessMemory(hProcess, pszRemoteBuffer, RemoteThreadProc, funcSize, &dwWriten))
{
MyOutputDebugString("写入内存失败");
return -1;
}
printf("shellcode write bytes:%d\n", dwWriten);
//3.在远程申请的内存空间中写MessageBox的数据{函数地址,content , title}
dwWriten = 0;
if (!WriteProcessMemory(hProcess, (CHAR *)pszRemoteBuffer + funcSize, &remoteData, sizeof(remoteData), &dwWriten))
{
MyOutputDebugString("写入内存失败");
return -1;
}
//4.创建远程线程
DWORD dwThreadId = 0;
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (FUN_RemoteThreadProc)pszRemoteBuffer, (CHAR *)pszRemoteBuffer + funcSize, 0, &dwThreadId);
if (hThread == NULL)
{
MyOutputDebugString("创建远程线程失败 %d\n", GetLastError());
return -1;
}
//5 等待远程线程退出,等待shellcode执行完毕
DWORD exitCode = 0;
DWORD dwRet = 0;
WaitForSingleObject(hThread, INFINITE);
dwRet = GetExitCodeThread(hThread, &exitCode);
if (dwRet == 0){
printf("GetExitCodeThread failed\n");
return -1;
}
//6 检查退出码,如果退出码等于1,说明shellcode执行成功
//为什么等于1执行成功,shellcode返回值是1啊
if (exitCode == 1){
printf("shell code execute success\n");
}
//释放内存,关闭句柄等
VirtualFreeEx(hProcess, pszRemoteBuffer, USN_PAGE_SIZE, MEM_DECOMMIT);
::CloseHandle(hThread);
::CloseHandle(hProcess);
return 0;
}