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

2021-08-22 09:08:03 字數 2475 閱讀 1358

在寫這個自動化測試框架的時候,我一直在留意各方面的需求。畢竟,我本人並沒有做過真正的自動化測試。管理測試方面的領導,提出乙個需求,就是在用例執行失敗的時候,應該將過程記錄下來,並形成報告,email給相關人員。

個人認為這個需求是非常合理的。事實上,任何系統,如果沒有輸出,那麼只能停留在程式設計師手裡。有了報表,才叫真正解決了使用者的目標需求。

在分析這個需求的過程,我提出了針對每乙個操作介面的每乙個方法,進行log。而完成這個工作的第一方法,就想到了aop,也就是hook技術的應用。因為delphi下面並沒有對aop的直接支援,所以考慮這個實現,變成了乙個技術研究過程。

第一、介面的方法,是由類來實現的。框架中,已經對所有支援的類都進行了登記。那麼,只需要在這些型別中,找到所有實現的介面的所有方法的位址,那麼hook就變得有可能了。

tobject有乙個方法:getinte***cetable,可以獲取所有介面列表。所有非常容易找到介面對應的vtable。vtable在delphi中並沒有明確的注釋,但是可以知道vtable是乙個指標列表,每一項都記錄著乙個方法的「實現位址」。

可惜的是,我發現vtable本身並沒有告訴你,這個介面有多少個方法!另外,你也不能得到每乙個方法的名稱,以及引數等等描述。

第二、於是我考慮到介面的rtti。介面的rtti,我以前是沒有使用過的。通過vcl的**研究,發現介面中有乙個非常特殊的介面定義:iinvokable = inte***ce;。這個介面本身並沒有新增什麼服務,只是使用了編譯指令m,來使得介面擁有了rtti。

實現的時候,可以通過從iinvokable派生,或者直接新增編譯指令,從而獲得rtti服務。

下面的問題是,如何使用rtti?我們知道,delphi中有乙個單元叫typinfo.pas,後來我發現,其實有另外乙個單元叫:intfinfo.pas。這裡面有乙個方法getintfmetadata可以幫助你獲得rtti。另外,值得一提的是,獲取介面型別的ptypeinfo的方法是呼叫typeinfo(imyinte***ce);

第三、通過metadata分析,我們可以知道介面的方法個數以及每乙個方法的詳細定義。那麼,現在就是如何hook了。下面是乙個object的物件例項事例圖。

做左邊,有self標識的是物件的例項資料塊。某乙個幾口指標imyinte***ce指標,指向了乙個vtable。而vtable中的每乙個method,都指向了一段**,這段**的前一部分,是為了計算eax(保證將imyinte***ce的位址,偏移到self所在位址)。

分析上面的結構,再實際在cpu窗體中,除錯以下介面方法的呼叫過程,發現,必然和method位址有關。因此,hook的目標,就非常自然地變成修改vtable中的method1的位址值。

第四、如何修改**?這裡建議大家學習以下fastcode**。簡單一點,就是通過呼叫virutalprotect方法,修改**段中記憶體的訪問屬性,然後修改位址,最後再恢復回去。

顯然在hook之前,必須宣告新的函式。

第五、新的函式並不是那麼好宣告的。關注一下,介面函式的呼叫**,你會發現很多問題。下面舉乙個簡單的例子。

imyinte***ce = inte***ce(iinvokable)

procedure aaa;

end;

假設tmyintfimpl類實現了上面的介面。那麼ointfobj: imyinte***ce宣告的物件,ointfobj.aaa;的彙編**是如下的樣子:

上面是兩段**的,其中[dex+$0c]指的就是$004661fd,也就是第二段**的首位址。大家可以再聯絡一下上面的示意圖理解一下位址的關鍵。

好,言歸正傳。這裡注意一下,我們要修改的是[dex+$0c]裡面的值。但是由於這個是call過去的。所以在call之前,會在堆疊中壓入函式返回位址。另外,在呼叫函式之前,還有函式引數的準備。比如說self指標的傳入到eax中,如果本身方法還有引數的話,可能占用其他暫存器或者堆疊。

由於我們要求是hook住所有的方法,並且所有方法的引數型別並不一定一樣。所以在call之前的**,是無法預計的。所以在新的函式中,必須考慮如何做到儲存暫存器和做到ret時候的棧平衡。

通過我的實踐,我的做法是通過先彈出當前的ret位址,儲存到乙個資料區中。等待呼叫完原先的**後,再壓棧。而呼叫writelog的時候,先儲存暫存器,呼叫完了之後,再恢復暫存器。這是因為暫存器也可能是返回值的地方。而且後續**有可能優化使用。

第六、完成了彙編的編寫,還有乙個問題,那就是由於每乙個函式的原位址不一樣,所以必須為每乙個函式,定義乙個**函式。由於這些函式的位址和個數都是未定的,所以,這裡就必須要用到動態建立**。

動態建立**的方法看上去簡單,申請一段空間,將那一段模板**位址複製過來。但是,實際情況並非如此。

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

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

在寫這個自動化測試框架的時候,我一直在留意各方面的需求。畢竟,我本人並沒有做過真正的自動化測試。管理測試方面的領導,提出乙個需求,就是在用例執行失敗的時候,應該將過程記錄下來,並形成報告,email給相關人員。個人認為這個需求是非常合理的。事實上,任何系統,如果沒有輸出,那麼只能停留在程式設計師手裡...

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

在寫這個自動化測試框架的時候,我一直在留意各方面的需求。畢竟,我本人並沒有做過真正的自動化測試。管理測試方面的領導,提出乙個需求,就是在用例執行失敗的時候,應該將過程記錄下來,並形成報告,email給相關人員。個人認為這個需求是非常合理的。事實上,任何系統,如果沒有輸出,那麼只能停留在程式設計師手裡...

pytest allure自動化測試框架(一)

pytest是python最流程的單測框架之一。在本文中,我們將會介紹pytest的特點,功能和使用。安裝python依賴庫 pip3 install pytest pip3 install pytest allure adapto 檔案目錄 三個pytest測試指令碼 注 一定要以test 或xx...