[源码下载]

介绍

在Code Project网站有许多关于password spy的文章,但是这些都是基于Windows Hooks的,还有没有其他方式能实现这种效果呢?是的,有,不过,先让我们简要的回顾一下。

要想读控件的内容,一般需要给它发送WM_GETTEXT 消息.edit控件就有这个特点。如果edit控件是在别的进程里,而且被设置成ES_PASSWORD类型,那么我们想要达到的目的就不会成功。这种情况下只有拥有password控件的进程才能实现WM_GETTEXT,所以我们的问题集中在了这一点上:

怎样才能在外部的进程空间内执行::SendMessage( hPwdEdit, WM_GETTEXT, nMaxChars, psBuffer );

通常有三种方式解决这个问题:

1.把代码写到Dll内;然后,通过windows hooks将Dll映射到远程进程。

2.把代码写道Dll内;然后,通过CreateRemoteThread & LoadLibrary 技术将Dll映射到远程进程。

3.不单独写一个DLL;通过WriteProcessMemory将你的代码直接拷贝到远程进程。

I.Windows Hooks

Windows hools的主要用途是监控一些线程的消息。

1.Local hooks,监控属于你的进程的所有线程消息。

2.Remote hooks:

a.thread-specific,监控属于其他的进程的线程消息。

b.system-width,监控当前运行在系统中的所有线程。

如果hook的线程属于别的进程,你的hook程序必须放置在Dll内。然后系统将包含hook程序的dll映射到线程的地址空间,Windows映射全部的Dll,不仅仅是hook程序,这就是为什么Windows hooks可以被用来将代码注入到其他进程的地址空间中去。

这篇文章里,我不去深入讨论hooks(可以在MSDN内详细的看一下SetWindowHookEx API函数)我还会给你两个有用的线索,这在文档里是找不到的。

1.在调用SetWindowHookEx成功后,系统自动将Dll映射到了线程的地址空间,但不会立即执行。因为Windows hooks全部是基于消息,直到正确的事件返回Dll才被真正映射。例如:

如果你安装了一个Hook用来监控一些线程的所有消息队列(WH_CALLWNDPROC),在消息被发送到线程之前DLL不会被映射到进程的地址空间。如果在消息发送到线程之前,UnhoolWindowsHoolEx被调用,那么Dll永远都不会被映射到进程(尽管调用SetWondosHookEx成功了)。要想立即执行,在SetWondosHookEx成功之后立即给线程发送一个合适的event。

在调用UnhoolWindowsHoolEx取消Dll映射的时候,也不会立即取消DLL映射。

2.当你安装Hooks的时候,他们会影响系统的总性能(尤其是system-wide hooks).如果你将thread-specific hooks单独作为一个Dll映射机制,那么你很容易就避免这个缺点,看下面的代码片断:

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD  ul_reason_for_call,
LPVOID lpReserved )
{
if( ul_reason_for_call == DLL_PROCESS_ATTACH )
{
// Increase reference count via LoadLibrary
        char lib_name[MAX_PATH];
::GetModuleFileName( hModule, lib_name, MAX_PATH );
::LoadLibrary( lib_name );
// Safely remove hook
        ::UnhookWindowsHookEx( g_hHook );
}
return TRUE;
}

那么,发生了什么了?首先我们通过Windows hooks将Dll映射到远程进程。然后,在Dll被真正的映射之后,我们Unhook它,正常情况下,Dll将要被取消映射,同时,第一个到达被hook的线程的消息也被激活, 我们可以通过LoadLibrary增加Dll的引用计数来阻止取消映射。

随之而来的问题是:现在怎么去卸载Dll,一旦结速了,UnhookWindowsHookEx不会做这些事情的,因为我们已经unhook了线程,可以这样去做:

1.在你想要取消Dll映射之前,安装另一个Hook。

2.发送一个特殊的消息到远程线程

3.在你的hook程序中,捕获消息,在回复消息中, 调用FreeLibrary & UnhookWindowsHookEx

现在,hooks只在映射/取消映射的时候使用, 在这期间不会对"hooked"线程产生影响。 在第二部分中我们还会讨论比LoadLibrary更好的机制来使Dll映射不影响目标进程。然而,相对于LoadLibrary技术,这种解决方式只能在Winnt和Win9x下运行。

但是,什么时候才使用这些窍门呢?当Dll不得不被放到远程进程中很长时间时(例如,你将一个控件变成另一个进程的子类),你可能想尽量小的影响目标进程。我不在HookSpy中使用它,因为此时Dll只会注入片刻。

II.CreateRemoteThread & LoadLibrary技术

通常,所有的进程都能通过LoadLibrary动态的导入Dll。但是,怎么才能让外部进程调用这个函数?回答是用CreateRemoteThread.

首先,让我们看一下LoadLibrary and FreeLibrary APIs 的声明:

HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName   // address of filename of library module
);
BOOL FreeLibrary(
HMODULE hLibModule      // handle to loaded library module
);

现在,跟ThreadProc(线程程序,用来传给CreateRemoteThread)比较一下:

DWORD WINAPI ThreadProc(
  LPVOID lpParameter   // thread data
);

正如你所看到的,所有的函数都使用同样的调用转换,都接受一个32位的参数。返回值的大小也是一样的。换句话说,我们可以将LoadLibrary/FreeLibrary 作为线程函数传递给CreateRemoteThread.

然而,还有两个问题:

1.CreateRemoteThread 里的lpStartAddress参数必须是远程进程的线程程序的首地址。

2.如果lpParameter(传递给ThreadFunc)被认为是一个普通的32位值,(FreeLibrary 认为它是一个HMODULE),所有事情就好说了,然而,如果lpParameter 被描述成一个指针,(LoadLibraryA 将它描述成一个指向字符串的指针),那么它就必须指向远程进程的一些数据。

第二个问题好解决,用WriteProcessMemory拷贝Dll的模块名(需要LoadLibrary)。

下面是使用CreateRemoteThread & LoadLibrary 技术的步骤:

1.得到远程进程的一个句柄(OpenPreocess).

2.在远程进程里给Dll名称分配内存(VirtualAllocEx).

3.将Dll名称,包括全路径,写到分配的内存里(WritePreocessMemory).

4.通过CreateRemoteThread & LoadLibrary将你的Dll映射到远程进程.

5.等待远程进程结束(WaitForSingleObject);也就是等到LoadLibrary返回.当我们的DllMain返回的时候线程就结束了(调用原因DLL_PROCESS_ATTACH).

6.返回远程线程的结束码,(GetExitCodeThread).注意这个值是LoadLibrary的返回值,

7.释放在步骤#2分配的内存(VirtualFreeEx).

8.从远程进程卸载DLL,将#6得到的返回句柄HMODULE传递给FreeLibrary(lpParameter in CreateRemoteThread).

  注意:如果你将Dll注入任何新的线程,确保在卸载的时候全部结束。

9.等待线程结束(WaitForSingleObject).

而且,一旦完成,别忘了关闭所有的句柄,对于在#4和#8创建的线程和在步骤#1返回的远程进程的句柄。

HANDLE hThread;
char    szLibPath[_MAX_PATH];  // The name of our "LibSpy.dll" module
                               // (including full path!);
void*   pLibRemote;   // The address (in the remote process) where
                      // szLibPath will be copied to;
DWORD   hLibModule;   // Base address of loaded module (==HMODULE);
HMODULE hKernel32 = ::GetModuleHandle("Kernel32");
// initialize szLibPath
//...

// 1. Allocate memory in the remote process for szLibPath
// 2. Write szLibPath to the allocated memory
pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath),
MEM_COMMIT, PAGE_READWRITE );
::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath,
sizeof(szLibPath), NULL );
// Load "LibSpy.dll" into the remote process
// (via CreateRemoteThread & LoadLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,
"LoadLibraryA" ),
pLibRemote, 0, NULL );
::WaitForSingleObject( hThread, INFINITE );
// Get handle of the loaded module
::GetExitCodeThread( hThread, &hLibModule );
// Clean up
::CloseHandle( hThread );
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );

III.The CreateRemoteThread & WriteProcessMemory 技术

   这种方法是将代码拷贝到要注入进程的地址空间,然后在进程的上下文中执行这些代码,包括远程线程的使用。这就不必再使用分离的DLL了。可以用API函数WriteProcessMemory将代码直接拷贝到远程地址空间,用CreateRemoteThread启动代码执行。   

   先看一个CreateRemoteThread的声明       

HANDLE CreateRemoteThread(
HANDLE hProcess,        // handle to process to create thread in
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // pointer to security
                                             // attributes
  DWORD dwStackSize,      // initial thread stack size, in bytes
  LPTHREAD_START_ROUTINE lpStartAddress,     // pointer to thread
                                             // function
  LPVOID lpParameter,     // argument for new thread
  DWORD dwCreationFlags,  // creation flags
  LPDWORD lpThreadId      // pointer to returned thread identifier
);

   用它和CreateThread的声明相比,有下面几点不同:

  • hProcess参数在CreateRemoteThread内是多加的。它是创建线程的进程句柄。
  • lpStartAddress参数是CreateRemoteThread 内描述远程地址空间内的线程的起始地址。函数必须存在于远程空间内。我们首先必须将代码拷贝到远程空间。
  • 同样的, lpParameter指针必须存在于远程空间, 所以我们也要将它拷贝到那儿。  

    现在我们将这种技术总结为如下几步:

    1.取得远程空间的句柄(OpenProcess).

    2.在远程地址空间为注入的数据分配内存(VirtualAllocEx)

    3.将自定义结构INJDATA拷贝到分配的内存里(WriteProcessMemory)

    4.在远程地址空间为注入的代码分配内存。

    5.将ThreadFunc拷贝到分配的内存里。

    6.通过CreateRemoteThread启动远程的ThreadFunc。

    7.等待远程线程的结束(WaitForSingleObject). 

    8.从远程进程返回结果(ReadProcessMemory or GetExitCodeThread). 

    9.释放在步骤2#和步骤#4分配的内存(VirtualFreeEx);

    10.关闭在步骤#1和步骤#6返回的句柄(CloseHandle).

   ThreadFunc必须服从下面的要求:

    1.ThreadFunc 只能调用kernel32.dll 和 user32.dll内部的函数,只有kernel32.dll 和 user32.dll(注意user32没有被映射的每一个win32进程)能保证本地和目标进程有同样的加载地址。如果你需要其他库中的函数,将LoadLibrary and GetProcAddress的地址传入注入代码,如果有其他有争议的DLL已经被映射到了进程空间你也可以用GetModuleHandle 代替of LoadLibrary,

    同样的,如果你想从ThreadFunc内调用你自己的子程序。可以将地址先通过INJDATA传入到ThreadFunc,再经过ThreadFunc加载到远程空间。

    2.不要使用任何静态字符串,最好将所有的字符串通过INJDATA传递给ThreadFunc.

      为什么呢?编译器将所有的静态字符串放入到了可执行文件的".data"段,并且只是在代码内引用。这样的话,远程空间中的ThreadFunc的拷贝将指向一些不存在的地址,(最少不在它的地址空间内).

    3.将/GZ编译开关关掉,在debug里它是默认的。

    4.要不将ThreadFunc and AfterThreadFunc设为静态的,要不取消增量链接。

    5.在ThreadFunc内本地变量必须小于page-worth(4kb)。注意在debug中有效的4kb中的大约10字节是内部变量使用的。

    6.如果switch块大于3个case,就要把它象下面这样分开。

      或者改用if-else if

switch( expression ) {
    case constant1: statement1; goto END;
    case constant2: statement2; goto END;
    case constant3: statement2; goto END;
}
switch( expression ) {
    case constant4: statement4; goto END;
    case constant5: statement5; goto END;
    case constant6: statement6; goto END;
}
END:

如果不使用这些规则,很可能使目标进程崩溃,记住,不要将目标进程的空间当成你自己进程的空间。

[源址]http://www.codeproject.com/KB/threads/winspy.aspx

// Unload "LibSpy.dll" from the target process // (via CreateRemoteThread & FreeLibrary) hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,
"FreeLibrary" ),
(void*)hLibModule, 0, NULL );
::WaitForSingleObject( hThread, INFINITE );
// Clean up ::CloseHandle( hThread );

将代码注入到进程的三种方式相关推荐

  1. python点击按钮创建进程_python-创建进程的三种方式

    1,os.fork() 方法 import os ret = os.fork() if ret == 0: #子进程 print("Sub process pid = %d, Sub pro ...

  2. 用代码实现标签打印的三种方式

    最近项目中要实现标签打印的功能,有几个条件 标签模板可以事先生成,用的是CodeSoft软件 标签模板里面有二维码 标签模板里面有一些变量,要求打印的时候自动填充 产线电脑上没有安装CodeSoft, ...

  3. SpringBoot中注入ApplicationContext对象的三种方式

    在项目中,我们可能需要手动获取spring中的bean对象,这时就需要通过 ApplicationContext 去操作一波了! 1.直接注入(Autowired) @Component public ...

  4. 后台长期运行进程的三种方式

    入门: nohup command > /var/log/test.log 2>&1 & 优雅: screen方式,通过screen 命令创建的环境下运行的终端命令,其父进 ...

  5. Spring依赖注入的三种方式(好的 坏的和丑的)

    关于spring bean三种注入方式的优缺点对比,翻译自Spring DI Patterns: The Good, The Bad, and The Ugly,水平有限,如有错误请指正. Sprin ...

  6. 依赖注入的三种方式_ASP.NET Core技术研究-探秘依赖注入框架

    ASP.NET Core在底层内置了一个依赖注入框架,通过依赖注入的方式注册服务.提供服务.依赖注入不仅服务于ASP.NET Core自身,同时也是应用程序的服务提供者. 毫不夸张的说,ASP.NET ...

  7. 直接将自身代码注入傀儡进程

    EXE注入-傀儡进程 2008-08-16 16:38 直接将自身代码注入傀儡进程,不需要DLL.首先用CreateProcess来创建一个挂起的IE进程,创建时候就把它挂起.然后得到它的装载基址,使 ...

  8. 依赖注入的三种方式_一起学Spring之三种注入方式及集合类型注入

    本文主要讲解Spring开发中三种不同的注入方式,以及集合数据类型的注入,仅供学习分享使用,如有不足之处,还请指正. 概述 Spring的注入方式一共有三种,如下所示: 通过set属性进行注入,即通过 ...

  9. Spring注解依赖注入的三种方式的优缺点以及优先选择

    当我们在使用依赖注入的时候,通常有三种方式: 1.通过构造器来注入: 2.通过setter方法来注入: 3.通过filed变量来注入: 那么他们有什么区别吗?应该选择哪种方式更好? 代码示例: Con ...

最新文章

  1. hdu 5155(DP+排列组合)
  2. 保研计算机辅助翻译,本科2013级推免生寄语
  3. 边缘计算应用场景_云计算与边缘计算协同九大应用场景(2019年)发布(附PPT解读)...
  4. vue全局引入openlayers_vue中使用OpenLayers(一):引入谷歌地图
  5. 脚本化HTTP 取得响应 指定请求
  6. 这篇文章,专治MQ中间件各种疑难杂症
  7. Android功耗(8)---Camera功耗分析和拆解
  8. Java虚拟机的什么周期
  9. ucc编译器(语义分析)
  10. pip 安装GDAL
  11. nlp项目:搭建一个简单的问答系统
  12. jdbc连接数据库代码
  13. Masonry 布局 cell 高度适应的一种方案(实现类似朋友圈简单布局)
  14. 最新 iOS13 苹果登录
  15. 反转链表-就地逆置法
  16. 红米note4出厂系统版本_红米Note4官方出厂rom系统刷机包_升级包降级包回退包下载...
  17. Python实现京东价格监控
  18. 边缘保留滤波(EPF)
  19. 如何抓住风口,利用互联网赚钱?(內含三大商业模式推荐)建议收藏
  20. 常见网络端口_您应该知道的14个常见网络端口

热门文章

  1. 温州首个元宇宙创新中心正式开园,助推温企数字化转型
  2. 《应用商务统计分析》第六章 泊松回归
  3. Scrapy Engine
  4. tensorflow2.0使用GELU激活函数
  5. HR人才测评,什么是协调能力?如何提高协调能力?
  6. 大模型时代,腾讯云“复制”腾讯|WAIC2023
  7. 一切都是命中注定的!
  8. Python 小数位保留
  9. python消费datahub_datahub消费数据
  10. MATLAB读取Argo数据【5】--Argo数据的提取和画图分析(2)(双x轴作图)