利用VB编写dll注入其他进程的源代码

本文没什么特别的技术含量,高手免阅。

本文是此帖的具体实现…………http://www.rekersoft.cn/article.asp?id=4

前些日子为了隐藏KillIM(见http://www.rekersoft.cn/article.asp?id=8)于是上网找了些注入dll的资料,另外在一个u盘里面意外发现了以前备份的一些代码(原来的电脑被偷了 ),其中就包含了dll注入,呵呵,可以省点事了,把精力集中在vb编译标准dll上。以前其实有个csdn上牛人写的vb插件,可以用标准exe工程生成dll,而不是网上流行的activex dll工程加连接参数生成标准dll(那样其实不是标准的,本质还是activex dll,比如不能用来做hook)。可惜csdn改版以后,原来的下载页面都没了,也就再也找不到了。

此帖主要讲一下dll的注入与卸载(叫反注入貌似不太恰当,反正就是那个意思),以及如何编写一个注入用dll。因为在网上好多讲注入的帖子中的dll都讲得很简单,简单的show一个msgbox。问题是注入之后,dllmain执行一次就返回了,接着要怎么继续让dll做事情很多菜鸟(包括我)都不知道。所以就研究了下,贴在这里。

第一部分讲的是注入程序:

dll注入的核心就是用CreateRemoteThread函数在目标进程中创建一个线程(如果你不知道线程是什么?just google it),该线程只有一个作用,就是LoadLibraryA我们的dll,而LoadLibraryA又会调用dll的DllMain函数并发送DLL_PROCESS_ATTACH参数告诉dll你被加载了。此时我们的dll就成为了目标进程的一部分,于是乎,你该干吗就可以干吗去了。

ok,来说说代码吧。下面是api申明,放在模块当中。


程序代码

Option Explicit
'dll注入程序
'api申明模块
'
'蓝色炫影  制作
'www.rekersoft.cn
'
'最后更新 2008/05/06
'您可以自由用于非商业用途。
'请保留此行版权信息,谢谢。

Public Const PROCESS_VM_READ = &H10
Public Const TH32CS_SNAPPROCESS = &H2
Public Const MEM_COMMIT = 4096
Public Const MEM_DECOMMIT = &H4000
Public Const PAGE_READWRITE = 4
Public Const PROCESS_Create_THREAD = (&H2)
Public Const PROCESS_VM_OPERATION = (&H8)
Public Const PROCESS_VM_WRITE = (&H20)
Public Const PROCESS_ALL_ACCESS = &H1F0FFF
Public Const INFINITE = &HFFFF      '  Infinite timeout

Public Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As Long, _
          ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, _
          ByVal flProtect As Long) As Long
Public Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As Long, _
          ByVal lpAddress As Long, ByVal dwSize As Long, _
          ByVal dwFreeType As Long) As Long
'这两个api的作用是在目标进程中分配一段空白内存供程序使用。在vb的api浏览器中是找不到的。

Public Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, _
          ByVal lpProcName As String) As Long
Public Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" _
          (ByVal lpModuleName As String) As Long
'得到函数地址与dll模块地址

Public Declare Function CreateToolhelp32Snapshot Lib "kernel32" _
          (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
Public Declare Function Process32First Lib "kernel32" (ByVal hSnapshot As Long, _
           lppe As PROCESSENTRY32) As Long
Public Declare Function Process32Next Lib "kernel32" (ByVal hSapshot As Long, _
           lppe As PROCESSENTRY32) As Long
'这三个api用来遍历进程

Public Declare Function OpenProcess Lib "kernel32" _
          (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
            ByVal dwProcessId As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
'打开与关闭进程句柄

Public Declare Function WriteProcessMemory Lib "kernel32" _
          (ByVal hProcess As Long, ByVal lpBaseAddress As Long, lpBuffer As Any, _
           ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
'这里注意lpBaseAddress的传送方式是byval,和api浏览器中的声明是不一样的。 _
byval是传值,默认是byref是传址,也就是传递的是参数在内存中的地址
Public Declare Function CreateRemoteThread Lib "kernel32" _
          (ByVal hProcess As Long, ByVal lpThreadAttributes As Long, _
            ByVal dwStackSize As Long, ByVal lpStartAddress As Long, _
            ByVal lpParameter As Long, ByVal dwCreationFlags As Long, _
            lpThreadId As Long) As Long
'这里也是一样,几个参数的传递方式与api浏览器中的声明不一样
Public Declare Function WaitForSingleObject Lib "kernel32" _
          (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function GetExitCodeThread Lib "kernel32" _
          (ByVal hThread As Long, lpExitCode As Long) As Long
'这两个函数程序当中没用到。在窗体代码中有注释。

Public Type PROCESSENTRY32
    dwSize              As Long
    cntUseage           As Long
    th32ProcessID       As Long
    th32DefaultHeapID   As Long
    th32ModuleID        As Long
    cntThreads          As Long
    th32ParentProcessID As Long
    pcPriClassBase      As Long
    swFlags             As Long
    szExeFile           As String * 1024
End Type



接着在窗体中画一个Text1用来输入准备注入的dll名,外加2个Button,Command1是注入,Command2是卸载。
窗体代码:



程序代码

Option Explicit
'dll注入程序
'主窗体代码
'
'蓝色炫影  制作
'www.rekersoft.cn
'
'最后更新 2008/05/06
'您可以自由用于非商业用途。
'请保留此行版权信息,谢谢。

Public Sub Inject()
'注入子程序

Dim MySnapHandle            As Long   '存放进程快照句柄
Dim ProcessInfo             As PROCESSENTRY32
Dim MyRemoteProcessId       As Long   '目标进程pid
Dim MyDllFileName           As String 'dll文件路径
Dim MyDllFileLength         As Long   'dll文件名长度
Dim MyDllFileBuffer         As Long   '写入dll文件名的内存地址
Dim MyAddr                  As Long   '执行远程线程代码的起始地址。这里等于LoadLibraryA的地址
Dim MyReturn                As Long

MySnapHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
ProcessInfo.dwSize = Len(ProcessInfo)
'建立进程快照

If Process32First(MySnapHandle, ProcessInfo) <> 0 Then
'开始遍历进程
Do
    If InStr(ProcessInfo.szExeFile, "explorer.exe") > 0 Then
        '遍历进程,查找explorer.exe
        
        MyDllFileName = App.Path & "\" & IIf(LCase(Right(Text1, 4)) = ".dll", Text1, Text1.Text & ".dll")
        'dll文件路径
        MyDllFileLength = LenB(StrConv(MyDllFileName, vbFromUnicode)) + 1
        '这里把dll文件名从Unicode转换成Ansi,否则英文字母是2个字节。 _
         顺便说一下,学过C的应该知道字符串要以/0标志结尾,所以dll文件名长度要加上1个字节存放Chr(0)
        
        MyRemoteProcessId = OpenProcess(PROCESS_ALL_ACCESS, False, ProcessInfo.th32ProcessID)
        '得到进程的句柄
        If MyRemoteProcessId = 0 Then MsgBox "OpenProcess Error"
        
        
        MyDllFileBuffer = VirtualAllocEx(MyRemoteProcessId, 0, MyDllFileLength, MEM_COMMIT, PAGE_READWRITE)
        '在目标进程中申请分配一块空白内存区域。内存的起始地址保存在MyDllFileBuffer中。 _
         这块内存区域我们用来存放dll文件路径,并作为参数传递给LoadLibraryA。
        If MyDllFileBuffer = 0 Then MsgBox "VirtualAllocEx Error"
        
        MyReturn = WriteProcessMemory(MyRemoteProcessId, MyDllFileBuffer, ByVal (MyDllFileName), MyDllFileLength, 0)
        '在分配出来的内存区域中写入dll路径径。注意第二个参数传递的是MyDllFileBuffer的内容, _
         而不是MyDllFileBuffer的内存地址。
        If MyReturn = 0 Then MsgBox "WriteProcessMemory Error"

        MyAddr = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA")
        '得到LoadLibraryA函数的起始地址。他的参数就是我们刚才写入的dll路径。但是LoadLibraryA本身是不知道参数在哪里的。 _
         接下来我们就用CreateRemoteThread函数告诉他参数放在哪里了。
        If MyAddr = 0 Then MsgBox "GetProcAddress Error"

        MyResult = CreateRemoteThread(MyRemoteProcessId, 0, 0, MyAddr, MyDllFileBuffer, 0, 0)
        '好了,现在用CreateRemoteThread在目标进程创建一个线程,线程起始地址指向LoadLibraryA, _
         参数就是MyDllFileBuffer中保存的dll路径。
        If MyResult = 0 Then MsgBox "error CreateRemoteThread"
        '接下来你可以使用WaitForSingleObject等待线程执行完毕。 _
         并用GetExitCodeThread得到线程的退出代码,用来判断时候正确执行了dll中的代码。
        CloseHandle MyResult
        CloseHandle MyRemoteProcessId
        '扫地工作
    End If
Loop While Process32Next(MySnapHandle, ProcessInfo) <> 0
End If

CloseHandle MySnapHandle
End Sub

Private Sub Command1_Click() '点击注入按钮
Inject '执行注入
End Sub

接下来我们讲怎么把注入进去的dll卸载掉,其实与注入基本上是一样的,只不过用到的API要换一下。
我们先在目标进程当中执行GetModuleHandleA,得到我们dll的句柄,然后再远程执行FreeLibrary,把我们的dll卸载。
那么这里有个问题,如何得到GetModuleHandleA的返回值呢?
那就要用到WaitForSingleObject,这个函数的作用是等待远程线程执行完毕,之后我们只要调用GetExitCodeThread得到远程线程的退出码就行了。因为远程线程是调用GetModuleHandleA,所以退出码就是GetModuleHandleA的返回值。

代码如下:



程序代码

Private Sub Enject()

Dim MySnapHandle As Long
Dim ProcessInfo As PROCESSENTRY32
Dim MyRemoteProcessId As Long
Dim MyDllFileLength As Long
Dim MyDllFileBuffer As Long
Dim MyReturn As Long
Dim MyAddr As Long
Dim MyResult As Long
Dim MyDllFileName As String
Dim dwHandle As Long

MySnapHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
ProcessInfo.dwSize = Len(ProcessInfo)


If Process32First(MySnapHandle, ProcessInfo) <> 0 Then
Do
    If InStr(ProcessInfo.szExeFile, "explorer.exe") > 0 Then
        MyRemoteProcessId = OpenProcess(PROCESS_ALL_ACCESS, False, ProcessInfo.th32ProcessID)
        If MyRemoteProcessId = 0 Then MsgBox "error OpenProcess"
        
        MyStartAddr = GetProcAddress(GetModuleHandle("Kernel32"), "GetModuleHandleA")
        '与注入一样,只不过这里换成了GetModuleHandleA. _
         原理就是先在目标进程执行GetModuleHandleA得到我们的dll的地址,然后再用FreeLibrary卸载掉我们的dll。
        If MyStartAddr = 0 Then MsgBox "error GetProcAddress"
        
        MyDllFileName = IIf(LCase(Right(Text1, 4)) = ".dll", Text1, Text1.Text & ".dll")
        MyDllFileLength = LenB(StrConv(MyDllFileName, vbFromUnicode)) + 1
        
        MyDllFileBuffer = VirtualAllocEx(MyRemoteProcessId, 0, MyDllFileLength, MEM_COMMIT, PAGE_READWRITE)
        If MyDllFileBuffer = 0 Then MsgBox "error VirtualAllocEx"
        
        MyReturn = WriteProcessMemory(MyRemoteProcessId, MyDllFileBuffer, ByVal (MyDllFileName), MyDllFileLength, temp)
        If MyReturn = 0 Then MsgBox "error WriteProcessMemory"

        MyResult = CreateRemoteThread(MyRemoteProcessId, 0, 0, MyAddr, MyDllFileBuffer, 0, temp)
        '都与注入一样。执行到这里就得到了
        WaitForSingleObject MyResult, INFINITE '等待远程线程执行完毕。
        GetExitCodeThread MyResult, dwHandle '这里可以得到远程线程的返回值,也就是GetModuleHandleA返回的我们的dll的句柄。
        
        VirtualFreeEx MyRemoteProcessId, MyDllFileBuffer, MyDllFileLength, MEM_DECOMMIT
        '释放掉我们申请的内存段
        CloseHandle MyResult
        
        MyAddr = GetProcAddress(GetModuleHandle("Kernel32"), "FreeLibrary")
        If MyAddr = 0 Then MsgBox "error FreeLibrary"
        MyResult = CreateRemoteThread(MyRemoteProcessId, 0, 0, MyAddr, dwHandle, 0, temp)
        '这次不需要再用WriteProcessMemory写入FreeLibrary的参数了, _
         因为我们的dll的句柄刚才已经保存在dwHandle里面了。
        If MyResult = 0 Then MsgBox "error CreateRemoteThread"
        CloseHandle MyResult
        CloseHandle MyRemoteProcessId
    End If
Loop While Process32Next(MySnapHandle, ProcessInfo) <> 0
End If


CloseHandle MySnapHandle

End Sub


我们再加上几个按钮的动作,整个注入程序就完成了


程序代码

Private Sub Command1_Click()
Inject
End Sub

Private Sub Command2_Click()
Enject
End Sub

Private Sub Form_Load()
Text1 = GetSetting(App.Title, "dllname", "dllname")
End Sub

Private Sub Form_Unload(Cancel As Integer)
SaveSetting App.Title, "dllname", "dllname", Text1
End Sub

Private Sub Text1_GotFocus()
Text1.SelStart = 0
Text1.SelLength = Len(Text1.Text)
End Sub



在text1里面输入想要注入的dll文件名,然后点Command1就行了。

明天讲注入的dll怎么写……

实在对不起,最近太忙了,没什么时间更新blog。

今天讲一下dll怎么写。

需要装一个vb插件:vbAdvance
利用它可以让生成dll文件。

具体用法我就不讲了,很简单。我主要讲一下代码。
由于是利用loadlibrary来注入,所以只会执行dllmain函数,然后返回
因为dll不是exe,没有自己的消息循环,执行完了dllmain函数就返回了,所以我们要想个办法让他一直可以发挥作用。其实很简单,就是SetTimer和KillTimer。
新建一个工程,类型选择vbAdvance提供的dll模板


程序代码

Option Explicit
Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long

Private Const DLL_PROCESS_ATTACH    As Long = 1
Private Const DLL_THREAD_ATTACH     As Long = 2
Private Const DLL_PROCESS_DETACH    As Long = 0
Private Const DLL_THREAD_DETACH     As Long = 3

'这个是Dll主函数,加载/卸载dll时会调用
Public Function DllMain(ByVal hinstDLL As Long, ByVal fdwReason As Long, ByVal lpvReserved As Long) As Long
Select Case fdwReason
    Case DLL_PROCESS_ATTACH 'dll加载
        'RuntimeInitialize hinstDLL 'vbAdvance提供的初始化函数,用来初始化vb运行库。我这里没有用,这样我们就不能使用vb的函数了,都要自己实现。
        getHandle
    Case DLL_PROCESS_DETACH 'dll卸载
        KillTimer Handle, 1 '杀掉timer
End Select
DllMain = 1 '返回true。
End Function

Private Sub getHandle()
    Handle = FindWindow("Progman", "Program Manager") '用来得到explorer.exe的句柄,当然,你也可以用GetCurrentProcess,因为此时dll已经是explorer.exe进程的一部分了
    SetTimer Handle, 1, 5000, AddressOf timer1_Timer 'SetTimer第一个参数是进程句柄,我们把explorer句柄传进去,第二个是timer的编号,用来区分timer的。第三个是时间间隔。第四个参数是一个回调函数。我们用AddressOf操作符把timer1_Timer函数的地址传进去。这样到了我们设定的时间间隔,系统就会自动调用timer_timer
End Sub

Public Sub timer_Timer()
    '这里就可以做你想做的事情了。
    '这个例子当中,此过程每5秒执行一次。
end Sub



另外我们可以改写默认的窗口函数,接收消息。这样我们可以利用我们的程序向被注入的进程发送消息来控制dll。

dll注入到此就结束了。
这篇文章有点凑数了,还望海涵。




文章来自: 锐客软件
引用通告: 查看所有引用 | 我要引用此文章
Tags:
评论: 0 | 引用: 0 | 查看次数: 159
发表评论
昵 称:
密 码: 游客发言不需要密码.
内 容:
验证码: 验证码
选 项:
虽然发表评论不用注册,但是为了保护您的发言权,建议您注册帐号.
字数限制 1000 字 | UBB代码 开启 | [img]标签 关闭