杀掉本地进程其实很简单,取得进程ID后,调用OpenProcess函数打开进程句柄,然后调用TerminateProcess函数就可以杀掉进程了。有些情况下并不能直接打开进程句柄,例如WINLOGON等系统进程,因为权限不够。这个时候我们就得先提升自己的进程的权限了。提升权限过程也不复杂,先调用GetCurrentProcess函数取得当前进程的句柄,然后调用OpenProcessToken打开当前进程的访问令牌,接着调用LookupPrivilegeValue函数取得你想提升的权限的值,最后调用AdjustTokenPrivileges函数给当前进程的访问令牌增加权限就可以了。一般有了SeDebugPrivilege特权后,就可以杀掉除Idle外的所有进程了。
]$iN#d|ZU OK!那如何杀掉远程进程呢?说起来有点复杂,但其实也不难。
/2uQCw&x- <1>与远程系统建立IPC连接
+Ov2`O8? <2>在远程系统的系统目录admin$\system32中写入一个文件killsrv.exe
{1lO <3>调用函数OpenSCManager打开远程系统的Service Control Manager[SCM]
0t.p1 <4>调用函数CreateService在远程系统创建一个服务,服务指向的程序是在<2>中写入的程序killsrv.exe
-8Ti*: <5>调用函数StartService启动刚才创建的服务,把想杀掉的进程的ID作为参数传递给它
m:CTPzAt <6>服务启动后,killsrv.exe运行,杀掉进程
\E4B&!m <7>清场
\ FzM4- 嗯!这样看来,我们需要两个程序了。Killsrv.exe的源代码如下:
15H6:_+=0 /***********************************************************************
~P f5ORoe Module:Killsrv.c
/!xF?OmVd Date:2001/4/27
6vy7l(% Author:ey4s
(!K_Fy@ Http://www.ey4s.org Oe]&( ***********************************************************************/
E+xuWdp.* #include
pw020}` #include
6j9)/ HP #include "function.c"
3`V1XE.; #define ServiceName "PSKILL"
'\ DSTr:N HeN~c<NuB SERVICE_STATUS_HANDLE ssh;
&<x@1, SERVICE_STATUS ss;
Ukphd$3J= /////////////////////////////////////////////////////////////////////////
qN|
fEO> void ServiceStopped(void)
VHUW]8We {
SAv<& ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
*TdnB'Gd ss.dwCurrentState=SERVICE_STOPPED;
TYgQJW? ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
83ipf"]* ss.dwWin32ExitCode=NO_ERROR;
h5zVGr ss.dwCheckPoint=0;
.m4;^S2cO ss.dwWaitHint=0;
C*70;:b SetServiceStatus(ssh,&ss);
)9nElb2 return;
H: {7X1bV }
h1"zV6U /////////////////////////////////////////////////////////////////////////
ug&[ IL~lc void ServicePaused(void)
H}0dd" {
!sSQQo2Sv ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
JqU ADm ss.dwCurrentState=SERVICE_PAUSED;
&Vk; VM`5 ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
=H5\$&xj4. ss.dwWin32ExitCode=NO_ERROR;
alFjc.~} ss.dwCheckPoint=0;
c@m5~
ss.dwWaitHint=0;
gQWd&)'muf SetServiceStatus(ssh,&ss);
D%/8{b: return;
6vzk\n }
\>/M .2 void ServiceRunning(void)
|1=
!;.# {
T6#"8qz< ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
'W. Vr4 ss.dwCurrentState=SERVICE_RUNNING;
v6a]1B ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
d.<~&.-$ ss.dwWin32ExitCode=NO_ERROR;
k)(Biz398E ss.dwCheckPoint=0;
UH`h OJ? ss.dwWaitHint=0;
?:rx1}:F SetServiceStatus(ssh,&ss);
QP I+y8N= return;
:Og:v#r8= }
u62 )QJE /////////////////////////////////////////////////////////////////////////
-#&kYK#Ph void WINAPI servier_ctrl(DWORD Opcode)//服务控制程序
,t$,idcT+ {
bMoAD.} switch(Opcode)
d}I(`%%) {
(zo^Nn9VJ case SERVICE_CONTROL_STOP://停止Service
b
B ServiceStopped();
!cEG}(|h break;
$A\m>*@ case SERVICE_CONTROL_INTERROGATE:
F_;tT%ywfx SetServiceStatus(ssh,&ss);
:K.4 n break;
!mfJpJ }
eARk
QV return;
ZDLMMXx> }
Bd0eC#UGkQ //////////////////////////////////////////////////////////////////////////////
D #2yIec //杀进程成功设置服务状态为SERVICE_STOPPED
zri}
h/{ //失败设置服务状态为SERVICE_PAUSED
/M0/-pV9 //
N> Jw void WINAPI ServiceMain(DWORD dwArgc,LPTSTR *lpszArgv)
zzpZ19"`1 {
^+70<#Xc ssh=RegisterServiceCtrlHandler(ServiceName,servier_ctrl);
"
BTE if(!ssh)
F
8yF {
%oykcf,# ServicePaused();
}E<^gAh} return;
L wJ0 }
x ,/TXTZ6 ServiceRunning();
Ps[$.h Sleep(100);
eH>#6R1- //注意,argv[0]为此程序名,argv[1]为pskill,参数需要递增1
"AueLl) //argv[2]=target,argv[3]=user,argv[4]=pwd,argv[5]=pid
c$E)P$<j if(KillPS(atoi(lpszArgv[5])))
V-O(U*] ServiceStopped();
!4#"!Md4o else
&wH:aD ServicePaused();
|lQ;ALH! return;
{kB `>VS }
G&{HTYP /////////////////////////////////////////////////////////////////////////////
&&8'0.M{ void main(DWORD dwArgc,LPTSTR *lpszArgv)
M7}Q=q\9 {
^y.UbI SERVICE_TABLE_ENTRY ste[2];
KpZ:Nh$ ste[0].lpServiceName=ServiceName;
JyBp-ii ste[0].lpServiceProc=ServiceMain;
FVWfDQ$&v ste[1].lpServiceName=NULL;
czWw~'." ste[1].lpServiceProc=NULL;
42) mM# StartServiceCtrlDispatcher(ste);
<+`(\ return;
,i}|5ozj4 }
F}?<v8#z0 /////////////////////////////////////////////////////////////////////////////
x4?10f(9= function.c中有两个函数,一个是提升权限的,一个是提供进程ID,杀进程的。代码如
o3Ot.9L 下:
f|3q^wjs
/***********************************************************************
N_wp{4 0/ Module:function.c
C9tb \?# Date:2001/4/28
@|-OJ4[5 Author:ey4s
SOh-,c\C Http://www.ey4s.org E$\~lcq ***********************************************************************/
8^ep/ b&| #include
mNmUUj9z ////////////////////////////////////////////////////////////////////////////
{aq9i BOOL SetPrivilege(HANDLE hToken,LPCTSTR lpszPrivilege,BOOL bEnablePrivilege)
`$;+g , {
@uleyB TOKEN_PRIVILEGES tp;
1 TJ0D_, LUID luid;
s&PM,BFf D9ufoa&ua if(!LookupPrivilegeValue(NULL,lpszPrivilege,&luid))
cSD{$B: {
a=]Wzlz printf("\nLookupPrivilegeValue error:%d", GetLastError() );
BR_TykP return FALSE;
D#rrW?-z }
+a)E|(cN tp.PrivilegeCount = 1;
)$M,Ul tp.Privileges[0].Luid = luid;
"cUg>a3 if (bEnablePrivilege)
i2,U,>. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
m)>&ZIXa else
T|4snU2M tp.Privileges[0].Attributes = 0;
Fe=8O ^\ // Enable the privilege or disable all privileges.
qt?*MyfV AdjustTokenPrivileges(
@s*,xHE hToken,
3}Xc71|v FALSE,
c$M%G)P &tp,
+c,[ Q sizeof(TOKEN_PRIVILEGES),
ETw]!
br (PTOKEN_PRIVILEGES) NULL,
t%0?N<9YkU (PDWORD) NULL);
:R6Q=g= // Call GetLastError to determine whether the function succeeded.
F4I6P if (GetLastError() != ERROR_SUCCESS)
85Y|CN] vQ {
X)Gp7k1w printf("AdjustTokenPrivileges failed: %u\n", GetLastError() );
v|t{1[C return FALSE;
?m%h`<wgMc }
X T>('qy return TRUE;
*>
3Qd7 }
Opg#*w%- ////////////////////////////////////////////////////////////////////////////
htJuGfDx1 BOOL KillPS(DWORD id)
4jwu'7Q {
=7/-i HANDLE hProcess=NULL,hProcessToken=NULL;
u=K2Q4 BOOL IsKilled=FALSE,bRet=FALSE;
I44s(G1jl __try
)/t6" " {
F@W*\3) pWaPC/,g if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hProcessToken))
/p`&;/V| {
Fz"ff4Bx [ printf("\nOpen Current Process Token failed:%d",GetLastError());
f05d ; __leave;
#gZ|T
M/h }
~9M!)\~ //printf("\nOpen Current Process Token ok!");
UZxmhsv if(!SetPrivilege(hProcessToken,SE_DEBUG_NAME,TRUE))
[~%`N*G {
ocA]M=3~k __leave;
wT_^'i*@I }
f=:.BR{ printf("\nSetPrivilege ok!");
5~VosUpe7 fj']?a!m if((hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,id))==NULL)
?T'][q {
;Rnhe_A. printf("\nOpen Process %d failed:%d",id,GetLastError());
QApyP CH __leave;
BSUPS+@+ }
T_hV%
//printf("\nOpen Process %d ok!",id);
.XH8YT42 if(!TerminateProcess(hProcess,1))
\_ow9vU {
Bq}x9C&< printf("\nTerminateProcess failed:%d",GetLastError());
pdz'!I __leave;
=Viy^ieN$ }
V|?WF& IsKilled=TRUE;
TUTe9;) }
{U @3yB __finally
3VO:+mT {
<0j{ $. if(hProcessToken!=NULL) CloseHandle(hProcessToken);
Ol+Kp!ocY if(hProcess!=NULL) CloseHandle(hProcess);
pM$ @m] }
A" !n1P return(IsKilled);
x mo&![P }
3)E(RyQA3 //////////////////////////////////////////////////////////////////////////////////////////////
*g7DPN$aQ OK!服务端的程序已经好了。接下来还需要一个客户端。如果通过在客户端运行的时候,把killsrv.exe COPY到远程系统上,那么就需要提供两个exe文件给用户,这样显得不是很专业,呵呵。不如我们就把killsrv.exe的二进制码作为buff保存在客户端吧,这样在运行的时候,我们直接把buff中的内容写过去,这样提供给用户一个exe文件就可以了。Pskill.c的源代码如下:
gY5l.& /*********************************************************************************************
,;iA2 ModulesKill.c
JeQ[qQ Create:2001/4/28
s (PY/{8 Modify:2001/6/23
>;lKLGJrd> Author:ey4s
zG%
|0
Http://www.ey4s.org vA>W9OI
PsKill ==>Local and Remote process killer for windows 2k
,b.n{91[]x **************************************************************************/
^#SBpLw #include "ps.h"
zy)i1d #define EXE "killsrv.exe"
z^`]7i #define ServiceName "PSKILL"
avNLV PdE>@0X?M #pragma comment(lib,"mpr.lib")
FmT
`Oa> //////////////////////////////////////////////////////////////////////////
Mtp%co )f //定义全局变量
esq<xuZM4 SERVICE_STATUS ssStatus;
%KV2<t? SC_HANDLE hSCManager=NULL,hSCService=NULL;
#x)}29%e# BOOL bKilled=FALSE;
"'{OIP char szTarget[52]=;
$h[Yz l //////////////////////////////////////////////////////////////////////////
j$PI,` BOOL ConnIPC(char *,char *,char *);//建立IPC连接函数
^Q2ZqAf^a BOOL InstallService(DWORD,LPTSTR *);//安装服务函数
rg)h5G BOOL WaitServiceStop();//等待服务停止函数
}98-5'u.X BOOL RemoveService();//删除服务函数
SMO*({/ /////////////////////////////////////////////////////////////////////////
.ZX2^)`XD int main(DWORD dwArgc,LPTSTR *lpszArgv)
xZ ;bMxZ {
3M*Y= ?pI BOOL bRet=FALSE,bFile=FALSE;
9k`~x1Y) char tmp[52]=,RemoteFilePath[128]=,
"$@,n7k szUser[52]=,szPass[52]=;
\y~)jq:d" HANDLE hFile=NULL;
'p)QyL`d DWORD i=0,dwIndex=0,dwWrite,dwSize=sizeof(exebuff);
{nRUH*(d9 I' A:J //杀本地进程
eP |)SU if(dwArgc==2)
d]7*mzw^j {
h$&rE@N| if(KillPS(atoi(lpszArgv[1])))
*.8:'F printf("\nLoacl Process %s have beed killed!",lpszArgv[1]);
o;;,iHu* else
jsm0kz printf("\nLoacl Process %s can't be killed!ErrorCode:%d",
q$ >_WF#|| lpszArgv[1],GetLastError());
(j N]OE^ return 0;
`o-*Tr }
}su6izx //用户输入错误
NbDda/7ki else if(dwArgc!=5)
Czh8zB+r {
VM;g+RRq printf("\nPSKILL ==>Local and Remote Process Killer"
%!wq:~B1 "\nPower by ey4s"
?!m ma\W "\nhttp://www.ey4s.org 2001/6/23"
j],&z^O$ "\n\nUsage:%s <==Killed Local Process"
R0IF' "\n %s <==Killed Remote Process\n",
;tr)=)q& lpszArgv[0],lpszArgv[0]);
Iaa|qJ4 return 1;
npj5U/
}
RAOKZ~` //杀远程机器进程
L9J;8+ge strncpy(szTarget,lpszArgv[1],sizeof(szTarget)-1);
bL
MkPty strncpy(szUser,lpszArgv[2],sizeof(szUser)-1);
Nh:4ys!P strncpy(szPass,lpszArgv[3],sizeof(szPass)-1);
Ij,Yuo /&6Q) //将在目标机器上创建的exe文件的路径
'\'7yN' sprintf(RemoteFilePath,"\\%s\admin$\system32\%s",szTarget,EXE);
+{^'i P __try
d~/xGB`< {
`&,_xUA //与目标建立IPC连接
s
kY0 \V if(!ConnIPC(szTarget,szUser,szPass))
H<z30r/-w {
Di])<V printf("\nConnect to %s failed:%d",szTarget,GetLastError());
pLo;#e8'f return 1;
m9I(TOw }
tnJ`D4 printf("\nConnect to %s success!",szTarget);
N.vG]%1" //在目标机器上创建exe文件
d3(+ztmG! 2{gwY85: hFile=CreateFile(RemoteFilePath,GENERIC_ALL,FILE_SHARE_READ|FILE_SHARE_WRIT
T7s+9CE E,
%|bN@@ NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
R/rcXX7% if(hFile==INVALID_HANDLE_VALUE)
*NF&Y {
WS5"!vz printf("\nCreate file %s failed:%d",RemoteFilePath,GetLastError());
8nf4Jk8r __leave;
`U!(cDY }
d4V 2[TX //写文件内容
wvX"D0eVn while(dwSize>dwIndex)
Sn0Xl3yr
{
x@Sra@ |&(H^<+Xp if(!WriteFile(hFile,&exebuff[dwIndex],dwSize-dwIndex,&dwWrite,NULL))
wNbTM.@ {
x6jm-n printf("\nWrite file %s
'%:5axg?] failed:%d",RemoteFilePath,GetLastError());
y^, "gD __leave;
@X|ok*v` }
"wF*O"WQo dwIndex+=dwWrite;
G1A$PR }
01-p
`H+ //关闭文件句柄
M Ey1~h/ CloseHandle(hFile);
0:HC;J bFile=TRUE;
^?#@[4?" //安装服务
:^K~t!@ if(InstallService(dwArgc,lpszArgv))
^sJ1 ^LT {
vW9^hbdx //等待服务结束
@f-0X1C."N if(WaitServiceStop())
uAC hu] {
IM$'J //printf("\nService was stoped!");
`Nkx7Z~w: }
0H;"5 else
XL=2wh {
>Zi|$@7t- //printf("\nService can't be stoped.Try to delete it.");
Z=0iPy,m> }
CzK%x?~] Sleep(500);
LPsh?Ca?N //删除服务
M_yZR^;^- RemoveService();
o@Dk%LxP }
(q055y }
<?{ SU
__finally
}ZxW"5oq {
:/ ~):tM //删除留下的文件
hLu&lY if(bFile) DeleteFile(RemoteFilePath);
;]|m((15G //如果文件句柄没有关闭,关闭之~
.n?5}s+q if(hFile!=NULL) CloseHandle(hFile);
0^-z?Kb<} //Close Service handle
FuIWiO( if(hSCService!=NULL) CloseServiceHandle(hSCService);
s9^"wN YQ //Close the Service Control Manager handle
QSO5 z2| if(hSCManager!=NULL) CloseServiceHandle(hSCManager);
"-G.V#zI //断开ipc连接
fJ,8g/f8 wsprintf(tmp,"\\%s\ipc$",szTarget);
J+-,^8) WNetCancelConnection2(tmp,CONNECT_UPDATE_PROFILE,TRUE);
\UVT_=Y if(bKilled)
P1M|f4* printf("\nProcess %s on %s have been
Z?.:5# killed!\n",lpszArgv[4],lpszArgv[1]);
F o--PtY`p else
k
i~Raa/e printf("\nProcess %s on %s can't be
cj,&&3sbV killed!\n",lpszArgv[4],lpszArgv[1]);
K*hf(w9="% }
T#ecLD# return 0;
2d,wrC<'$ }
e!O &~#'h} //////////////////////////////////////////////////////////////////////////
M$DwQ}Z BOOL ConnIPC(char *RemoteName,char *User,char *Pass)
$6qR/#74 {
>EPaZp6 NETRESOURCE nr;
i[V,IP + char RN[50]="\\";
BbXmT"@ Ip1QVND strcat(RN,RemoteName);
2}W6{T' strcat(RN,"\ipc$");
0O@[on;Bd CJ37:w{%*Y nr.dwType=RESOURCETYPE_ANY;
B$iMU?B3 nr.lpLocalName=NULL;
Br?++\ nr.lpRemoteName=RN;
~cWLu5 nr.lpProvider=NULL;
cHfK-R ]}*G[[
^p if(WNetAddConnection2(&nr,Pass,User,FALSE)==NO_ERROR)
J\,@Bm|1n{ return TRUE;
7]0\[9DyJ else
qX}dbuDE"P return FALSE;
2l;ge>DJ }
LS?` {E
/////////////////////////////////////////////////////////////////////////
>xk:pL*o` BOOL InstallService(DWORD dwArgc,LPTSTR *lpszArgv)
oQE_?">w {
3M5=@Fwkr BOOL bRet=FALSE;
Wl}G[>P __try
`pn-fk {
ixUiXP //Open Service Control Manager on Local or Remote machine
`K ~>!d_ hSCManager=OpenSCManager(szTarget,NULL,SC_MANAGER_ALL_ACCESS);
6Wcn(h8%* if(hSCManager==NULL)
s?z=q%-p {
oWn_3gzw; printf("\nOpen Service Control Manage failed:%d",GetLastError());
D0"yZp} __leave;
#&HarBxx }
)xXrs^ //printf("\nOpen Service Control Manage ok!");
./z"P]$ //Create Service
]MBJ"1F hSCService=CreateService(hSCManager,// handle to SCM database
TO8\4p*tE ServiceName,// name of service to start
P7^TRrMF ServiceName,// display name
iz$v8;w SERVICE_ALL_ACCESS,// type of access to service
~=aI2(b SERVICE_WIN32_OWN_PROCESS,// type of service
6 I>xd SERVICE_AUTO_START,// when to start service
G=0}IPfp SERVICE_ERROR_IGNORE,// severity of service
nY.Umj failure
pNk,jeo EXE,// name of binary file
^U|CNB%. NULL,// name of load ordering group
^Ypb"Wx8 NULL,// tag identifier
_@}MGWlAPt NULL,// array of dependency names
<CdG[Ih NULL,// account name
RaJ}>e NULL);// account password
aF_ZV bS //create service failed
y0Q/B|&[ if(hSCService==NULL)
xHR+(( {
$T@xnZ //如果服务已经存在,那么则打开
:+X2>Lu$FA if(GetLastError()==ERROR_SERVICE_EXISTS)
tue%L]hc {
bU@>1>b6lE //printf("\nService %s Already exists",ServiceName);
1+y6W1m^R //open service
&Cn9
k3E\R hSCService = OpenService(hSCManager, ServiceName,
)y
[[Se SERVICE_ALL_ACCESS);
EKI+Dq, if(hSCService==NULL)
fuwp p {
ag*Hs<gi printf("\nOpen Service failed:%d",GetLastError());
?F_;~ __leave;
azATKH+j }
I'wk/ //printf("\nOpen Service %s ok!",ServiceName);
d}A2I }
vo^9qSX
f else
"Ezr- 4 {
5d>YE printf("\nCreateService failed:%d",GetLastError());
3C5D~9v __leave;
EIl$"^- }
>@92K]J }
w1/T>o //create service ok
MsVI <+JZ else
?5+KHG*) {
z ]4g`K+ //printf("\nCreate Service %s ok!",ServiceName);
sGm(Aax*0 }
6d?2{_} , Z6
|'k:R8 // 起动服务
qS`|=5f if ( StartService(hSCService,dwArgc,lpszArgv))
F(kRAe; {
26klW:2* //printf("\nStarting %s.", ServiceName);
?tM]. \ Sleep(20);//时间最好不要超过100ms
DcvmeGl while( QueryServiceStatus(hSCService, &ssStatus ) )
():?FJM {
5In8VE
!P if ( ssStatus.dwCurrentState == SERVICE_START_PENDING)
GzE3B';g {
7ump:| printf(".");
#j~FA3O Sleep(20);
jH#^O;A }
R5~vmT5W else
;ZW}47:BS6 break;
>[3,qP]E }
88LbO(q\d if ( ssStatus.dwCurrentState != SERVICE_RUNNING )
OgpH{" printf("\n%s failed to run:%d",ServiceName,GetLastError());
zk_hDhg&' }
~k<31 ez else if(GetLastError()==ERROR_SERVICE_ALREADY_RUNNING)
%}AY0fg?T {
V<R+A* gY: //printf("\nService %s already running.",ServiceName);
F/,<dNJ }
o[+|n[aT)3 else
Xcpm?aTo {
}\qdow- printf("\nStart Service %s failed:%d",ServiceName,GetLastError());
FZI 4?YD?< __leave;
h],%va[ }
7)8}8tY^{ bRet=TRUE;
k=/|?% }//enf of try
B0SmE_u_N __finally
Ej3hdi) {
8t
35j return bRet;
h[)aRo }
^+l\YB7pD return bRet;
VX@G}3Ck }
|ssIUJ /////////////////////////////////////////////////////////////////////////
UE;)mZ=l| BOOL WaitServiceStop(void)
= 8e8!8 {
] ,aAzjZ BOOL bRet=FALSE;
F!cAaL1 //printf("\nWait Service stoped");
FwzA_
nn while(1)
') cgx9 {
gBS#Z. Sleep(100);
SX<mj if(!QueryServiceStatus(hSCService, &ssStatus))
!rqR]nd {
Tsp-]-) printf("\nQueryServiceStatus failed:%d",GetLastError());
<UL|%9=~ break;
s5
'nWMo }
t'/;Z: if(ssStatus.dwCurrentState==SERVICE_STOPPED)
)CTM {
~,65/O bKilled=TRUE;
)GKgK;=~ bRet=TRUE;
@{a-IW3 break;
#C7j|9Ew1] }
P&^7wud-sb if(ssStatus.dwCurrentState==SERVICE_PAUSED)
U:jf9L2 {
?a9k5@s //停止服务
XFe7qt;% bRet=ControlService(hSCService,SERVICE_CONTROL_STOP,NULL);
pREYAZh break;
{4q:4i }
?7ZlX?D[ else
cb,sb^- {
zQ+t@;g1 //printf(".");
.O.R continue;
q,&T$Tw }
OIT;fKl9 }
wdV?&W+ return bRet;
B\&