<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/mobile/13486

                    Author:leonnewton

                    0x00 前言


                    DEXLabs發表過題為《Detecting Android Sandboxes》的博客,文章提出了一個檢測Android沙箱的方法,并附了PoC。本文對原文的內容進行介紹,并加上筆者自己實際實驗的結果及討論。

                    0x01 Qemu的二進制翻譯機制


                    看下百度百科對二進制翻譯的解釋:

                    二進制翻譯(Binary Translation)是一種直接翻譯可執行二進制程序的技術,能夠把一種處理器上的二進制程序翻譯到另外一種處理器上執行。

                    Qemu使用二進制翻譯技術把要翻譯的native代碼,比如ARM下的代碼,翻譯到host系統下一樣或者不同的指令集,比如x86指令集。跟指令集模擬技術相比,二進制翻譯運行速度更快,因為已經翻譯的代碼塊可以進行cache然后直接執行。下面是Qemu二進制翻譯過程的流程圖:

                    p1

                    從圖中可以看出,當遇到分支指令的時候,就會對后面的代碼進行處理。后面的代碼地址可以在分支指令里面找到,所以會先去找是否有代碼的cache。當命中的時候,代碼塊已經有cache了,代碼已經翻譯過,所以直接執行就行了。當沒有命中的時候,翻譯函數就會去翻譯代碼,一直翻譯到遇到下一個分支指令。然后把翻譯好的代碼放到cache中接著執行。

                    0x02 Qemu的一個優化


                    物理CPU會在每執行一條指令以后,就把程序計數器加一,程序計數器永遠都是最新的值。那么對于一個虛擬的CPU來說,也應該是在每執行完代碼中的一條指令然后將程序計數器加一,這樣來保證程序計數器是最新的值。然而,由于被翻譯的代碼是在本地執行的,也就是模擬出來的CPU,所以只有在原來代碼需要訪問程序計數器的時候,才需要返回一個最新的正確的值(因為這不會影響host系統上面正常的運行)。Qemu在遇到需要返回最新值的時候會更新程序計數器,其他時候不會每次都更新,來作為一種優化措施。因此,程序計數器會指向一個代碼塊的最開始位置,因為每次進行分支跳轉的時候才需要更新程序計數器。

                    0x03 由優化聯想到的


                    試想下在多任務的操作系統中,當有中斷發生的時候,上面的優化會導致什么事?由于程序計數器不是每執行一次就更新的,那么虛擬CPU在執行一個代碼塊的時候是不能被中斷的,被中斷后就沒法恢復正確的程序計數器值。所以在運行一個代碼塊的時候,一般不會發生任務調度的情況。整個過程如下圖所示:

                    p2

                    0x04 實驗驗證


                    上面說不能中斷,不能任務調度,那么實驗就人為制造任務調度的情況,然后考察任務調度的情況。記錄發生任務調度的地址,然后統計查看這些地址的分布情況。那么在一個真機的環境中,這些地址應該近似是相等的次數,均勻的分布。每個地址發生調度情況的可能性是相等的。在Qemu虛擬的環境中,應該是在一個代碼塊的最后才會發生任務調度,所以地址分布也不是均勻的,而是變化很大。

                    具體的實驗是通過2個線程。一個線程在一個代碼塊中對一個全局變量不斷地加1,每次循環全局變量都賦值為1。另一個線程也是循環,每次都去讀前面的那個全局變量。然后統計讀出來每個數字的次數。流程如下:

                    p3

                    不斷加1的線程代碼如下:

                    #!cpp
                    void* thread1(void * data){
                        for(;;){
                            __asm__ __volatile__ ("mov r0, %[global];"
                                                  "mov r1, #1;"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  "add r1, r1, #1;" "str r1, [r0];"
                                                  :
                                                  :[global] "r" (&global_value)
                                                  :
                                                  );
                    
                        }
                    }
                    

                    一共是從2一直加到32。

                    另一個線程直接讀全局變量,并且把讀出來的值進行統計

                    #!cpp
                    for(i=0;i<50000;i++)
                        count[global_value]++;
                    

                    然后計算count數組統計出來的值的方差,看看分布情況的離散程度,這里我嘗試了3個計算方式。

                    1. 直接計算原始數據的方差和標準差;
                    2. 對所有數據除以最大值,然后計算方差和標準差;

                    3. p4,進行離差標準化以后,計算方差和標準差。

                    0x05 結果與討論


                    一共是循環讀取50000次。

                    在模擬器里,3種方式計算出來的方差和標準差如下:

                    p5

                    每個數字統計的次數如下:

                    p6

                    除了count[32]其他都是0。

                    在真機中,3種方式計算出來的方差和標準差如下:

                    p7

                    每個數字統計的次數如下:

                    p8

                    雖然count[32]比其他各個地方都多,但是中間基本都是均勻分布的。

                    那么實驗的含義是什么呢。其實就是在一個線程加1的過程中,另一個線程在什么地方打斷了它,然后從讀出來的值看是在哪里打斷了,也就是進行了任務調度。因此從結果來看,確實符合上面的分析,模擬器只在代碼塊的最后,有分支指令的時候進行了任務調度。而在真機中,實驗中發現也是在這里調度最多,但在上面其他位置是基本均勻分布的。大家可以自己設計反應離散程度的指標來判斷是否運行在模擬器。

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频