日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

自動(dòng)化測(cè)試框架:用AOP為每一個(gè)操作寫(xiě)Log

系統(tǒng) 1823 0

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

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

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

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

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

TObject有一個(gè)方法:GetInterfaceTable,可以獲取所有接口列表。所有非常容易找到接口對(duì)應(yīng)的VTable。VTable在Delphi中并沒(méi)有明確的注釋?zhuān)强梢灾繴Table是一個(gè)指針列表,每一項(xiàng)都記錄著一個(gè)方法的“實(shí)現(xiàn)地址”。

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

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

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

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

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

自動(dòng)化測(cè)試框架:用AOP為每一個(gè)操作寫(xiě)Log

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

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

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

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

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

IMyInterface = interface(IInvokable)

procedure AAA;

end;

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

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

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

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

通過(guò)我的實(shí)踐,我的做法是通過(guò)先彈出當(dāng)前的ret地址,保存到一個(gè)數(shù)據(jù)區(qū)中。等待調(diào)用完原先的代碼后,再壓棧。而調(diào)用Writelog的時(shí)候,先保存寄存器,調(diào)用完了之后,再恢復(fù)寄存器。這是因?yàn)榧拇嫫饕部赡苁欠祷刂档牡胤健6液罄m(xù)代碼有可能優(yōu)化使用。

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

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

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

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

自動(dòng)化測(cè)試框架:用AOP為每一個(gè)操作寫(xiě)Log


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

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

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

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦?。?!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 洪湖市| 江永县| 临洮县| 万年县| 将乐县| 浦城县| 如皋市| 鞍山市| 共和县| 安化县| 南陵县| 顺平县| 迭部县| 泰州市| 柞水县| 驻马店市| 正镶白旗| 新宾| 泉州市| 恩平市| 昌江| 巨鹿县| 孟州市| 三原县| 和林格尔县| 保亭| 乌鲁木齐市| 繁昌县| 惠安县| 南昌县| 永丰县| 衡山县| 郴州市| 新邵县| 开封市| 黄山市| 贞丰县| 乐昌市| 静海县| 凤凰县| 麻阳|