亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

自動化測試框架:用AOP為每一個操作寫Log

系統(tǒng) 1943 0

在寫這個自動化測試框架的時候,我一直在留意各方面的需求。畢竟,我本人并沒有做過真正的自動化測試。管理測試方面的領(lǐng)導(dǎo),提出一個需求,就是在用例運行失敗的時候,應(yīng)該將過程記錄下來,并形成報告,Email給相關(guān)人員。

個人認(rèn)為這個需求是非常合理的。事實上,任何系統(tǒng),如果沒有輸出,那么只能停留在程序員手里。有了報表,才叫真正解決了用戶的目標(biāo)需求。

在分析這個需求的過程,我提出了針對每一個操作接口的每一個方法,進(jìn)行Log。而完成這個工作的第一方法,就想到了AOP,也就是Hook技術(shù)的應(yīng)用。因為Delphi下面并沒有對AOP的直接支持,所以考慮這個實現(xiàn),變成了一個技術(shù)研究過程。

從技術(shù)上講,本篇博客只適合了解VCL的Delphi程序員閱讀。但其間的思想,相信大家都可以借鑒。下面我的描述過程,是以我的探索過程來進(jìn)行講述的。中間會帶出相關(guān)技術(shù)點,供大家參考。

第一、接口的方法,是由類來實現(xiàn)的。框架中,已經(jīng)對所有支持的類都進(jìn)行了登記。那么,只需要在這些類型中,找到所有實現(xiàn)的接口的所有方法的地址,那么Hook就變得有可能了。

TObject有一個方法:GetInterfaceTable,可以獲取所有接口列表。所有非常容易找到接口對應(yīng)的VTable。VTable在Delphi中并沒有明確的注釋,但是可以知道VTable是一個指針列表,每一項都記錄著一個方法的“實現(xiàn)地址”。

可惜的是,我發(fā)現(xiàn)VTable本身并沒有告訴你,這個接口有多少個方法!另外,你也不能得到每一個方法的名稱,以及參數(shù)等等描述。

第二、于是我考慮到接口的RTTI。接口的RTTI,我以前是沒有使用過的。通過VCL的代碼研究,發(fā)現(xiàn)接口中有一個非常特殊的接口定義:{$M+}IInvokable = interface;{$M-}。這個接口本身并沒有添加什么服務(wù),只是使用了編譯指令M,來使得接口擁有了RTTI。

實現(xiàn)的時候,可以通過從IInvokable派生,或者直接添加編譯指令,從而獲得RTTI服務(wù)。

下面的問題是,如何使用RTTI?我們知道,Delphi中有一個單元叫TypInfo.pas,后來我發(fā)現(xiàn),其實有另外一個單元叫:IntfInfo.pas。這里面有一個方法GetIntfMetaData可以幫助你獲得RTTI。另外,值得一提的是,獲取接口類型的PTypeInfo的方法是調(diào)用TypeInfo(IMyInterface);

第三、通過MetaData分析,我們可以知道接口的方法個數(shù)以及每一個方法的詳細(xì)定義。那么,現(xiàn)在就是如何Hook了。下面是一個Object的對象實例事例圖。

自動化測試框架:用AOP為每一個操作寫Log

做左邊,有Self標(biāo)識的是對象的實例數(shù)據(jù)塊。某一個幾口指針I(yè)MyInterface指針,指向了一個VTable。而VTable中的每一個Method,都指向了一段代碼,這段代碼的前一部分,是為了計算EAX(保證將IMyInterface的地址,偏移到Self所在地址)。

分析上面的結(jié)構(gòu),再實際在CPU窗體中,調(diào)試以下接口方法的調(diào)用過程,發(fā)現(xiàn),必然和Method地址有關(guān)。因此,Hook的目標(biāo),就非常自然地變成修改VTable中的Method1的地址值。

第四、如何修改代碼?這里建議大家學(xué)習(xí)以下FastCode代碼。簡單一點,就是通過調(diào)用VirutalProtect方法,修改代碼段中內(nèi)存的訪問屬性,然后修改地址,最后再恢復(fù)回去。

顯然在Hook之前,必須聲明新的函數(shù)。

第五、新的函數(shù)并不是那么好聲明的。關(guān)注一下,接口函數(shù)的調(diào)用代碼,你會發(fā)現(xiàn)很多問題。下面舉一個簡單的例子。

IMyInterface = interface(IInvokable)

procedure AAA;

end;

假設(shè)TMyIntfImpl類實現(xiàn)了上面的接口。那么oIntfObj: IMyInterface聲明的對象,oIntfObj.AAA;的匯編代碼是如下的樣子:

上面是兩段代碼的圖片,其中[dex+$0c]指的就是$004661FD,也就是第二段代碼圖片的首地址。大家可以再聯(lián)系一下上面的示意圖理解一下地址的關(guān)鍵。

好,言歸正傳。這里注意一下,我們要修改的是[dex+$0c]里面的值。但是由于這個是call過去的。所以在call之前,會在堆棧中壓入函數(shù)返回地址。另外,在調(diào)用函數(shù)之前,還有函數(shù)參數(shù)的準(zhǔn)備。比如說Self指針的傳入到EAX中,如果本身方法還有參數(shù)的話,可能占用其他寄存器或者堆棧。

由于我們要求是Hook住所有的方法,并且所有方法的參數(shù)類型并不一定一樣。所以在call之前的代碼,是無法預(yù)計的。所以在新的函數(shù)中,必須考慮如何做到保存寄存器和做到ret時候的棧平衡。

通過我的實踐,我的做法是通過先彈出當(dāng)前的ret地址,保存到一個數(shù)據(jù)區(qū)中。等待調(diào)用完原先的代碼后,再壓棧。而調(diào)用Writelog的時候,先保存寄存器,調(diào)用完了之后,再恢復(fù)寄存器。這是因為寄存器也可能是返回值的地方。而且后續(xù)代碼有可能優(yōu)化使用。

第六、完成了匯編的編寫,還有一個問題,那就是由于每一個函數(shù)的原地址不一樣,所以必須為每一個函數(shù),定義一個代理函數(shù)。由于這些函數(shù)的地址和個數(shù)都是未定的,所以,這里就必須要用到動態(tài)創(chuàng)建代碼。

動態(tài)創(chuàng)建代碼的方法看上去簡單,申請一段空間,將那一段模板代碼地址復(fù)制過來。但是,實際情況并非如此。

首先,申請控件的時候,使用VirutalAlloc,并指定EXECUTE_READWRITE屬性。另外,要關(guān)注到原來的代碼是在代碼段執(zhí)行的,所以有些函數(shù)的地址可能只是一個偏移地址。而后申請的代碼,是在HEAP中運行的,所以,如果只是單純地復(fù)制,函數(shù)調(diào)用就會報錯了。

好了,上面講了六點關(guān)鍵因素。如果你足夠理解上面的過程,你也可以做到AOP了。這篇文章是一個純技術(shù)的,可能關(guān)心測試的會非常失望,只能說sorry了。

自動化測試框架:用AOP為每一個操作寫Log


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 99久久免费国产香蕉麻豆 | 特级毛片aaaa级毛片免费 | 神马不卡 | 国产美女在线观看 | 国产精品不卡在线观看 | 天天干天天干天天干 | 男人天堂黄色 | 亚洲欧美激情综合第一区 | 99热1| 久久亚洲综合中文字幕 | 免费日韩在线视频 | 免费在线观看的毛片 | 婷婷亚洲综合一区二区 | 一本色道久久综合亚洲精品高清 | 97国内精品久久久久久久影视 | 婷婷六月天激情 | 久久er热这里只有精品23 | 亚洲日日夜夜 | 九九久久久2 | 精品国产人成在线 | 青草香蕉精品视频在线观看 | 亚洲一区在线日韩在线深爱 | 久草美女视频 | 综合网激情 | 91成人午夜性a一级毛片 | 四虎永久免费 | 中文字幕亚洲综合久久菠萝蜜 | 国产精品久久视频 | 国产欧美久久久精品影院 | 99热久久精品最新 | 可以免费观看的一级片 | 日韩精品大片 | 色婷婷视频在线观看 | 天天做夜夜操 | 天天射天天草 | 国产综合色在线视频播放线视 | 日本在线不卡免费视频一区 | 欧美精品成人久久网站 | 手机看片日韩欧美 | 在线观看日本中文字幕 | 99爱在线视频这里只有精品 |