首页
论坛
专栏
课程

[原创][Hook][ws2_32.dll]

2019-1-31 20:38 2568

[原创][Hook][ws2_32.dll]

2019-1-31 20:38
2568

目标程序:ws2_32.dll.

工具:

1.Visual Studio
2.DebugView

知识储备

1.dll的编写和加载流程.
2.Windows核心编程.
3.维基百科:Hooking.
4.Socket网络编程.

目标:

编写dll文件Hook调用ws2_32.dll的Send和Recv函数的程序,截取程序收发的数据包.

 

PS:当逆向程序时发现有反调试或是加壳,而你又暂时只需要程序的网络模块的收发数据时,Hook程序调用的网络函数是个不错选择.

思路

1.什么是API的Hook,原理和本质是什么,最后如何Hook?

  1. 实例:消息钩子算是Hook的一个实例.
  2. 原理:对函数内部进行挂钩处理,获取参数甚至修改执行流程跳转到自定义的函数中去执行.
  3. 本质:修改EIP指向的下一条指令,达到修改正常运行逻辑的目的.
  4. 操作:在目标函数内部修改字节,写入跳转地址的指令,防止程序崩溃需要在自定义函数中修复原先修改的内容后正常返回.

2.代码的编写流程是如何?

  1. Socket编程:编写两个示例分别调用Send/Recv函数进行通信.
  2. DLL编程:编写DLL文件获取Send/Recv的函数地址,进行挂钩截获数据.
  3. 注入利用:DLL的注入技术种类繁多,最为方便快捷有两种,远线程注入和注册表注入.

步骤

一、Socket编程

  • Recv_Server
#include "stdafx.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
VOID eMsg(char* msg);
int _tmain(int argc, _TCHAR* argv[])
{
    //HINSTANCE hlib = LoadLibrary(L"afflux.dll");
    //if (!hlib)
    //    error_msg("error:000:loadlibrary_error\n");
    //init wsadata
    WSADATA wsd;
    int code;
    code = WSAStartup(MAKEWORD(2, 2), &wsd);
    if (code == SOCKET_ERROR)
        eMsg("error:001:init_error\n");
    if (HIBYTE(wsd.wVersion) != 2 || LOBYTE(wsd.wVersion) != 2)
        eMsg("error:002:version_error\n");

    //create socket
    SOCKET sSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sSocket == SOCKET_ERROR)
        eMsg("error:003:create_socket_error\n");
    //bind
    sockaddr_in Saddr;
    Saddr.sin_port = htons(0x1234);
    Saddr.sin_family = AF_INET;
    Saddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    code = bind(sSocket, (sockaddr*)&Saddr, sizeof(sockaddr));
    if (code == SOCKET_ERROR)
        eMsg("error:004:bind_socket_error\n");
    code = listen(sSocket, SOMAXCONN);
    if (code == SOCKET_ERROR)
        eMsg("error:005:listen_socket_error\n");
    printf("server>>\n");
    while (true)
    {
        SOCKET Csocket;
        sockaddr_in Caddr;
        int CaddrSize = sizeof(Caddr);
        Csocket = accept(sSocket, (sockaddr*)&Caddr, &CaddrSize);
        if (Csocket == INVALID_SOCKET)
            eMsg("error:006:accept_client_error\n");
        char buff[100] = "";
        code = recv(Csocket, buff, 100, 0);
        if (code == SOCKET_ERROR)
            eMsg("error:005:recv_server_error\n");
        printf("recv>>%s\n", buff);
    }
    return 0;
}
  • Send_Client
#include "stdafx.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
VOID eMsg(char* msg);
int _tmain(int argc, _TCHAR* argv[])
{

    //HINSTANCE hlib = LoadLibrary(L"afflux.dll");
    //if (!hlib)
    //    error_msg("error:000:loadlibrary_error\n");
    //init wsadata
    WSADATA wsd;
    int code;
    code = WSAStartup(MAKEWORD(2, 2), &wsd);
    if (code == SOCKET_ERROR)
        eMsg("error:001:init_error\n");
    if (HIBYTE(wsd.wVersion) != 2 || LOBYTE(wsd.wVersion) != 2)
        eMsg("error:002:version_error\n");
    printf("Client>>\n");
    while (true)
    {
        //create socket
        SOCKET Csocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (Csocket == INVALID_SOCKET)
            eMsg("error:003:create_socket_error\n");
        //link
        sockaddr_in Saddr;
        Saddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
        Saddr.sin_family = AF_INET;
        Saddr.sin_port = htons(0x1234);
        int SaddrSize = sizeof(Saddr);
        code = connect(Csocket, (sockaddr*)&Saddr, SaddrSize);
        if (Csocket == INVALID_SOCKET)
            eMsg("error:004:link_server_error\n");
        char buff[100] = "";
        printf("input>>");
        gets_s(buff, 50);
        code = send(Csocket, buff, 50, 0);
        if (code == SOCKET_ERROR)
            eMsg("error:006:send_data_error\n");
        closesocket(Csocket);
    }
    return 0;
}

二、Hook_Dll编写

  • 函数地址
1. LoadLibrary()
2. GetProcAddress()
fun* p = (fun*)GetProcAddress(GetModuleHandle(L"moduleName"), "functionName");
  • 函数原型
>>recv
int WSAAPI recv(
    _In_ SOCKET s,
    __out_data_source(NETWORK) char FAR * buf,
    _In_ int len,
    _In_ int flags);
-->
typedef int (WINAPI *pRecv)(UINT, PSTR, int, int);
>>send
int WSAAPI send(
    _In_ SOCKET s,
    _In_reads_bytes_(len) const char FAR * buf,
    _In_ int len,
    _In_ int flags
    );
-->
typedef int (WINAPI *pSend)(UINT, PSTR, int, int);
  • 跳转指令

jmp addr    ==>    E9 XX XX XX XX
1.win32程序地址占四个字节.
2.jmp和E9对应占一字节.
3.程序内存空间虽然分页管理但是地址还是连续的,即函数之间的位移可以计算.
地址A:Recv函数的开始地址
地址A+5:jmp addr(跳转指令)
地址B:MyRecv函数的开始地址
addr = B-(A+5)
  • dllmain.cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <windows.h>
#include <stdio.h>

#define SIZE 6
BYTE oldBytes[SIZE] = { 0 };
BYTE JMP[SIZE] = { 0 };
DWORD oldProtect, myProtect = PAGE_EXECUTE_READWRITE;
typedef int (WINAPI *pRecv)(UINT, PSTR, int, int);
pRecv pOrigAddress = NULL;

void BeginRedirect(LPVOID);
int WINAPI MyRecv(UINT s, PSTR buf, int len, int flags);

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        pOrigAddress = (pRecv)GetProcAddress(GetModuleHandle(L"WS2_32.dll"), "recv");
        if (pOrigAddress != NULL)
            BeginRedirect(MyRecv);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
  • MyRecv
int WINAPI MyRecv(UINT s, PSTR buf, int len, int flags)
{
    VirtualProtect((LPVOID)pOrigAddress, SIZE, myProtect, &oldProtect);
    memcpy(pOrigAddress, oldBytes, SIZE);
    int retValue = recv(s, buf, len, flags);
    memcpy(pOrigAddress, JMP, SIZE);
    VirtualProtect((LPVOID)pOrigAddress, SIZE, oldProtect, &myProtect);
    OutputDebugStringA("do Something!!");
    printf("MyRecv>>%s\n",buf);
    return retValue;
}
  • BeginRedirect
void BeginRedirect(LPVOID newFunction)
{
    BYTE tempJMP[SIZE] = { 0xE9, 0x90, 0x90, 0x90, 0x90, 0xC3 };
    memcpy(JMP, tempJMP, SIZE);
    DWORD JMPSize = ((DWORD)newFunction - (DWORD)pOrigAddress - 5);
    VirtualProtect((LPVOID)pOrigAddress, SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy(oldBytes, pOrigAddress, SIZE);
    memcpy(&JMP[1], &JMPSize, 4);
    memcpy(pOrigAddress, JMP, SIZE);
    VirtualProtect((LPVOID)pOrigAddress, SIZE, oldProtect, &myProtect);
}

ps:
1.跳转指令的位置不一定必须在开始,也可以在别处.
2.跳转指令不一定要使用jmp,其他和jmp等价的指令(sub,push等)都可.
3.跳转完毕后要恢复opcode否则会无限递归.

三、Dll的注入利用

1.DLL注入即让程序主动调用.dll文件在DLL PROCESS_ATTACH时时就会进行HOOK操作.
2.项目源码:https://github.com/Monkey1350/Hook_ws2_32.dll

四、总结

对于Hook的使用环境和方式还是可以有很多变化的.
环境
1.函数:是函数都可以HOOK?
1.1 C++类的虚函数.
1.2 程序自定义的函数.
1.3 回调函数:线程回调、消息回调等.
1.4 用户层的其他DLL文件里面的函数.
1.5 内核层的SSDT/ShadowSSDT的函数.
......
2.保护:反Hook的保护?
2.1 程序自身的保护.
2.2 应用层的保护.
2.3 内核层的保护.
......
3.平台:不同操作系统或硬件平台的Hook?
3.1 windows.
3.2 linux.
3.3 Unix.
3.4 Mips.
3.5 Mac.
3.6 Ios.
3.7 Android.
4.语言:不同语言编写的函数Hook?
4.1 编译型:C/C++/Java/C#.
4.2 解释型:Python/PHP/Js.
......

 

形式
1.脱离DLL文操作,以二进制形式进行挂钩.
2.静态挂钩,来源修改或替换(IAT挂钩,DLL劫持).
3.框架:frida/Xposed/detours.
......

 

Hook的使用可以从windows系统的应用层到内核层进行纵向拓展,再从windows到linux(android)等其他不同系统或是平台进行横向对比,中途还会碰到程序或是系统的保护和崩溃,时不时的还可以去了解下流行的Hook框架原理,不得不说想要完全理解以Hook为原点而扩展的脑图里的所有知识点还是有点意思的.
(///^_^....... 哈?)



[推荐]看雪企服平台,提供安全分析、定制项目开发、APP等级保护、渗透测试等安全服务!

最后于 2019-1-31 20:40 被Weaving编辑 ,原因:
上一主题 下一主题
最新回复 (7)
windxiang 2019-2-1 11:58
2
0
下载源码来研究。多谢
xie风腾 2019-2-1 13:10
3
0

学习了,多谢楼主分享
五天 2019-2-1 14:17
4
0
谢谢分享
IamHuskar 4 2019-2-1 14:47
5
0
单线程是没问题 ,对于多线程 这些函数调用非常频繁,执行HOOK,以及恢复的时候,利用memcpy修改的是5个字节,可能别的线程此时刚好调用recv,memcpy尚未完成,导致指令错误崩溃。 有很多方式解决 一种是利用 Interlock系列函数修改8字节,这样可能会导致错过一些调用。可以在另外的地方申请内存写入调用头,采取二次跳转,参考detours,还可以IAT EAT HOOK解决!!
最后于 2019-2-1 14:48 被IamHuskar编辑 ,原因:
理想小青年 2019-2-1 16:51
6
0
http://blog.51cto.com/13352079/2347228
niuzuoquan 2019-2-2 14:13
7
0
mark
开花的水管 2019-2-3 09:41
8
0
mark
游客
登录 | 注册 方可回帖
返回