API HOOK 实战解决War FTP无法重命名覆盖已经存在文件的问题之最终篇
看过这个系列的朋友一定都会发现,两种方法都存在着缺憾,如果只是用于普通环境也罢,但我编写此程序的目的是要用于windows 2003的服务器生产环境的,没有达到理想前都不可能轻易部署上去。
由于AutoIt3的版主不太可能再写一次hook的代码,于是只好继续研究Deviare组件。
之前碰到的问题是用VB调用时速度偏慢,而且CPU占用比较高。在Deviare的论坛上逛时,发现还可以用VBScript来编写,于是将我原来的代码稍微进行了修改。然后我惊奇地发现VBScript调用Deviare组件进行Hook时,速度和AutoIt3几乎相同,而且CPU也恢复正常了!
于是我在Deviare论坛发问,得到的答复是由于VB 6.0的单线程设计架构,导致在处理事件时会出现一些问题。
看来继续使用VB 6.0是无法完成了,那么我该用何种语言完成呢?
我的程序需要完成如下目标:
1、能够Hook以系统服务运行的war-ftpd.exe进程的CreateFileW函数,并修改相关的参数值;
2、程序要以GUI方式运行,不能用CUI方式运行;
要能够完成第1点,目前只能使用Deviare组件。
要能够完成第2点,VBScript语言是做不到,至少做起来相当麻烦。
Deviare论坛版主,在给我的问题进行答复后没有多久,更新了Deviare组件版本到2.0.6.在其中我发现example目录增加了一些内容,除了原来的C#的例子,又增加了VBScript和Python的例子。于是我开始了Python的研究之路。
Python说实话是一个相当不错的语言,各种库函数都相当丰富。根据版主提供的Python例子,我很快就编写好了CUI的版本。
但在研究Python编写GUI程序时,我还是碰壁不少。先后研究了Python自带的Tkinter和wxpython,但发现这些GUI库对于事件的处理似乎与VB还是有点不同,搞了几天也没有弄出个啥。
我当然可以继续静下心来研究Python,并最终完成第2点目标,但我不知道会要多久,我不可能一直在这个问题上面耗下去。
碰巧,在研究Python时,偶然在某个帖子看到有人讨论到了FreeBasic程序如何转换为Python。于是我开始关注了FreeBasic。不看不知道,一看吓一跳。原来世界上除了VB之外还有那么多Basic的产品存在,而且似乎都在某些方面要远远好于VB 6.0.
我从中挑选了几个比较有名的开始研究,先后看过FreeBasic、PowerBasic、RealBasic,这些软件要实现GUI我觉得问题都不大,但用起来都没有VB那么方便,另外在使用COM组件时,显然离VB还是有不小的差距。所以说为什么VB能成为Basic事实上的标准,是有它的道理的。微软的亲生儿子怎么也不会比其它要差吧?
在辗转反侧之间,又是几周过去了。想想也真是沮丧,如果当初把VC学好,把Windows编程学精,那么我何至于如此呢?但世界上的问题那么多,我也不可能有那么多精力去一一研究仔细,尽量使用现成的东西,尽量踩在前人的肩膀上,那才是我们应该学习的。
在各种编程语言中挑选时,我突然想到了“快手——aauto”。这个语言作者最初是写按键精灵的,后来开发了更强大的aauto语言。我以前在研究AutoIt之前实际上研究过aauto,只是后来由于其它原因中断了。于是我赶快把抛弃的aauto捡起来,发现要实现我的目标还真是简单。
用来大概2天时间,我完成了我的目标,另外增加了其它的一些功能,完善了我的程序。程序代码如下:
import win.ui;
import win.util.tray;
import win.ui.menu;
import com.picture;
import com;
import process;
import mouse;
import win.ui.atom;
/*DSG{{*/
mainForm = ..win.form( right=600;bottom=400;parent=...;text="WarFTP参数拦截程序";max=false )
mainForm.add(
listview={ bgcolor=16777215;bottom=380;right=573;left=26;top=18;z=1;vscroll=1;fullRow=1;edge=1;cls="listview" }
)
/*}}*/
/*listviewColorize{{*/
import util;
listviewColorize = (function () {
var NMLVCUSTOMDRAW = class {
struct nmcd = {
struct hdr = ..win.ui.NMHDR();
INT dwDrawStage;
pointer hdc;
struct rc = ::RECT();
int dwItemSpec;
INT uItemState;
int lItemlParam;
};
INT clrText;
INT clrTextBk;
int iSubItem;
INT dwItemType;
INT clrFace;
int iIconEffect;
int iIconPhase;
int iPartId;
int iStateId;
struct rcText = ::RECT();
INT uAlign;
}
return function (listview) {
listview._colors = ..table.array(listview.count, 0);
listview.onnotify = function (id,code,ptr) {
if (code === 0xFFFFFFF4/*_NM_CUSTOMDRAW*/) {
var nmlvcd = NMLVCUSTOMDRAW();
raw.convert(ptr, nmlvcd);
select (nmlvcd.nmcd.dwDrawStage) {
case 0x1/*_CDDS_PREPAINT*/
return 0x20/*_CDRF_NOTIFYITEMDRAW*/
case 0x10001/*_CDDS_ITEMPREPAINT*/ {
raw.mixin(ptr, nmlvcd, { clrText = owner._colors[nmlvcd.nmcd.dwItemSpec + 1] });
return 0/*_CDRF_DODEFAULT*/;
}
}
}
}
listview.setItemColor = function (item, col) {
owner._colors[item] = col;
}
listview.addItem = ..util.connect(listview, listview.addItem,
function (text, ind, ...) {
if (ind)
..table.insert(owner._colors, 0, ind);
else
..table.push(owner._colors, 0);
}
);
listview.delItem = ..util.connect(listview, listview.delItem,
function (item) {
..table.remove(owner._colors, item);
}
);
}
})();
/*}}*/
var atom,hwnd/*冲突窗口的句柄,该函数会自动激活此窗口*/ = mainForm.atom("6D949DCA-7233-46D3-B55A-9E1B2FC758C6");
if(!atom){
/*为窗口设置原子值可以避免一个程序重复运行多个实例*/
win.quitMessage()
return
}
listviewColorize(mainForm.listview);
var spyMgr
var processName="war-ftpd.exe"
var serviceName = "WARSVR"
var myHook,event_handler,spyMgr_events
EmptyWorkingSet := ::Psapi.api("EmptyWorkingSet","bool(pointer handle)")
emptymem=function(){
var handle = process.OpenProcess(0x1f0fff,false,process.getId())
EmptyWorkingSet(handle)
raw.closehandle(handle)
}
//在listview写日志
//flag=0 —— 正常日志
//flag=1 —— 错误日志
function writelog(msg,flag=0){
if(mainForm.listview.count>5000){
mainForm.listview.clear()
}
var item = mainForm.listview.addItem({tostring(time());msg})
if( flag == 1 ){
mainForm.listview.setItemColor(item, 0x0000FF)
}
mainForm.listview.ensureVisible(mainForm.listview.count)
mainForm.listview.redraw()
return
}
//检查进程
function CheckProcess(processName) {
var objWMIService,colProcessList,objProcess
var strComputer
var pCount
strComputer = "."
objWMIService = com.GetObject("winmgmts:{impersonationLevel=impersonate}!\\" ++ strComputer ++ "\root\cimv2")
colProcessList = objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Name='" ++ processName ++ "'")
pCount = colProcessList.Count
if(pCount > 1) {
return -1
}
elseif(pCount == 0) {
return -2
}
elseif(pCount == 1) {
return -3
}
}
//结束进程
function KillProcess(processName) {
var objWMIService,colProcessList,objProcess
var strComputer
strComputer = "."
objWMIService = com.GetObject("winmgmts:{impersonationLevel=impersonate}!\\" ++ strComputer ++ "\root\cimv2")
colProcessList = objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Name='" ++ processName ++ "'")
for(i,objProcess in com.each(colProcessList)){
objProcess.Terminate()
}
}
//结束服务
function StopService(serviceName) {
var objWMIService,colServiceList,objService
var strComputer
var iStatus
strComputer = "."
objWMIService = com.GetObject("winmgmts:{impersonationLevel=impersonate}!\\" ++ strComputer ++ "\root\cimv2")
colServiceList = objWMIService.ExecQuery("SELECT * FROM Win32_Service WHERE Name='" ++ serviceName ++ "'")
for(i, objService in com.each(colServiceList)) {
if (objService.started == true) {
iStatus = objService.StopService()
//等待10秒钟以便结束服务
//sleep(10000)
win.delay(10000)
if( iStatus != 0 ) {
writelog("停止服务【" ++ serviceName ++ "】失败")
return false
}
else {
writelog("成功停止服务【" ++ serviceName ++ "】")
return true
}
}
else {
writelog("服务【" ++ serviceName ++ "】未启动")
return true
}
}
}
//启动服务
function StartService(serviceName) {
var objWMIService,colServiceList,objService
var strComputer
var iStatus
strComputer = "."
objWMIService = com.GetObject("winmgmts:{impersonationLevel=impersonate}!\\" ++ strComputer ++ "\root\cimv2")
colServiceList = objWMIService.ExecQuery("SELECT * FROM Win32_Service WHERE Name='" ++ serviceName ++ "'")
for(i, objService in com.each(colServiceList)) {
if (tonumber(objService.started) == false) {
iStatus = objService.StartService()
//等待10秒钟以便启动服务
//sleep(10000)
win.delay(10000)
if( iStatus != 0 ) {
writelog("启动服务【" ++ serviceName ++ "】失败")
return false
}
else {
writelog("成功启动服务【" ++ serviceName ++ "】")
return true
}
}
else {
writelog("服务【" ++ serviceName ++ "】已经启动")
return true
}
}
}
//*********************************************************主程序开始************************************************************************
RegisterWindowMessage := ::User32.api( "RegisterWindowMessageA", "Long(String lpString)");
msgTaskbarRestart = RegisterWindowMessage("TaskbarCreated")
tray = win.util.tray(mainForm,130) //创建托盘图标
tray.message = 0x400+9981/*_WM_TRAYMESSAGE*/
tray.tip = "WarFTP参数拦截程序" //设置鼠标提示
popmenu = win.ui.popmenu(mainForm)
popmenu.add('隐藏',function(id){
stat = win.isVisible(mainForm.hwnd)
if(stat){
mainForm.show(false);
popmenu.setString(1,"显示")
}
else {
mainForm.show(true)
popmenu.setString(1,"隐藏")
}
})
popmenu.add()//分隔线
popmenu.add('退出',function(id){
mainForm.close()
})
mainForm.wndproc = function(hwnd,message,wParam,lParam){
select(message) {//判断消息类型
case (0x10/*_WM_CLOSE*/){
//询问是否退出
var ok = mainForm.msgboxTest("退出此程序将导致WarFTP出现问题,请确认是否退出?","确认信息")
if(ok == false){
return wParam
}
tray.delete()
}
case( 0x400+9981/*_WM_TRAYMESSAGE*/ ) { //托盘图标消息
try{
//右键单击弹出菜单
if( lParam == 0x205/*_WM_RBUTTONUP*/ ){
x,y = mouse.getPos()
//弹出托盘菜单以前,一定要前置主窗口中,不然不点击菜单不会消失
win.setForeground(mainForm.hwnd)
popmenu.popup( x,y,true )
}
//左键双击
if(lParam == 0x203/*_WM_LBUTTONDBLCLK)*/){
stat = win.isVisible(mainForm.hwnd)
if(stat){
mainForm.show(false)
popmenu.setString(1,"显示")
tray.pop("程序隐藏到系统托盘","程序提示")
}
else {
mainForm.show(true)
popmenu.setString(1,"隐藏")
}
}
}
catch(e){
writelog("错误信息:" ++ e,1)
}
}
case( 0x112/*_WM_SYSCOMMAND*/ ){ //系统命令消息
if( wParam == 0xF020/*_SC_MINIMIZE*/ ){ //用户点击了最小化按钮
//import win.util.tray
//tray = win.util.tray(mainForm) //创建托盘图标
tray.pop("程序隐藏到系统托盘","程序提示")
mainForm.show(false) //隐藏窗口
popmenu.setString(1,"显示")
emptymem() //释放内存
return true//阻击默认消息传递,取消最小化过程
}
}
case(msgTaskbarRestart){ //任务栏重建
//win.util.tray.Shell_NotifyIcon(0x0/*_NIM_ADD*/,tray.m_tnd)
//writelog("系统托盘重建")
tray.reset()
}
}
//无返回值则继续调用默认回调函数
}
//增加listview的列
mainForm.listview.insertColumn("时间",120)
mainForm.listview.insertColumn("信息",-1)
//让主窗口先显示,以便在listview输出内容
mainForm.show( true )
writelog("程序启动")
//查找是否存在 war-ftpd.exe 进程
lPID = CheckProcess(processName)
if(lPID == -1) {
writelog("找到多个【" ++ processName ++ "】进程")
writelog("结束【" ++ processName ++ "】进程...")
KillProcess(processName)
//sleep(1000)
win.delay(1000)
}
elseif( lPID == -2) {
writelog("未找到【" ++ processName ++ "】进程")
}
elseif( lPID == -3) {
writelog("找到1个【" ++ processName ++ "】进程")
writelog("结束【" ++ processName ++ "】对应的服务程序...")
StopService(serviceName)
}
writelog("启动【" ++ processName ++ "】对应的服务程序...")
StartService(serviceName)
writelog("开始创—eviareCom组件...")
spyMgr = com.CreateObject("DeviareCOM.NktSpyMgr")
spyMgr.DatabasePath = ""
spyMgr.AgentPath = ""
spyMgr.Initialize()
writelog("DeviareCom组件创建完毕")
lPID = spyMgr.FindProcessId(processName)
if(lPID == 0){
writelog("未找到【" ++ processName ++ "】进程",1)
mainForm.msgboxErr("未找到【" ++ processName ++ "】进程","错误")
mainForm.close()
}
else {
writelog("【" ++ processName ++ "】进程PID:" ++ tostring(lPID))
}
writelog("开始Hook【" ++ processName ++ "】的 kernel32.dll!CreateFileW 函数..." )
myHook = spyMgr.CreateHook("kernel32.dll!CreateFileW", 0)
myHook.Attach(lPID,true)
myHook.Hook(true)
//sleep(2000)
win.delay(2000)
if( tonumber(myHook.State(lPID)) == 2 ){
writelog("Hook成功")
emptymem() //释放内存
}
else{
writelog("Hook未成功,状态为:" ++ tostring(myHook.State(lPID)),1)
mainForm.msgboxErr("Hook未成功","错误")
mainForm.close()
}
//注册事件
spyMgr_events = {
OnFunctionCalled = function(hook, proc, callInfo) {
var paramEnum,param1,param4
//writelog("Function called: " ++ hook.FunctionName ++ " within process " ++ proc.Path ++ " " ++ callInfo.ToString())
paramEnum = callInfo.Params()
param1 = paramEnum.GetAt(0)
param4 = paramEnum.GetAt(4)
if( tonumber(param4.LongVal) == 1 ){
//writelog("发现要修改的参数值")
if( callInfo.IsPreCall == true ){
writelog(tostring(param1.Value))
param4.LongVal = 2
}
}
}
}
res, cookie = com.Connect(spyMgr, spyMgr_events)
/***
import config;
mainForm.bindConfig( config.mainForm,{
edit = "text"; //绑定edit控件的text属性值到配置文件
radiobutton = "checked";
checkbox = "checked";
combobox = "selIndex";
} );
***/
win.loopMessage();
至此,我的最初目标已经可以完美的实现。我也不想再花更多精力放在此问题上面。
经过我的几天测试,我的程序运行稳定,唯一的小缺憾是Deviare组件加载时会加载API的描述数据库,占用大概90M的内存。
Deviare版主答复说以后版本会增加一个可以减少数据库的版本,以减少内存占用。
如果将来有时间有精力的话,我希望用某种语言,使用原始代码直接完成我的目标。
不过这是将来的事情了,现在我已经满足了。