dll注入浏览器标签进程

varding 2010-03-17 03:20:22
呵呵,本人对dll注入了解到很少,不知道标题这样合适不


我做的一个项目需要监测机器上浏览器的网络通讯数据
x86系统下用CreateRemoteThread的方法把代码注入到目标进程,在新线程里把recv,send替换成自己的函数就行,但在x64下CreateRemoteThread似乎已经不能用了

(主要是x64平台下的x86浏览器,x64浏览器暂时可以不考虑)
我想用dll注入的方法,通过setWindowHookEx注入自己的dll,在自己的dll中hook recv,send

我对dll注入的方法不熟悉,最近在网上找了些例子自己看着

用setWindowHookEx的方法我已经能注入到ie8的UI线程中去

但这个UI线程和标签页不是一个进程(一个标签一个进程,UI属于独立的另一个进程)

要注入每个标签的所在的进程有没有什么好的方法呢?

或者有没有其他的方法实现我的想法呢?

(因为我的目标不仅仅是ie8,所以不能用BHO)

我的级别低只能给100分,如果谁能提供好的方法我再开帖给分,谢谢!
...全文
472 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
cnzdgs 2010-03-19
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 varding 的回复:]
我是枚举所有的进程,然后判断名字是iexplore.exe之类的浏览器就注入。你的意思是用全局钩子然后在dll main里判断要不要hook api?这个和我的办法有区别吗?

还有我昨天搞错了,一直把进程id传给SetWindowHookEx函数了,后来直接用spy查看ie标签的句柄再得到主线程id就可以SetWindowHookEx了。在知道进程id的情况下有没有什么办法得到他的主线程id呢?(除了EnumWindow)
[/Quote]
我所提的方法是用SetWindowHookEx全局Hook,不是Hook某一个线程。在DLL被注入时再判断是否要Hook API。
枚举进程是一次性动作,SetWindowHookEx是自动注入的。IE8浏览器只要开一个选项卡就会创建一个新进程,所以应该用后者。另外,我这样回复也是因为你在帖子中提到了SetWindowHookEx。
可以用Thread32First、Thread32Next枚举所有线程,但主线程只是编程中的概念,系统中无法区分哪个线程是主线程。全局Hook不需要线程ID。
dibotiger 2010-03-18
  • 打赏
  • 举报
回复
先回答你第一个问题:
x86系统下用CreateRemoteThread的方法把代码注入到目标进程,在新线程里把recv,send替换成自己的函数就行,但在x64下CreateRemoteThread似乎已经不能用了

32位的DLL是不能被64位的进程加载的。64位的进程只能加载64位的DLL。

但是64位的系统通过SYSWOW64机制,兼容了32位的进程,使得32位的程序可以运行在64位系统下。


如果你想你的DLL能正确注入IE,试试X64位系统上地x:\programe files(x86)\internet iexplorer\
下的那个IE,那个IE才是32位的。


第二个问题,看代码,转的:

#include "stdafx.h"
#include "windows.h"
#include "winnt.h"

PVOID pNtDeviceIoControl = NULL ;
//

#define AFD_RECV 0x12017

#define AFD_SEND 0x1201f


typedef struct AFD_WSABUF{
UINT len ;
PCHAR buf ;
}AFD_WSABUF , *PAFD_WSABUF;

typedef struct AFD_INFO {
PAFD_WSABUF BufferArray ;
ULONG BufferCount ;
ULONG AfdFlags ;
ULONG TdiFlags ;
} AFD_INFO, *PAFD_INFO;
typedef LONG NTSTATUS;

#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

const CHAR GetXX[] = "GET ";
const CHAR PostXX[] = "POST ";
const CHAR HttpXX[] = "HTTP";
//////////////////////////////////////////////////////////////////////////
//
// LookupSendPacket
// 检查Send包
// 目前实现了过滤HTTP请求(GET AND POST)
//
//////////////////////////////////////////////////////////////////////////

BOOL LookupSendPacket(PVOID Buffer , ULONG Len)
{
if (Len < 5)
{
return FALSE ;
}

//外层已有异常捕获

if (memcmp(Buffer , GetXX , 4) == 0
||
memcmp(Buffer , PostXX , 5) == 0 )
{
return TRUE ;
}
return FALSE ;
}
//////////////////////////////////////////////////////////////////////////
//
// LookupRecvPacket
//
// 检查Recv包
// 在这里可以实现Recv包查字典功能
// 目前实现了过滤HTTP返回数据包的功能
//
//
///////////////////////////////////////////////////////////////////////////
BOOL LookupRecvPacket(PVOID Buffer , ULONG Len)
{
if (Len < 4)
{
return FALSE ;
}

if (memcmp(Buffer , HttpXX , 4) == 0 )
{
return TRUE ;
}

return FALSE ;
}
//hook函数

//////////////////////////////////////////////////////////////////////////
//
// NtDeviceIoControlFile的HOOK函数
// ws2_32.dll的send , recv最终会调用到mswsock.dll内的数据发送函数
// mswsock.dll会调用NtDeviceIoControlFile向TDI Client驱动发送Send Recv指令
// 我们在这里做拦截,可以过滤所有的TCP 收发包(UDP之类亦可,不过要更改指令)
//
//////////////////////////////////////////////////////////////////////////

NTSTATUS __stdcall NewNtDeviceIoControlFile(
HANDLE FileHandle,
HANDLE Event OPTIONAL,
PVOID ApcRoutine OPTIONAL,
PVOID ApcContext OPTIONAL,
PVOID IoStatusBlock,
ULONG IoControlCode,
PVOID InputBuffer OPTIONAL,
ULONG InputBufferLength,
PVOID OutputBuffer OPTIONAL,
ULONG OutputBufferLength
)
{

//先调用原始函数

LONG stat ;
__asm
{
push OutputBufferLength
push OutputBuffer
push InputBufferLength
push InputBuffer
push IoControlCode
push IoStatusBlock
push ApcContext
push ApcRoutine
push Event
push FileHandle
call pNtDeviceIoControl
mov stat ,eax
}

//如果原始函数失败了(例如RECV无数据)

if (!NT_SUCCESS(stat))
{
return stat ;
}

//检查是否为TCP收发指令

if (IoControlCode != AFD_SEND && IoControlCode != AFD_RECV)
{
return stat ;
}


//访问AFD INFO结构,获得SEND或RECV的BUFFER信息
//这里可能是有问题的BUFFER,因此我们要加TRY EXCEPT
//

__try
{
//从InputBuffer得到Buffer和Len

PAFD_INFO AfdInfo = (PAFD_INFO)InputBuffer ;
PVOID Buffer = AfdInfo->BufferArray->buf ;
ULONG Len = AfdInfo->BufferArray->len;

if (IoControlCode == AFD_SEND)
{
if (LookupSendPacket(Buffer , Len))
{
//输出包内容
//这里输出调试信息,可以用DbgView查看,如果有UI可以做成SendMessage形式~
OutputDebugString("SendPacket!\n");
OutputDebugString((char*)Buffer);
}
}
else
{
if (LookupRecvPacket(Buffer , Len))
{
OutputDebugString("RecvPacket!\n");
OutputDebugString((char*)Buffer);
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return stat ;
}

return stat ;






}


//////////////////////////////////////////////////////////////////////////
//
// Hook mswsock.dll导出表的Ntdll!NtDeviceIoControlFile
// 并过滤其对TDI Cilent的请求来过滤封包
// 稳定,隐蔽,RING3下最底层的包过滤~
//
//////////////////////////////////////////////////////////////////////////
void SuperHookDeviceIoControl()
{
//得到ws2_32.dll的模块基址
HMODULE hMod = LoadLibrary("mswsock.dll");
if (hMod == 0 )
{
return ;
}

//得到DOS头

PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMod ;

//如果DOS头无效
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return ;
}

//得到NT头

PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG)hMod + pDosHeader->e_lfanew);

//如果NT头无效
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
return ;
}

//检查输入表数据目录是否存在
if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0 ||
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0 )
{
return ;
}
//得到输入表描述指针

PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG)hMod + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

PIMAGE_THUNK_DATA ThunkData ;

//检查每个输入项
while(ImportDescriptor->FirstThunk)
{
//检查输入表项是否为ntdll.dll

char* dllname = (char*)((ULONG)hMod + ImportDescriptor->Name);

//如果不是,则跳到下一个处理

if (stricmp(dllname , "ntdll.dll") !=0)
{
ImportDescriptor ++ ;
continue;
}

ThunkData = (PIMAGE_THUNK_DATA)((ULONG)hMod + ImportDescriptor->OriginalFirstThunk);

int no = 1;
while(ThunkData->u1.Function)
{
//检查函数是否为NtDeviceIoControlFile

char* functionname = (char*)((ULONG)hMod + ThunkData->u1.AddressOfData + 2);
if (stricmp(functionname , "NtDeviceIoControlFile") == 0 )
{
//
//如果是,那么记录原始函数地址
//HOOK我们的函数地址
//
ULONG myaddr = (ULONG)NewNtDeviceIoControlFile;
ULONG btw ;
PDWORD lpAddr = (DWORD *)((ULONG)hMod + (DWORD)ImportDescriptor->FirstThunk) +(no-1);
pNtDeviceIoControl = (PVOID)(*(ULONG*)lpAddr) ;
WriteProcessMemory(GetCurrentProcess() , lpAddr , &myaddr , sizeof(ULONG), &btw );
return ;

}

no++;
ThunkData ++;
}
ImportDescriptor ++;
}
return ;
}

//////////////////////////////////////////////////////////////////////////
//
// CheckProcess 检查是否是需要挂钩的进程
//
//
//////////////////////////////////////////////////////////////////////////

BOOL CheckProcess()
{
//在此加入你的进程过滤
return TRUE ;
}

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
//当加载DLL时,进行API HOOK

if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
//检查是否是要过滤的进程
if (CheckProcess() == FALSE)
{
//如果不是,返回FALSE,将自身从进程中卸除
return FALSE ;
}

//HOOK API
SuperHookDeviceIoControl();
}
return TRUE;
}
varding 2010-03-18
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 cnzdgs 的回复:]

每个进程都注入。用SetWindowHookEx,在DllMain中用GetModuleFileName获取当前进程exe文件名称,判断出是ie8就Hook recv、send函数。
[/Quote]

我是枚举所有的进程,然后判断名字是iexplore.exe之类的浏览器就注入。你的意思是用全局钩子然后在dll main里判断要不要hook api?这个和我的办法有区别吗?

还有我昨天搞错了,一直把进程id传给SetWindowHookEx函数了,后来直接用spy查看ie标签的句柄再得到主线程id就可以SetWindowHookEx了。在知道进程id的情况下有没有什么办法得到他的主线程id呢?(除了EnumWindow)
vincent_1011 2010-03-18
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 miaolingshaohua 的回复:]

要想注入每个进程得一个一个来,这就是应用态的麻烦,我为了HOOK两个函数,最后还是选定用HOOK SSDT了,这样方便的多,而且技术也比较成熟!
[/Quote]
顶,带驱动。不好玩
哈利路亚1874 2010-03-18
  • 打赏
  • 举报
回复
要想注入每个进程得一个一个来,这就是应用态的麻烦,我为了HOOK两个函数,最后还是选定用HOOK SSDT了,这样方便的多,而且技术也比较成熟!
cnzdgs 2010-03-18
  • 打赏
  • 举报
回复
每个进程都注入。用SetWindowHookEx,在DllMain中用GetModuleFileName获取当前进程exe文件名称,判断出是ie8就Hook recv、send函数。
eajum 2010-03-17
  • 打赏
  • 举报
回复
帮楼主顶一下,我也在看DLL。还没有楼主了解的多呢,只知道个_declspec(dllexport)和_declspec(dllimport)
奥鹏南开作业21春学期(1709、1803、1809、1903、1909、2021、2021、2103)《计算机病毒分析》在线作业 注入shellcode属于()。 选项A:进程注入 选项B:DLL注入 选项C:钩子注入 选项D:直接注入 正确选项:D 能调试内核的调试器是() 选项A:OllyDbg 选项B:IDA Pro 选项C:WinDbg 选项D:Process Explorer 正确选项:C 进程浏览器的功能不包括()。 选项A:比较进程浏览器中的DLL列表与在Dependency Walker工具中显示的导入DLL列表来判断一个DLL是否被加载到进程 选项B:单击验证按钮,可以验证磁盘上的镜像文件是否具有微软的签名认证 选项C:比较运行前后两个注册表的快照,发现差异 选项D:一种快速确定一个文档是否恶意的方法,就是打开进程浏览器,然后打开文档。若文档启动了任意进程,你能进程浏览器中看到,并能通过属性窗口中的镜像来定位恶意代码在磁盘上的位置。 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第1页。正确选项:C 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第1页。 以下那个窗口是操作和分析二进制的主要位置,也是反汇编代码所在的地方 选项A:函数窗口 选项B:结构窗口 选项C:反汇编窗口 选项D:二进制窗口 正确选项:C Base64编码将二进制数据转化成()个字符的有限字符集。 选项A:16 选项B:32 选项C:48 选项D:64 正确选项:D 当代码库被链接时,宿主操作系统会在程序被装载时搜索所需的代码库,如果程序调用了被链接的库函数,这个函数会在代码库中执行,这种链接方法是()。 选项A:静态链接 选项B:动态链接 选项C:运行时链接 选项D:转移链接 正确选项:B OllyDbg的硬件断点最多能设置()个。 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第2页。选项A:3个 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第2页。 选项B:4个 选项C:5个 选项D:6个 正确选项:B Shell是一个命令解释器,它解释()的命令并且把它们送到内核。 选项A:系统输入 选项B:用户输入 选项C:系统和用户输入 选项D:输入 正确选项:B 以下说法错误的是()。 选项A:OllyDbg可以很容易修改实时数据,如寄存器和标志。它也可以将汇编形式的修补代码直接插入到一个程序 选项B:OllyDbg可以使用00项或nop指令填充程序 选项C:键单击高亮的条件跳转指令,然后选择Binary Fill with NOPs,该操作产生的结果时NOP指令替换了JNZ指令,这个过程会把那个位置上的NOP永久保存在磁盘上,意味着恶意代码以后会接受任意输入的密钥 选项D:当异常发生时,OllyDbg会暂停运行,然后你可以使用进入异常、跳过异常、运行异常处理 等方法,来决定是否将异常转移到应用程序处理 正确选项:C 以下不是GFI沙箱的缺点的是()。 选项A:沙箱只能简单地运行可执行程序,不能带有命令行选项 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第3页。选项B:沙箱环境的操作系统对恶意代码来说可能不正确 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第3页。 选项C:沙箱不能提供安全的虚拟环境 选项D:恶意代码如果检测到了虚拟机,将会停止运行,或者表现异常。不是所有的沙箱都能完善地考虑这个问题 正确选项:C 下列是抓包的工具是()。 选项A:ApateDNS 选项B:Netcat 选项C:INetSim 选项D:Wireshark 正确选项:D 以下逻辑运算符中是位移指令的是() 选项A:OR、AND 选项B:Shr和shl 选项C:ror和rol 选项D:XOR 正确选项:C 要插入一个跨反汇编窗口,并且在任何时候只要存在对你添加注释的地址的交叉引用就重复回显,应该按()键。 选项A:; 选项B:: 选项C:shift 选项D:ctrl 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第4页。正确选项:A 南开21春学期《计算机病毒分析》在线作业全文共14页,当前为第4页。 以下不是检测SSDT挂钩的方法是 选项A:遍历SSDT表 选项B:使用查杀病毒的软件 选项C:查找异常的函数入口地址 选项D:ntoskrnl.exe的地址空间是从804d7000到806cd580 正确选项:B ()能够将一个被调试的进程转储为一个PE文件 选项A:OllyDump 选项B:调试器隐藏插件 选项C:命令行 选项D:书签 正确选项:A WinINet API实现了()层的协议。 选项A:网络层 选项B:数据链路层 选项C:应用层 选项D:传输
VB一些常用控件集,以及一些方法模块,编辑框.ctl、进度条、全局热键钩子、网站服务器、托盘控件、WinSock.ctl、曲线图.ctl、压缩算法-升级版.cls、数组加解密.cls、打开文件属性面板.bas等,其中一个模块的部分代码摘录如下:   ------------------------------------------------------------------------------------------    ‘遍历进程,查找notepad.exe    MyRemoteProcessId = OpenProcess(PROCESS_CREATE_THREAD PROCESS_VM_OPERATION PROCESS_VM_WRITE PROCESS_VM_READ, False, ProcessInfo.th32ProcessID)    ‘打开进程获得notepad的句柄供后面的操作使用    DllFileName = "C:Vblegend.dll"    MyDllFileLength = Len(DllFileName) 1    ‘学过C语言的朋友应该知道字符串最后要一个ASCII 0标志结尾,所以要加1    MyDllFileBuffer = VirtualAllocEx(MyRemoteProcessId, 0, MyDllFileLength, MEM_COMMIT, PAGE_READWRITE)    ‘在指定进程里申请一块内存区域出来供我们存放字符串“c: est.dll“    ‘传string给api时,byval byref有区别,应该使用byval,这样会传给api一个标准的C字符指针,不能byref,否则函数调用没问题    ‘但是起不到预期效果,VirtualAllocEx返回的是申请到的内存地址值.    MyReturn = WriteProcessMemory(MyRemoteProcessId, MyDllFileBuffer, DllFileName, MyDllFileLength, temp)    ‘向刚才申请的内存中写入dll文件路径字符串    ‘顺便说一下,很多api浏览器上的api声明都是错的,包括VB6自带的也不例外,writeprocessmemory第二个参数要的是    ‘lpBaseAddress 但是这个值不能传址得到,如果你按byref传址,实际上传的是MyDllFileBuffer变量的地址,而不是它里面存放的那个数字    ‘上面说了MyDllFileBuffer的数值才是WriteProcessMemory要的地址,所以声明API的时候一定要byval,大家知道空着不写就是默认byref    ‘下面还有几处不该传址的参数,只要搞清楚API函数要的到底是什么值才可以确定到底传值还是传址,API浏览器仅能供参考,还是要仔细阅读MSDN    MyStartAddr = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA")    ‘获取loadlibrary函数的地址,这个函数可以载入指定的dll文件,那他的参数呢?就是我们刚才在notepad.exe进程里写入的“c: est.dll“    ‘不过还得让CreateRemoteThread告诉他.另外简单的说一下windows下应用程序的内存管理,我也不很懂,呵呵,win32下的应用程序    ‘的内存区域是隔开的,每个程序有自己的一块内存不能直接访问别的程序的内存区,当然,这里调用的几个系统函数有访问别的程序内存区域的特权    ‘而且每个应用程序的内存区域都映射到系统内存区域里,也就是说在这里GetProcAddress得到的VB程序里LoadLibraryA函数的入口地址和    ‘notepad程序里的LoadLibraryA函数地址是一致的(映射的作用),所以不必担心.另外在VB写的程序里    ‘要使用LoadLibraryA,notepad不是用vc写的吗?要注意根notepad没关系,我们现在是在自己的VB程序里面找LoadLibraryA函数的入口.    ‘还有要注意函数大小写,api函数和vb不一样的。    MyResult = CreateRemoteThread(MyRemoteProcessId, 0, 0, MyStartAddr, MyDllFileBuffer, 0, temp)    ‘好了,现在该让LoadLibrary载入“c: est.dll“吧,现在CreateRemoteThread做的就是在notepad进程中把控制权转到LoadLibrar

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧