接触DLL编写并实现线程注入和全局钩子

 资料"<<windows核心编程>> 
你不会C,那先找本C的书...如果想要电子书的可以回帖留下你的Email,有空发给你,一些 

自己珍藏的书籍 
还有你应有VC++6.0和MSDN 
好了,开始吧 

目录 
0一点建议 

1DLL基本知识 

2编写我们的DLL 

3编写加载我们DLL的Loader,实现一个类似RUNDLL32.EXE的程序 

4线程注入之Loader编写 

5全局钩子之实现 


0一点建议 
书不是重头看到尾,有些书只是用来参考的..所以不要什么书都看...把基础打好,就可以 

深入去探讨某些问题..不懂的就找资料..查,总结收获,这样学到的知识才是你的 
这文章很多代码没怎么注释,这样文章感觉会比较长,而且很多解释MSDN和<<windows核心 

编程>>讲的很好了,就不再重复.,而且讲这些东西很累比如说什么DllMain()和哪个API.. 

这些别人说的已经足够好了,如果不懂应该先去把基础打好.好了费话到此.. 


1 DLL基本知识 
DLLs,函数储存在一个独立的动态链接库文件中。在创建Windows程序时,链接过程并不 

把DLLs文件链接到程序上。直到程  
序运行并调用一个DLLs中的函数时,该程序才要求这个函数的地址。此时Windows才在 

DLLs中寻找被调用函数,并把它的地址传送给调用程序。采用这种方法,DLLs达到了复用 

代码的极限。  
DLLs不仅提供了函数重用的机制,而且提供了数据共享的机制。任何应用程序都可以共享 

由装入内存的DLLs管理的内存资源块。只包含共享数据的DLLs称为资源文件。如Windows 

的字体文件等 
2 编写我们的DLL 
上次在吧里已经有发一个帖说明怎么编写DLL了http://tieba.baidu.com/f?kz=304645377,,不过那是很基本的..现在我们重新再写一个吧...里面有俩个函数,一个是 

修改系统时间的..,一个是输出你电脑用户名的...就是前些时候我写的哪个 
好了开始我们的DLL编写,让它导出俩个函数 
VC++6.0打开->文件->新建->工程->win32 动态连接库-> 工程名我写了sysnap 
然后一个空的DLL工程,完成 
接下来就是文件->新建->c++ source 
开始写代码 
#include <windows.h> 


_declspec(dllexport)   void SetTime()  
{  
 WORD wYear; 
 SYSTEMTIME time;  
  
 printf("想改到哪年:"); 
 scanf("%d",&wYear); 
  
 GetSystemTime(&time);  
 time.wYear = wYear;  
 if (!SetSystemTime(&time))  
 printf("修改错误");  
}  

_declspec(dllexport) void sysnap()  
{   
const int nBufSize = MAX_COMPUTERNAME_LENGTH + 1;    

 DWORD nSize = nBufSize;   

 char name[nBufSize];   
    
 GetComputerName(name,&nSize);   
    
 MessageBox(NULL,name,TEXT("信息"), MB_ICONINFORMATION );   

}   
在 

好了,开始编译.得到了一个DLL,它导出了俩个函数名 
?sysnap@@YAXXZ?SetTime@@YAXXZ 
下面我们要用到的名字就是这俩个,,,呵呵,是不是感觉很难记,有没有可以直接导出 

sysnap这样的名字,有//...在VC编写前可以自己弄,这里我就不说(其实我再弄的时候没考 

虑到导出名,呵呵..不过已经编译好了.算了) 

3编写加载我们DLL的Loader,实现一个类似RUNDLL32.EXE的程序 
好了,我们的DLL已经编写好了,现在就是运行它了,当然不可能双击运行,我们需要编写一 

个加载这个DLL的Loader 
依然是打开VC,编写控制台程序,我命名为sysnap..所以编译后得到sysnap.exe 
代码如下  

#include <windows.h> 
#include <stdio.h> 
void main() 

typedef int (*ADDPROC)(); 
HINSTANCE hInst; 

char funName[30]; 
char dllName[30]; 
printf("______献给黑吧______"); 
printf("想使用哪个DLL中的函数:\n"); 
scanf("%s",dllName); 
hInst=LoadLibrary(dllName); 
if(hInst) 

printf("%s加载成功",dllName); 

else 

return; 

printf("想使用%s中的哪个函数\n",dllName); 
scanf("%s",funName); 

ADDPROC HOOK=(ADDPROC)GetProcAddress(hInst,funName); 
if(!HOOK) 

printf("失败!"); 
return; 

else 
{ //hwnd=GetConsoleHwnd(); 
HOOK(); 
 printf("成功!"); 
int a; 
scanf("%d",&a); 

编译它,得到一个EXE程序,把我们的sysnap.dll放到跟它同一个目录下,这样可以方便快速 找个我们的dll,现在可以按照我们这个程序运行我们的DLL了,依次输入sysnap.dll,?  sysnap@@YAXXZ看到没,弹出一个窗口,里面显示了我们的电脑用户名..你也可以运行第二  个函数,sysnap.dll....?SetTime@@YAXXZ...因为开始没考虑到导出名字问题,所以写到这就算了...如果想导出sysnap和SetTime这样的函数名是可以的,怎么弄你自己上网查下本来还想写是弄成rundll32.exe形式的,不过没什么意义就算了,,其实也很简单...就是用main()的俩个参数argv和argc..有兴趣可以自己实现 4线程注入之Loader编写

大家知道,上面的sysnap.exe是来加载我们的DLL..那就是说如果把进程sysnap.exe关掉, 那DLL也被卸载,那有没有办法让我们的loader运行一次,然后我们的dll就一直运行而无论 sysnap.exe进程是否被关掉,有..那就是线程注入,把我们的dll注入到别的进程...接下来 

我们就是来实现这样一个loader..测试的dll的代码在前几天我的章里有 
http://tieba.baidu.com/f?kz=304645377这个DLL是有DLLMain()的..详细参考<<windows 核心编程>>好了... 
主要思想是根据我们要注入的进程名得到它的ID,然后是提权,最后就是注入 
代码如下..可以看下<<windows核心编程和MSDN>> 
#include <windows.h>  
#include <stdio.h> 
#include <Tlhelp32.h> 

BOOL InjectDll(const char *DllFullPath, const DWORD dwRemoteProcessId); 
int Hightpriv(const char * name) ; 
DWORD GetProcessIdByName(LPCTSTR name); 

int main() 
{  
 char strname[30]; 
 char dllname[30]; 
 int proID; 
 printf("想注入哪个进程,比如explorer.exe,请注意大小写,:"); 
 scanf("%s",strname); 
 printf("想把哪个dll注入,请填好完整路劲名:"); 
 scanf("%s",dllname); 
 proID=GetProcessIdByName(strname); 
  
 InjectDll(dllname,proID) ; 
 return 0;  
}  


//开始我们的工作了,<<windows核心编程里讲的比较细>> 
BOOL InjectDll(const char *DllFullPath, const DWORD dwRemoteProcessId)  
{  
 HANDLE hRemoteProcess;  
 Hightpriv(SE_DEBUG_NAME);  
 char *pszLibFileRemote; 
  
hRemoteProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId); 

pszLibFileRemote = (char *) VirtualAllocEx( hRemoteProcess, NULL, lstrlen 

(DllFullPath)+1, MEM_COMMIT, PAGE_READWRITE);  
  
WriteProcessMemory(hRemoteProcess,pszLibFileRemote, (void *) DllFullPath,  

lstrlen(DllFullPath)+1, NULL);  

PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress 

(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");  
  
 HANDLE hRemoteThread;  
 if( (hRemoteThread = CreateRemoteThread(
hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL) ) == NULL)  
 {  
 printf("注入线程失败!");  
 return FALSE;  
 }  

  
 CloseHandle(hRemoteProcess); 
 CloseHandle(hRemoteThread); 
 return TRUE;  
}  

//提高我们程序的权限,因为我们要操作的是系统中的其他进程,如果没有足够的系统权限,我们是无法写入甚至连读取其它进程的内存地址的 
int Hightpriv(const char * name)  
{  
 HANDLE hToken;  
 TOKEN_PRIVILEGES tp;  
 LUID luid;  
  
 OpenProcessToken(GetCurrentProcess(),  
 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,  
 &hToken);  
  
 LookupPrivilegeValue(NULL,name,&luid) ; 
  
 tp.PrivilegeCount = 1;  
 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  
 tp.Privileges[0].Luid = luid;  
  
 AdjustTokenPrivileges(hToken,0,&tp,sizeof 

(TOKEN_PRIVILEGES),NULL,NULL);  
 return 0;  
}  

//主要是枚举进程名,与我们想要注入的做比较,如果是就返回它的ID,ROCESSENTRY32是一 

个与进程有关的数据结构 
DWORD GetProcessIdByName(LPCTSTR name) 

 PROCESSENTRY32 prostruct; 
 DWORD id = 0; 
 HANDLE hSnapshot; 
  
 hSnapshot= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); 
 prostruct.dwSize = sizeof(PROCESSENTRY32); 
 if(!Process32First(hSnapshot,&prostruct)) 
 return 0; 
 do 
 { 
 prostruct.dwSize = sizeof(PROCESSENTRY32); 
 if(!Process32Next(hSnapshot,&prostruct)) 
 break; 
 if(strcmp(prostruct.szExeFile,name) == 0) 
 { 
 id = prostruct.th32ProcessID; 
 break; 
 } 
 }while(TRUE); 
 CloseHandle(hSnapshot); 
 return id; 

编译后得到了一个EXE文件,现在我们把我们的DLL注入到notepad.exe..这个dll的代码可以在http://tieba.baidu.com/f?kz=304645377的到,自己编译一下.. 
好了,线程注入就这么完成


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/carl2380/archive/2009/11/13/4805606.aspx

原文地址:https://www.cnblogs.com/carl2380/p/2093327.html