正确设置自己的 PPC 键盘
因为是 T-Mobile MDA 版的PPC,所以使用的是德版键位,这位我日常使用带来了诸多的不便,因为平时使用的 ROM 要么是英文版的要么是中文版的,而他们的键位都是基于标准键位的。之前看过资料可以修改,但是一直没有在意,想着将就用不就可以了,但是随着 MDA 的 简体化 ROM 越来越稳定,使用键盘的几率也就越来越频繁,更不想再去死机这个键盘位置,干脆直接修改键位设置,恢复成默认的 MDA 键位,这样起码可以轻松地输入字符和符号。修改参数如下:
修改 Layout 的键值为“66567”,此外因该牢记之前的键位码方便恢复“67588”。
扩展 ISA 防火墙的SSL隧道端口范围
因为公司内部需要访问外部公网上的一个对帐系统,而这个对帐系统使用8443作为SSL的隧道端口,当从内部访问时便会出现错误。之前做过一次添加,因为升级到了ISA2006,重新修改了访问规则等配置,竟然忘记扩展端口了,最近频繁有员工反映这个问题,于是决定载入到 Blog 上做个永久性工作笔记。
先创建一个.js文件,当然这个文件是从网上获取到现成的,我直接将代码粘贴至此,方便我拷贝。
This program is Copyright (c) 2004 Microsoft Corporation.
All rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROSOFT AND/OR ITS RESPECTIVE SUPPLIERS BE
LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS CODE OR INFORMATION.
This program will allow the user to create, remove and display
Tunnel Port Ranges on either ISA 2000 or ISA 2004.
Revisions:
- 08/19/2004 - First working version
*/
var g_oObjects = new Objects;
var g_oValues = new Values;
var g_oMessages = new Messages;
main();
/**********************************************************************
* main()
* This function:
* 1. Attempts to create the ISA Admin COM object
* Determines if the environment is ISA 2K or ISA 2K4
* Sets g_oObjects.tpRanges to a proper TPR object
* Defers to ParseArgs() to perform the proper tasking
* 2. calls into
* GetISA()
* ParseArgs()
* 3. called by
* - user -
*
* if successful:
* 1. g_oObjects.ISA and g_oObjects.ThisArray are valid ISA objects
* 2. returns the result of GetISA() or ParseArgs() as appropriate
*
* if unsuccessful:
* 1. called functions indicate the failure and cause
* 2. returns the result of GetISA() or ParseArgs() as appropriate
*********************************************************************/
function main()
{
//WScript.Echo( 'working in main().' );
var iRtn;
iRtn = GetISA();
if ( iRtn == g_oValues.OK )
{
g_oObjects.tpRanges =
g_oObjects.thisArray.ArrayPolicy.WebProxy.TunnelPortRanges;
iRtn = ParseArgs( );
}
return iRtn;
}
/**********************************************************************
* GetISA( )
* This function:
* 1. Creates the default ISA COM object
* Determines if the environment is ISA 2K or ISA 2K4
* Sets g_oObjects.ISA to the proper ISA object
* 2. calls into
* GetISA2KSE()
* GetISA2K4SE()
* ShowErrors()
* 3. called by
* main()
*
* if successful:
* 1. g_oObjects.ISA and g_oObjects.ThisArray are valid objects
* 2. returns g_oValues.OK
*
* if unsuccessful:
* 1. ShowErrors indicate the failure and cause
* 2. returns values provided by called functions
*********************************************************************/
function GetISA( )
{
//WScript.Echo( 'working in GetISA().' );
var szSE = 'FPC.Root';
var iRtn;
try
{
g_oObjects.ISA = new ActiveXObject( szSE );
iRtn = GetISA2KSE( );
if( iRtn == g_oValues.notISA2K )
{
iRtn = GetISA2K4SE( );
}
if( iRtn )
{
WScript.Echo( g_oMessages.L_NoISA_txt )
ShowUsage( WScript.Arguments );
}
}
catch( err )
{
ShowErrors( err, g_oMessages.L_NoISA_txt );
ShowUsage( WScript.Arguments );
iRtn = g_oValues.noISA;
}
return iRtn;
}
/**********************************************************************
* GetISA2KSE( )
* This function:
* 1. Distinguishes between ISA2KSE, ISA2KEE and ISA Admin-only
* 2. calls into
* GetISA2K4EE()
* ShowErrors()
* 3. called by
* GetISA()
*
* if successful:
* 1. g_oObjects.ISA and g_oObjects.ThisArray are valid ISA objects
* 2. returns g_oValues.OK
*
* if unsuccessful:
* 1. ShowErrors indicate the failure and cause
* 2. returns values provided by called functions
*********************************************************************/
function GetISA2KSE( )
{
//WScript.Echo( 'working in GetISA2KSE().' );
var fpcTypeArray = 2;
var iRtn = g_oValues.OK;
try
{
if( g_oObjects.ISA.Type == fpcTypeArray )
{
iRtn = GetISA2KEE( );
}
if( iRtn == g_oValues.OK )
{
g_oObjects.thisArray = g_oObjects.ISA.Arrays.GetContainingArray();
}
}
catch( err )
{
switch( ToHex( err.number ) )
{
case g_oValues.lErrNotSupported:
iRtn = g_oValues.notISA2K;
err.clear;
break;
default:
iRtn = err.number;
ShowErrors( err, g_oMessages.L_notISA2K_txt );
}
}
return iRtn;
}
/**********************************************************************
* GetISA2KEE( )
* This function:
* 1. Sets g_oObjects.ISA to the correct EE context
* 2. calls into
* GetISA2K4EE()
* ShowErrors()
* 3. called by
* GetISASE()
*
* if successful:
* 1. g_oObjects.ISA is a valid EE object
* 2. returns g_oValues.OK
*
* if unsuccessful:
* 1. ShowErrors indicate the failure and cause
* 2. returns values provided by called functions
*********************************************************************/
function GetISA2KEE( )
{
//WScript.Echo( 'working in GetISA2KEE().' );
var szEE = 'FPCDS.Root';
var iRtn;
try
{
g_oObjects.ISA = new ActiveXObject( szEE );
iRtn = g_oValues.OK;
}
catch( err )
{
iRtn = err.number;
ShowErrors( err, g_oMessages.L_ErrUnknown_txt );
}
return iRtn;
}
/**********************************************************************
* GetISA2K4SE( )
* This function:
* 1. Sets g_oObjects.ISA to the an ISA 2004SE context
* 2. calls into
* ShowErrors()
* 3. called by
* GetISASE()
*
* if successful:
* 1. g_oObjects.ISA is a valid ISA 2004 SE object
* 2. returns g_oValues.OK
*
* if unsuccessful:
* 1. ShowErrors indicate the failure and cause
* 2. returns values according to determined state
*********************************************************************/
function GetISA2K4SE( )
{
//WScript.Echo( 'working in GetISA2K4SE().' );
var iRtn;
try
{
g_oObjects.thisArray = g_oObjects.ISA.GetContainingArray();
g_oObjects.thisArray.Refresh();
iRtn = g_oValues.OK;
}
catch( err )
{
switch( ToHex( err.number ) )
{
case g_oValues.lErrNotFound:
err.clear;
iRtn = g_oValues.AdminOnly;
break;
default:
iRtn = err.number
ShowErrors( err, g_oMessages.L_ErrUnknown_txt );
}
}
return iRtn;
}
/**********************************************************************
* ParseArgs( )
* This function:
* 1. Evaluates the cmd-line arguments
* Calls appropriate function
* 2. calls into
* AddRange()
* DelRange()
* ShowRanges()
* ShowUsage()
* 3. called by
* main()
*
* if successful:
* 1. user-defined action is executed
* 2. returns value from called function
*
* if unsuccessful:
* 1. returns values according to determined state
*********************************************************************/
function ParseArgs( )
{
//WScript.Echo( 'working in ParseArgs( ).' );
var Args = WScript.Arguments;
var iRtn = g_oValues.badCommand;
var szName = '';
var szJob = '';
var inx;
if ( Args.length == 0 )
{
return ShowRanges( '' );
}
szJob = Args( 0 ).toLowerCase();
if( Args.length == 1 && ( szJob == '/add' || szJob == '/del' ) )
{
szJob = '';
}
if( Args.length >= 2 )
{
szName = Args( 1 );
}
switch( szJob )
{
case '/add':
if( Args.length >= 3 && CheckPorts( Args ) == g_oValues.OK )
{
if( AddRange( Args ) == g_oValues.OK )
{
ShowRanges( szName );
}
else
{
ShowRanges( '' );
}
}
else
{
ShowUsage( Args );
}
break;
case '/del':
if( Args.length >= 2 )
{
iRtn = DelRange( szName );
ShowRanges( '' );
}
else
{
ShowUsage( Args );
}
break;
case '/show':
iRtn = ShowRanges( szName );
break;
default:
ShowUsage( Args );
}
return iRtn;
}
/**********************************************************************
* AddRange( )
* This function:
* 1. Adds a defined tunnel port range to ISA Web Proxy
* 2. calls into
* ShowError()
* 3. called by
* ParseArgs()
*
* if successful:
* 1. user-defined tunnel port range is added
* 2. returns g_oValues.OK
*
* if unsuccessful:
* 1. returns values according to failure state
*********************************************************************/
function AddRange( oArgs )
{
//WScript.Echo( 'working in AddRange().' );
var iRtn = g_oValues.OK;
var name = '';
var port1 = 0;
var port2 = 0;
name = oArgs( 1 );
port1 = oArgs( 2 );
if( oArgs.length == 3 )
{
port2 = port1;
}
else
{
port2 = oArgs( 3 );
}
if( port1 > port2 )
{
port1 = oArgs( 3 );
port2 = oArgs( 2 );
}
if( DoesItExist( name, port1, port2 ) )
{
return g_oValues.PortsExist ;
}
try
{
g_oObjects.tpRanges.AddRange( name, port1, port2 );
g_oObjects.tpRanges.Save();
WScript.Echo( g_oMessages.L_RestartSvcs_txt );
iRtn = g_oValues.OK;
}
catch( err )
{
iRtn = err.number;
ShowErrors( err, g_oMessages.L_GenError_txt +
' adding \'' + name + '\'.\r\n' );
}
return iRtn;
}
/**********************************************************************
* DelRange( )
* This function:
* 1. Removes a defined tunnel port range from ISA Web Proxy
* 2. calls into
* ShowError()
* 3. called by
* ParseArgs()
*
* if successful:
* 1. user-defined tunnel port range is removed
* 2. returns g_oValues.OK
*
* if unsuccessful:
* 1. returns values according to failure state
*********************************************************************/
function DelRange( szName )
{
//WScript.Echo( 'working in DelRange().' );
var iRtn = g_oValues.OK;
try
{
g_oObjects.tpRanges.Remove( szName );
g_oObjects.tpRanges.Save();
WScript.Echo( g_oMessages.L_RestartSvcs_txt );
iRtn = g_oValues.OK;
}
catch( err )
{
if( ToHex( err.number ) == g_oValues.lErrNotFound )
{
WScript.Echo( '\'' + szName + g_oMessages.L_TprNotFound_txt );
}
else
{
iRtn = err.number
ShowErrors( err, g_oMessages.L_GenError_txt +
' deleting \'' + szName + '\'.\r\n' );
}
}
return iRtn;
}
/**********************************************************************
* ShowRanges( )
* This function:
* 1. displays eiither a selected range or the entire tunnel port ranges
* listing depending on user-provided options
* 2. calls into
* ShowRange()
* ShowUsage()
* 3. called by
* ParseArgs()
*
* if successful:
* 1. user-defined tunnel port range or entire tunnel port definition
* is displayed
*
* if unsuccessful:
* 1. returns values according to failure state
*********************************************************************/
function ShowRanges( szName )
{
//WScript.Echo( 'working in ShowRanges(' + szName + ').' );
var cTprList = null;
var TPR = null;
var iRtn = g_oValues.OK;
if( szName == '' )
{
WScript.Echo ( g_oMessages.L_ShowList_txt );
cTprList = new Enumerator( g_oObjects.tpRanges );
for ( ; !cTprList.atEnd(); cTprList.moveNext() )
{
TPR = cTprList.item();
ShowRange( TPR );
}
return iRtn;
}
try
{
TPR = g_oObjects.tpRanges( szName );
WScript.Echo ( g_oMessages.L_ShowItem_txt + szName + ':\r\n' );
ShowRange( TPR );
}
catch( err )
{
if( ToHex( err.number ) == g_oValues.lErrNotFound )
{
WScript.Echo( '\'' + szName + g_oMessages.L_TprNotFound_txt );
iRtn = ShowRanges( '' );
}
else
{
iRtn = err.number
ShowErrors( err, g_oMessages.L_GenError_txt +
' showing \'' + szName + '\'.\r\n' );
}
}
return iRtn;
}
/**********************************************************************
* ShowRange( )
* This function:
* 1. displays the data from a selected tunnel pot range
* 2. calls into
* - none -
* 3. called by
* ShowRanges()
*
* if successful:
* 1. selected tunnel port range definition is displayed
*
* if unsuccessful:
* 1. dunno
*********************************************************************/
function ShowRange( oTPR )
{
//WScript.Echo( 'working in ShowRange(' + oTPR.Name + ' ).' );
if ( oTPR.TunnelLowPort == oTPR.TunnelHighPort )
{
WScript.Echo( '\t' + oTPR.Name + ' (single port): ' + oTPR.TunnelLowPort );
}
else
{
WScript.Echo( '\t' + oTPR.Name + ' (port range) : ' + oTPR.TunnelLowPort +
' --> ' + oTPR.TunnelHighPort );
}
}
/**********************************************************************
* CheckPorts( oArgs )
* This function:
* 1. Compares the user-specified port values to predefined limits
* 2. calls into
* - none -
* 3. called by
* ParseArgs()
*
* Returns g_oValues.OK if ports are within limits, g_oValues.badCommand
otherwise
*********************************************************************/
function CheckPorts( oArgs )
{
var iRtn = g_oValues.OK;
if( parseInt( oArgs( 2 ) ) < 1 ||
parseInt( oArgs( 2 ) ) > 65535 )
{
WScript.Echo( '\'' + oArgs( 2 ) + '\'' + g_oMessages.L_NaN_txt );
iRtn = g_oValues.badCommand;
}
if( oArgs.length >= 4 )
{
if( parseInt( oArgs( 3 ) ) < 1 ||
parseInt( oArgs( 3 ) ) > 65535 )
{
WScript.Echo( '\'' + oArgs( 3 ) + '\'' + g_oMessages.L_NaN_txt );
iRtn = g_oValues.badCommand;
}
}
return iRtn;
}
/**********************************************************************
* DoesItExist( szName, iPort1, iPort2 )
* This function:
* 1. compares the new port range data to existing definitions
* 2. calls into
* - nothing -
* 3. called by
* AddRange()
*
* 4 Returns true if definition exists in any form, false otherwise
*
* errors are not evaluated
*********************************************************************/
function DoesItExist( szName, iPort1, iPort2 )
{
//WScript.Echo( 'working in DoesItExist(' + szName + ', ' + iPort1 + ', ' + iPort2 + ').' );
var TPR;
var cTprList = new Enumerator( g_oObjects.tpRanges );
for ( ; !cTprList.atEnd(); cTprList.moveNext() )
{
TPR = cTprList.item();
if( TPR.Name.toLowerCase() == szName.toLowerCase() ||
( TPR.TunnelLowPort == iPort1 &&
TPR.TunnelHighPort == iPort2 )
)
{
WScript.Echo( g_oMessages.L_TprExists_txt + TPR.Name + '\'' );
return true;
}
}
return false;
}
/**********************************************************************
* ToHex( lValue)
* This function:
* 1. Converts a number to its hexadecimal equivalent and accounts for
* negative numbers (hResults)
* 2. calls into
* - nothing -
* 3. called by
* - nearly all functions -
*
* errors are not evaluated
*********************************************************************/
function ToHex( lValue)
{
var lNewVal;
var szHexVal;
if( lValue >= 0 && lValue < 10 )
{
szHexVal = lValue.toString();
}
else
{
lNewVal = ( lValue < 0 )? lValue + 0x100000000: lValue;
szHexVal = lNewVal.toString( 16 ).toUpperCase();
}
return szHexVal;
}
/**********************************************************************
* ShowErrors( oErr, szMessage )
* This function:
* 1. Displays szMessage and any error data if not running in MPSReports
*
* 2. calls into
* LogMessage
* 3. called by
* - nearly all functions -
*
* errors are not evaluated
*********************************************************************/
function ShowErrors( oErr, szMessage )
{
var WshShell = new ActiveXObject( 'WScript.Shell' );
var Exclamation = 48;
var YesNo = 4;
var Yes = 6;
var No = 7;
var RtnVal;
if( oErr != null )
{
szMessage += g_oMessages.L_ErrNum_txt + ToHex( oErr.number ) +
g_oMessages.L_ErrDesc_txt + oErr.description +
g_oMessages.L_CopyMsg_txt;
}
RtnVal = WshShell.Popup( szMessage, 0, g_oMessages.L_TitleMsg_txt,
Exclamation + YesNo );
if( RtnVal == No )
{
WScript.quit();
}
oErr.clear;
}
/**********************************************************************
* ShowErrors( oErr, szMessage )
* This function:
* 1. Displays szMessage and any error data if not running in MPSReports
*
* 2. calls into
* LogMessage
* 3. called by
* - nearly all functions -
*
* errors are not evaluated
*********************************************************************/
function ShowUsage( oArgs )
{
var szJob = WScript.ScriptName;
var inx;
for( inx = 0; inx < oArgs.length; inx++ )
{
szJob += ( ' ' + oArgs( inx ) );
}
if( szJob.indexOf( '?' ) == -1 )
{
WScript.Echo( g_oMessages.L_BadCommand_txt + '\'' + szJob + '\'\r\n' );
}
WScript.Echo( g_oMessages.L_Usage_txt );
}
/**********************************************************************
* Objects()
* This function holds our global objects
*
*********************************************************************/
function Objects()
{
this.ISA = null; //core ISA COM object
this.thisArray = null; //current operating array
this.tpRanges = null; //selected tunnel port range collection
}
/**********************************************************************
* Values()
* This function holds our global values
*
*********************************************************************/
function Values()
{
this.OK = 0;
this.ng_oObjects = 999; //failed to get a useful ISA COM object
this.AdminOnly = 666; //ISA Admin only
this.notISA2K = 333; //not an ISA 2000 COM
this.badCommand = 123;
this.PortsExist = 321;
this.lErrNotFound = '80070002'; //E_NOT_FOUND
this.lErrNotSupported = '800A01B6'; //method/property not supported
this.lErrExists = '800700B7'; //item already exists
}
/**********************************************************************
* Messages()
* This function holds our global messages
*
*********************************************************************/
function Messages()
{
this.divider = '###############################################################################\r\n';
this.L_Version_txt = '1.0';
this.L_TitleMsg_txt = WScript.ScriptName + ' version ' + this.L_Version_txt;
this.L_CopyMsg_txt = '\r\n\r\nHit <Ctrl>-C to copy this message to the clipboard.';
this.L_noISA_txt = '\r\nISA COM objects are not properly registered on this machine.'
this.L_notISA2K_txt = '\r\nThis is machine not an ISA 2000 Server.';
this.L_notISA2K4_txt = '\r\nThis machine is not an ISA 2004 Server.';
this.L_NoISA_txt = '\r\nThis is not an ISA Server.';
this.L_ErrUnknown_txt = '\r\nUnknown error occured...';
this.L_BadCommand_txt = '\r\n\t\tIncorrect usage: ';
this.L_NaN_txt = ' is not a valid numerical value.';
this.L_GenError_txt = 'Error encountered while ';
this.L_TprNotFound_txt = '\' was not found in the list; make sure you typed it correctly.\r\n';
this.L_TprExists_txt = 'That definiton already exists as \'';
this.L_ShowList_txt = '\r\nThis is your current Tunnel Port Range list:\r\n';
this.L_ShowItem_txt = '\r\nThis is the definition for ';
this.L_ErrNum_txt = '\r\n\r\nError Number : ';
this.L_ErrDesc_txt = '\r\nDescription : ';
this.L_ErrSource_txt = '\r\nSource : ';
this.L_RestartSvcs_txt = '\r\nThe Web Proxy service should be restarted to pick up the change.';
this.L_Usage_txt = this.divider +
'# \t\t\t' + this.L_TitleMsg_txt + '\r\n' +
this.divider +
'#\r\n' +
'# You must execute this tool on an ISA Server as:\r\n' +
'# cscript ' + WScript.ScriptName + ' [/opt1] [opt2] [port1] [port2], where:\r\n' +
'# ..opt1 (text) \'/add\', \'/del\' or \'/show\'\r\n' +
'# ..opt2 (text) The name of the tunnel port range. This is mandatory for \r\n' +
'# \'/add\' and \'/del\', optional for \'/show\'.\r\n' +
'# ..port1 (digit >0 and <65536 )\r\n' +
'# The first value of the port range. This value is mandatory for \r\n' +
'# \'/add\' and is ignored for \'/del\' and \'/show\'.\r\n' +
'# ..port2 (digit >0 and <65536 )\r\n' +
'# The second value of the port range. This value is optional for \r\n' +
'# \'/add\' and is ignored for \'/del\' and \'/show\'.\r\n' +
'#\r\n' +
'# The order of the port1 and port2 values is not important and if they\r\n' +
'# are equal, a static port definition will be created.\r\n' +
'#\r\n' +
'# Specifying no options displays all currently configured Tunnel Port entries.\r\n' +
'#\r\n' +
this.divider +
'#\r\n' +
'# Examples:\r\n' +
'#\r\n' +
'# cscript ' + WScript.ScriptName + ' /add port123 123\r\n' +
'# ..adds a single port called \'port123\' with a static value of \'123\'\r\n' +
'# cscript ' + WScript.ScriptName + ' /add port123-124 123 124\r\n' +
'# ..adds a port range called \'port123-124\' with a range of \'123\'-\'124\'\r\n' +
'# cscript ' + WScript.ScriptName + ' /del port123\r\n' +
'# ..removes a port range called \'port123\'\r\n' +
'# cscript ' + WScript.ScriptName + ' /show port123\r\n' +
'# ..displays a port range called \'port123\'\r\n' +
'# cscript ' + WScript.ScriptName + ' /show\r\n' +
'# ..displays all defined port ranges\r\n' +
'#\r\n' +
this.divider;
}
之后运行这个脚本,将会显示当前使用的端口,之后添加我们需要的端口,如添加8443:
完毕。
通过 ADS 修复 Norton 5月17日的病毒门事件
最近很多安装有 Norton 杀毒软件的系统在5月17日升级了病毒库后,系统重启便会出现蓝屏的故障,具体的原因我不再复述,大家可以通过 google 获得具体的信息,之所以要写这篇 Blog 是因为我猜想大家可能遇到了与我一样的场景,就是一些企业为了考虑到安全和管理,一些 PC 或 Server 并未安装光、软驱,因为型号比较老,还可能无法支持USB引导,那么此时修复系统就成了一个大的问题,单单的重作系统可能并不现实,所以接下来我将会向大家分享使用 ADS 服务来修复此故障的经验。
如果在环境中已经部署了 ADS 服务,那么你的修复过程将会非常之快,如果并未部署,我希望你借此次机会尝试一下部署 ADS ,你会在今后的管理中有前所未有的体验。关于 ADS 的相关资讯可以在我的 Blog 中获取。
这里假设你已经部署了 ADS,那么请将你所有的故障机器自动或手工方式引导入 ADS 代理环境,之后在 ADS 服务器上使用 ADS 任务编辑器创建一个新的任务脚本,添加一个“Download File”任务,配置“Controller File”的值为:“C:\Norton517Fix\netapi32.dll“(注意:你可以在ADS服务器的逻辑分区C上创建一个名为“Norton517Fix”的目录,并将正确的修复文件放在下面);配置“Target File”的值为“\device\harddisk0\partition1\windows\system32\netapi32.dll”;最后添加一个 reboot 任务,至此完成。其他文件参考这个!
之后向故障机分发这个任务,你的故障机器就可以正常启动了。这是一个很简单的任务脚本,大家可以深入地挖掘ADS,获得更强的信息!
OK,就是这么简单,大家可以赶紧试试!