这个实战小工具是“吃了亏”才反思出来,名字叫“远程登陆报警器”(有点土),设计目的是为了在远程“勘验“嫌疑人的服务器时,临时部署在服务器上,防止嫌疑人突然登陆服务器,而你还在撅着屁股埋头苦干,避免被人抓个现行的防御性工具。
一、序言
2008年写的,没什么技术含量,主要是思路有点意思。
始于当年的一个小插曲:“那几天,很无聊地远程登陆到一哥们的服务器上,看看他的机器中可有什么好东西,query user后发现他人不在,觉得胆子大了点,就在我翻箱倒柜忘我时,突然弹出一个窗口,惊了我一头汗,
图-1
晕啊,光顾着找东西,忘了注意他是什么时候上来的。还好是朋友,要是别的什么可就麻烦了。”
所以设计这个工具的应用场景就是:当你在远程“勘验”别人服务器时,一要做好防御,怎么才能第一时间发现别人(可能是嫌疑人)上来?想知道你们平时用的什么招?
二、想法
想到要有个报警器之类的报警程序就好啦!网上搜索,有一个是用批处理来完成记录远程登陆的日志东西:
建个TSLog.bat文件,用来记录登录者的IP,内容如下:
time /t >>TSLog.log
netstat -n -p tcp | find ":3389">>TSLog.log
start Explorer
运行后,会在SLog.log中记录如下:
22:40 TCP 192.168.12.28:3389 192.168.10.123:4903 ESTABLISHED
22:54 TCP 192.168.12.28:3389 192.168.12.29:1039 ESTABLISHED
为了让这个批处理在用户登陆时自动执行,这样设置:在终端服务中,我们覆盖用户的登录脚本设置并指定TSLog.bat为用户登录时需要打开的脚本,这样每个用户登录后都必须执行这个脚本,因为默认的脚本是Explorer,所以加上启动Explorer的命令start Explorer,如果不加这一行命令,用户是没有办法进入桌面的!
但这个并不能实时的报警,只是做为一种记录性东西。
于是有了写一个报警程序的念头,让它自动报警!
三、原理及思路
当我们远程登陆到一个服务器上,都可以用query user命令或netstat –an查看3389端口的连接方或任务管理器中看到当前有几个登陆用户。
图-2
图-3
在图-2中,我们可以看到有两个IP地址连接到218.X.X.131的3389端口上,这说明有两个用户登陆到服务器上,其中一个是我的IP,另一个就是我那哥们的IP。
我下面要实现的就是这么个原理,思路是:我登陆进来后,运行我的程序(程序简称“报警器”),当再有人通过3389端口登陆上来时,就会向我报警,界面如图-4:
图-4
看到这样的,就要收拾工具,擦除痕迹,赶紧闪人啦!
先来看下程序界面,
图-5
在程序中,设定了两个要填写的参数,分别是:你远程登陆的IP和服务器远程终端端口号,解释一下,①你远程登陆的IP:自己远程登陆到服务器会有一个IP,我们要将自己的本地IP填写在这里,到时候程序报警时就会将我们自己的远程登陆IP排除在外。②远程端口号:一般都是3389,但也可能会改变,象在这里我的举例图中就改为了18203。
当这个参数填写正确后,按“开始”就可以自动监测了,如果有其他人通过远程登陆上来,会立即弹出图-1样的窗口向我们报警。
在程序中,主要是利用一个Timer来不停地查看当前的连接情况(利用netstat)。这是个演示程序,仅将自己的IP设为报警对象(在“你远程登陆的IP”那个框中填入你自己的IP,如图-5中显示的那样,我的IP为220.179.X.X,服务器的端口号为:18203),如果需要将自己设为排除对象(报其他人远程登陆的警),请修改源程序(将“=”修改为“<>”就可以啦),很方便的。
这里给出正宗的源代码,今天看了下,网上的代码都是流出去的。
四、程序源代码
//程序写于2008年,代码有些幼稚及不规范,请见谅
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, WinSock;
type
TMainForm = class(TForm)
Eip: TEdit;
lbl1: TLabel;
lbl2: TLabel;
EPort: TEdit;
timer1: TTimer;
StartButton: TButton;
StopButton: TButton;
procedure StartButtonClick(Sender: TObject);
procedure StopButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure timer1Timer(Sender: TObject);
procedure EipKeyPress(Sender: TObject; var Key: Char);
procedure EPortKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TAsnOctetString = record
stream: pByte;
length: Cardinal;
dynamic: Boolean;
end;
TAsnObjectIdentifier = record
idLength: Cardinal;
ids: Pointer;
end;
pAsnObjectIdentifier = ^TAsnObjectIdentifier;
TAsnObjectSyntax = record
case asnType: Byte of
0: (number: LongInt);
1: (unsigned32: Cardinal);
2: (counter64: Int64);
3: (AsnString: TAsnOctetString);
4: (bits: TAsnOctetString);
5: (AsnObject: TAsnObjectIdentifier);
7: (sequence: TAsnOctetString);
8: (address: TAsnOctetString);
9: (counter: Cardinal);
10: (gauge: Cardinal);
11: (ticks: Cardinal);
12: (arbitrary: TAsnOctetString);
end;
TRFC1157VarBind = record
name: TAsnObjectIdentifier;
value: TAsnObjectSyntax;
end;
pRFC1157VarBind = ^TRFC1157VarBind;
TRFC1157VarBindList = record
list: pRFC1157VarBind;
len: DWord
end;
pRFC1157VarBindList = ^TRFC1157VarBindList;
pTcpInfo = ^TTcpInfo;
TTcpInfo = record
prev: pTcpInfo;
next: pTcpInfo;
state: Cardinal;
localip: Cardinal;
localport: Cardinal;
remoteip: Cardinal;
remoteport: Cardinal;
end;
TSnmpExtensionInit = function(dwTimeZeroReference: DWord;
hPollForTrapEvent: PHandle;
pFirstSupportedRegion: pAsnObjectIdentifier
): Boolean; stdcall;
TSnmpExtensionQuery = function(requestType: Byte;
variableBindings: pRFC1157VarBindList;
errorStatus: pLongInt;
errorIndex: pLongInt
): Boolean; stdcall;
type userarray = array of string; //将字符串分隔成数组
const
HOSTNAMELEN = 256;
PORTNAMELEN = 256;
ADDRESSLEN = HOSTNAMELEN + PORTNAMELEN;
tcpidentifiers: array[0..9] of Cardinal = (1, 3, 6, 1, 2, 1, 6, 13, 1, 1);
udpidentifiers: array[0..9] of Cardinal = (1, 3, 6, 1, 2, 1, 7, 5, 1, 1);
TcpState: array[0..11] of PChar =
('???', 'CLOSED', 'LISTENING', 'SYN_SENT',
'SEN_RECEIVED', 'ESTABLISHED', 'FIN_WAIT', 'FIN_WAIT2',
'CLOSE_WAIT', 'CLOSING', 'LAST_ACK', 'TIME_WAIT');
var
MainForm: TMainForm;
implementation
{$R *.dfm}
var
MySnmpExtensionInit: TSnmpExtensionInit;
MySnmpExtensionQuery: TSnmpExtensionQuery;
wsaData: TWSAData;
hTrapEvent: THandle;
hIdentifier: TAsnObjectIdentifier;
bindList: TRFC1157VarBindList;
bindEntry: TRFC1157VarBind;
TcpInfoTable: TTcpInfo;
CurrentIndex: Cardinal;
newEntry, CurrentEntry: pTcpInfo;
errorStatus, errorIndex: LongInt;
localaddr, remoteaddr: array[1..ADDRESSLEN] of Char;
localname, remotename: array[1..HOSTNAMELEN] of Char;
remoteport, localport: array[1..PORTNAMELEN] of Char;
remoteip_array: userarray;
ipaddr, remote_addr, remote_addr1, tcp_status: string;
//portnum, remote_portnum: Integer;
s1, s2: PChar;
port_mark: Boolean;
dotnumber: Integer;
function GetPortName(Port: Integer; Proto, name: PChar; NameLen: Integer): PChar;
var Srvent: PServEnt;
begin
if not port_mark then
begin
Srvent := GetServByPort(htons(WORD(Port)), Proto);
if Srvent = nil then StrPLCopy(name, Format('%d', [Port]), NameLen)
else
if name <> MainForm.EPort.text then
StrPLCopy(name, Format('%s', [Srvent^.s_name]), NameLen);
Result := name;
end;
end;
function GetIpHostName(Local: Boolean; ipaddr: DWord; name: PChar; NameLen: Integer): PChar;
var HostEnt: PHostEnt;
nIpAddr: DWord;
begin
nIpAddr := htonl(ipaddr);
if ipaddr = 0 then begin
if not Local then StrPCopy(name, Format('%d.%d.%d.%d', [(nIpAddr shr 24) and $FF,
(nIpAddr shr 16) and $FF,
(nIpAddr shr 8) and $FF,
nIpAddr and $FF]))
else Gethostname(name, NameLen);
end else begin
if (ipaddr = $0100007F) then
if Local then Gethostname(name, NameLen)
else StrCopy(name, 'localhost')
else begin
HostEnt := GetHostByAddr(@ipaddr, Sizeof(nIpAddr), PF_INET);
if HostEnt <> nil then StrCopy(name, HostEnt^.h_name)
else StrPCopy(name, Format('%d.%d.%d.%d', [(nIpAddr shr 24) and $FF,
(nIpAddr shr 16) and $FF,
(nIpAddr shr 8) and $FF,
nIpAddr and $FF]))
end;
end;
Result := name;
end;
function LoadInetMibEntryPoints: Boolean;
var hInetLib: THandle;
begin
Result := False;
hInetLib := LoadLibrary('inetmib1.dll');
if hInetLib = 0 then Exit;
@MySnmpExtensionInit := GetProcAddress(hInetLib, 'SnmpExtensionInit');
if @MySnmpExtensionInit = nil then Exit;
@MySnmpExtensionQuery := GetProcAddress(hInetLib, 'SnmpExtensionQuery');
if @MySnmpExtensionQuery = nil then Exit;
Result := True;
end;
//按所给字符将字符串分隔成数组
function split(s: string; dot: Char): userarray;
var
str: userarray;
i, j: Integer;
begin
i := 1;
j := 0;
SetLength(str, 255);
while Pos(dot, s) > 0 do //Pos返回子串在父串中第一次出现的位置.
begin
str[j] := copy(s, i, Pos(dot, s) - i);
i := Pos(dot, s) + 1;
s[i - 1] := chr(ord(dot) + 1);
j := j + 1;
end;
dotnumber := j;
str[j] := copy(s, i, strlen(PChar(s)) - i + 1);
Result := str;
end;
function GetIPDotCount(IPAddress: string): Integer;
//判断IP的分隔数,分隔号为“.”,如:X.X.X.X,简单的判断IP格式是否正确
var
i, j: Integer;
begin
j := 0;
for i := 1 to 3 do
begin
if Pos('.', IPAddress) < 1 then
begin
Result := j;
Exit;
end;
IPAddress := StringReplace(IPAddress, '.', '', [rfIgnoreCase]);
j := j + 1;
end;
Result := j;
end;
procedure TMainForm.StartButtonClick(Sender: TObject);
begin
if Eip.text = '' then
begin
ShowMessage('输入你远程登陆的IP!');
Exit;
end
else
begin
if GetIPDotCount(Trim(Eip.text)) <> 3 then
begin
ShowMessage('你输入的IP格式不对!');
Exit;
end;
end;
if EPort.text = '' then
begin
ShowMessage('输入远程登陆的端口号(如:3389)');
Exit;
end;
ipaddr := Eip.text;
//portnum := StrToInt(EPort.text);
timer1.enabled := True;
StartButton.enabled := False;
StopButton.enabled := True;
end;
procedure TMainForm.StopButtonClick(Sender: TObject);
begin
timer1.enabled := False;
StartButton.enabled := True;
StopButton.enabled := False;
end;
procedure TMainForm.EipKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
if Eip.text = '' then
begin
ShowMessage('输入你远程登陆的IP!');
Exit;
end
else
begin
if GetIPDotCount(Trim(Eip.text)) <> 3 then
begin
ShowMessage('你输入的IP格式不对!');
Exit;
end;
end;
end;
end;
procedure TMainForm.EPortKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
if EPort.text = '' then
begin
ShowMessage('输入远程登陆的端口号(如:3389)');
Exit;
end;
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
if WSAStartup($0101, wsaData) <> 0 then begin
ShowMessage('Could not initialize Winsock.');
Exit;
end;
if not LoadInetMibEntryPoints then begin
ShowMessage('Could not load extension DLL.');
Exit;
end;
if not MySnmpExtensionInit(GetCurrentTime, @hTrapEvent, @hIdentifier) then begin
ShowMessage('Could not initialize extension DLL.');
Exit;
end;
timer1.enabled := False;
end;
procedure TMainForm.timer1Timer(Sender: TObject);
begin
bindEntry.name.idLength := $0A;
bindEntry.name.ids := @(tcpidentifiers[0]);
bindList.list := @bindEntry;
bindList.len := 1;
TcpInfoTable.prev := @TcpInfoTable;
TcpInfoTable.next := @TcpInfoTable;
CurrentIndex := 1;
CurrentEntry := @TcpInfoTable;
while True do begin
if not MySnmpExtensionQuery($A1, @bindList, @errorStatus, @errorIndex) then Exit;
if bindEntry.name.idLength < $0A then break;
if CurrentIndex <> (pDWord(Integer(bindEntry.name.ids) + 9 * Sizeof(Cardinal)))^ then begin
CurrentEntry := TcpInfoTable.next;
CurrentIndex := (pDWord(Integer(bindEntry.name.ids) + 9 * Sizeof(Cardinal)))^;
end;
case (pDWord(Integer(bindEntry.name.ids) + 9 * Sizeof(Cardinal)))^ of
1: begin // Always allocate a new structure
newEntry := AllocMem(Sizeof(TTcpInfo));
newEntry^.prev := CurrentEntry;
newEntry^.next := @TcpInfoTable;
CurrentEntry^.next := newEntry;
CurrentEntry := newEntry;
CurrentEntry^.state := bindEntry.value.number;
end;
2: begin
CurrentEntry^.localip := (pDWord(bindEntry.value.address.stream))^;
CurrentEntry := CurrentEntry^.next;
end;
3: begin
CurrentEntry^.localport := bindEntry.value.number;
CurrentEntry := CurrentEntry^.next;
end;
4: begin
CurrentEntry^.remoteip := (pDWord(bindEntry.value.address.stream))^;
CurrentEntry := CurrentEntry^.next;
end;
5: begin
CurrentEntry^.remoteport := bindEntry.value.number;
CurrentEntry := CurrentEntry^.next;
end;
end;
end;
CurrentEntry := TcpInfoTable.next;
while CurrentEntry <> @TcpInfoTable do
begin
s1 := GetIpHostName(True, CurrentEntry^.localip, PChar(@localname[1]), HOSTNAMELEN);
s2 := GetPortName(CurrentEntry^.localport, 'tcp', PChar(@localport[1]), PORTNAMELEN);
StrPCopy(@localaddr[1], Format('%s:%s', [s1, s2]));
s1 := GetIpHostName(False, CurrentEntry^.remoteip, PChar(@remotename[1]), HOSTNAMELEN);
s2 := GetPortName(CurrentEntry^.remoteport, 'tcp', PChar(@remoteport[1]), PORTNAMELEN);
if CurrentEntry^.remoteip <> 0 then
begin
if StrToInt(EPort.text) = CurrentEntry^.localport then
begin
remoteip_array := split(s1, '.');
//会生成这样的格式:145.123.210.218.kx.cq.china.com,全是反的,所以当大于3个。分隔号时,下面要转换成正常的IP格式
if dotnumber > 3 then
s1 := StrPCopy(s1, Format('%s.%s.%s.%s', [PChar(remoteip_array[3]), PChar(remoteip_array[2]), PChar(remoteip_array[1]), PChar(remoteip_array[0])]));
port_mark := True;
end;
StrPCopy(@remoteaddr[1], Format('%s:%s', [s1, s2]))
end
else
begin
port_mark := False;
StrPCopy(@remoteaddr[1], Format('%s:0', [GetIpHostName(False, CurrentEntry^.remoteip, PChar(@remotename[1]), HOSTNAMELEN)]));
end;
CurrentEntry := CurrentEntry^.next;
remote_addr := PChar(@remoteaddr[1]);
tcp_status := TcpState[CurrentEntry^.state];
remote_addr1 := copy(remote_addr, 1, Pos(':', remote_addr) - 1);
if port_mark then
begin
if remote_addr1 = Eip.text then
//这里为举例,设置报警的是我自己的远程登陆IP,如果你设为排除自己登陆的IP,那就是别有人登陆进来了,就要注意了,最好擦除痕迹,赶快闪人。
begin
StopButton.Click();
ShowMessage('有人登陆了,快闪!登陆IP:' + remote_addr1 + ':' + copy(remote_addr, Pos(':', remote_addr) + 1, length(remote_addr) - Pos(':', remote_addr)));
Exit;
end
else port_mark := False;
end;
//else
// remote_portnum := StrToInt(Copy(remote_addr, Pos(':', remote_addr) + 1, length(remote_addr) - Pos(':', remote_addr)));
end;
end;
end.
这个程序有一定的实用价值,有兴趣的可以继续深入的完善它。另外,为了快速地清除痕迹,可以将清除痕迹的功能做到这个程序里,这样跑起来会快很多。
毕竟是2008年写的代码,请多多包涵。