<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/tips/2871

                    0×00 起因


                    最近想了解下移動端app是如何與服務端進行交互的,就順手下了一個某app抓下http包。誰知抓下來的http居然都長這個模樣:

                    POST /ca?qrt=***LoginHTTP/1.1
                    Content-Type:application/x-www-form-urlencoded
                    Content-Length:821
                    Host:client.XXX.com
                    Connection:Keep-Alive
                    
                    c=B946D7CF7B9E5B589F8DE6BC9E5DACF08DA6B35DA7A899DCA5AD5A58ADDAD5E66363589EF2D385B1ACACABDAD5E65F63589EF2D3F5B43EA7ACE36DFCA5383EABE7D4F1403E379FF0DEFCAD9FABA7E6E1F243A7AAAC74E380A4A09E6593D3FEA4ABA8ACF0DDF0AEAAB3A7EAE9FBA4A09E5F97D3FEA49EA09E9B97A9A4B69EADE7E1F6B0AC9EA0DA94A95E57609EF2D3B55E659EA0DA94B55F9EB69EDAD5E668689EB6DA98AA6E576E62939DE6A69E616D868CB673636162DAEBE6AEA2ACA2E486F3AD9EA09EA898A0A4B69EABE8E1F3B29EA09EA998A0A4B69EADE3E385AE3DA7ABDB6FF4B93A9F3DEFE3FBA537ACAD77D486AD37ADABE77383B4B4ACB4DAD5E66E9EB69EA886AF634061599F97E6A69E676394D3FEA4ACACACE8E1F4B2ACACACE8E1F4B2AC9EA0DA9CAAA4B69E9EDCD3B269589EB6DADFF4B2ACABACE3E9E675&b=AD178E267CA8666F636D6C54ADC1B3AEAD736574696950776D71A9BEACADA7A8727762A8C0AA67656172736A6D676F706E6369837F726C71A5AAA4756C656963ADC1A6797870615E676E7063666C756CAC7E&ext=&v=alex
                    

                    這是我在嘗試登陸時抓包獲取的唯一http包,顯而易見POST數據中的c參數是包含登錄信息的,但是為什么長這個模樣?為了得到答案,我開啟了我一周的Android動態調試和靜態分析學習之旅。

                    這篇文章將通過這段字符串原文的過程,向各位介紹幾種非常好用的Android調試工具以及它們的一些簡單用法。

                    0×01 分析過程


                    1.基本靜態分析過程

                    拿到一個apk最常規的做法應該是就是,反編譯查看一下java源碼了。

                    apktool反編譯得到smali(其實主要是為了看AndroidManifest.xml):

                    /*我用的apktool是2.0.0-Beta9,命令和常見的1.x版本的命令有所不同*/ 
                    apktool d XXX.apk -o ./doc
                    /*我用的apktool是2.0.0-Beta9,命令和常見的1.x版本的命令有所不同*/
                    apktooldXXX.apk-o./doc
                    

                    enter image description here

                    然后用的dex2jar工具將apk反編譯為jar,并通過JD-GUI來查看java源碼:

                    dex2jar.shXXX.apk
                    

                    enter image description here

                    在apktool反編譯的目錄中我們可以翻看AndroidManifest.xml來了解apk文件的基本結構,我從中首先找到主Activity的所在:

                    <activityandroid:configChanges=”keyboardHidden|orientation”android:exported=”true”android:name=”com.XXX.NoteActivity”android:screenOrientation=”portrait”android:[email protected]:style/Theme.Black.NoTitleBar.Fullscreen”><intent-filter><actionandroid:name=”android.intent.action.MAIN”/><categoryandroid:name=”android.intent.category.LAUNCHER”/><categoryandroid:name=”android.intent.category.MULTIWINDOW_LAUNCHER”/></intent-filter>
                    

                    可以從上面的內容看出主Activity是com.XXX.NoteActivity這個類所定義的,然后通過JD-GUI打開dex2jar反編譯后的jar包,查看NoteActivity。

                    enter image description here

                    先來看onCreate中的操作,發現使用produard對apk進行了混淆了。這種混淆雖然不會影響我們反編譯出來的代碼內容,但是由于對類名、函數名、變量名進行了隨機命名,導致我們閱讀代碼的過程比較痛苦。

                    我的目的很明確,就是定位到處理提交數據的代碼,沒有必要去花大量的時間來閱讀被混淆的代碼,所以我決定使用動態跟蹤程序運行的軌跡來定位我想要獲得的代碼。

                    2.動態定位過程

                    雖然要用動態的方法來定位,但是還是需要簡單的閱讀java源碼來確定提交數據的大概處理方式。

                    我的運氣還是不錯,網絡傳輸部分的代碼并沒有被混淆。大體看了一下這些代碼,發現和一般的app一樣,客戶端和服務端的數據交互也是使用json的格式進行的,并且使用了阿里開源的fastjson類來處理json內容。

                    enter image description here

                    enter image description here

                    了解了以上的這些情況,我決定通過跟蹤JSONObject這個類來定位處理提交數據的位置。

                    這里推薦一個分析app的神器——Andbug,雖然不能用來單步調試,但是動態跟蹤app中各種線程調用棧、類調用棧、方法調用棧,斷點獲取當前內存中變量內容等功能還是非常實用的。

                    廢話不多說了,來看操作吧,先獲取要動態分析的app進程ID:

                    adb shell ps
                    

                    enter image description here

                    進程ID 445,使用Andbug掛載該進程,并使用classes命令查找fastjson類的全路徑:

                    andbug shell -p 435 classes JSONObject andbug shell -p435 classes JSONObject

                    enter image description here

                    這里提示一個使用classes和method命令查找的小技巧。我們在Andbug的shell環境下使用classes時很容易由于class過多而導致沒辦法看到所有的class。這是我們可以在終端環境下使用classes命令配合more來一點點的查看,就像這樣: andbug classes -p 435|more

                    然后我們使用class-trace命令來對這個類進行跟蹤:

                    class-trace com.alibaba.fastjson.JSONObject
                    

                    enter image description here

                    在app中隨便觸發一個會提交請求的事件,調用過程在終端中完美的呈現了出來:

                    enter image description here

                    可以看到調用過程都用到了com.XXX.net.task.CommonTask中的方法,打開這個類的java源碼第一眼就看到這段代碼:

                    #!java
                    protectedHttpEntity buildHttpEntity()
                      {
                        if(this.hostUrl.indexOf(“?”)>0)
                          this.hostUrl=(this.hostUrl+“&qrt=”+this.networkTask.param.key.getDesc());
                        while(true)
                        {
                          Stringstr=String.valueOf(this.networkTask.param.ke);
                          StringBuilderlocalStringBuilder1=newStringBuilder();
                          localStringBuilder1.append(“c=”+chrome(str));
                          localStringBuilder1.append(“&”);
                          StringBuilderlocalStringBuilder2=newStringBuilder(“b=”);
                          BaseParamlocalBaseParam=this.networkTask.param.param;
                          SerializerFeature[]arrayOfSerializerFeature=newSerializerFeature[1];
                          arrayOfSerializerFeature[0]=SerializerFeature.WriteTabAsSpecial;
                          localStringBuilder1.append(NetworkParam.convertValue(JSON.toJSONString(localBaseParam,arrayOfSerializerFeature),str));
                          if((this.networkTask.param.param instanceofHotelBookParam))
                          {
                            HotelBookParamlocalHotelBookParam=(HotelBookParam)this.networkTask.param.param;
                            if(localHotelBookParam.vouchParam!=null)
                              dealVouchRequest(localStringBuilder1,localHotelBookParam.vouchParam);
                          }
                          localStringBuilder1.append(“&”);
                          localStringBuilder1.append(“ext=”+NetworkParam.convertValue(XXXApp.getContext().ext,str));
                          localStringBuilder1.append(“&v=alex”);
                          this.networkTask.param.url=localStringBuilder1.toString();
                          try
                          {
                            StringEntitylocalStringEntity=newStringEntity(this.networkTask.param.url);
                            returnlocalStringEntity;
                            this.hostUrl=(this.hostUrl+“?qrt=”+this.networkTask.param.key.getDesc());
                          }
                          catch(UnsupportedEncodingExceptionlocalUnsupportedEncodingException)
                          {
                            cl.m();
                          }
                        }
                        returnnull;
                      }
                    

                    結合之前的抓包,這應該就是我要找的地方了。從中找到處理c參數的代碼,看到調用了com.XXX.net.task.AbstractHttpTask.chrome對參數值進行了處理,跟進chrome方法:

                    #!java
                    protectedStringchrome(StringparamString)
                      {
                        JSONObjectlocalJSONObject=gcc(paramString);
                        Stringstr=“60001058″.substring(0,4)+“lex”;
                        returnNetworkParam.convertValue(localJSONObject.toString(),str);
                      }
                    

                    繼續趕進到convertValue方法:

                    #!java
                    publicstaticStringconvertValue(StringparamString1,StringparamString2)
                      {
                        if(TextUtils.isEmpty(paramString1))
                          return "";
                        if(paramString2==null)
                          paramString2=“”;
                        try
                        {
                          Stringstr=URLEncoder.encode(Goblin.e(paramString1,paramString2),“utf-8″);
                          returnstr;
                        }
                        catch(ThrowablelocalThrowable)
                        {
                          localThrowable.printStackTrace();
                        }
                        return "";
                      }
                    

                    感覺的勝利的曙光越來越近了,這個Goblin.e應該就是最后的加密方法了吧,誰知打開這個文件(內心一萬只草泥馬在狂奔):

                    #!java
                    packageXXX.lego.utils;
                    importcom.XXX.XXXApp;
                    
                    publicclassGoblin
                    {
                      static
                      {
                        try
                        {
                          System.loadLibrary(“goblin_2_5″);
                          return;
                        }
                        catch(UnsatisfiedLinkErrorlocalUnsatisfiedLinkError1)
                        {
                          try
                          {
                            System.load(“/data/data/”+XXXApp.getContext().getPackageName()+“/lib/lib”+“goblin_2_5″+“.so”);
                            return;
                          }
                          catch(UnsatisfiedLinkErrorlocalUnsatisfiedLinkError2)
                          {
                          }
                        }
                      }
                    
                      publicstaticnativeStringSHR();
                    
                      publicstaticnativeStringd(StringparamString1,StringparamString2);
                    
                      publicstaticnativeStringdPoll(StringparamString);
                    
                      publicstaticnativeStringda(StringparamString);
                    
                      publicstaticnativeStringdn(byte[]paramArrayOfByte,StringparamString);
                    
                      publicstaticnativebyte[]dn1(byte[]paramArrayOfByte,StringparamString);
                    
                      publicstaticnativeStringduch(StringparamString);
                    
                      publicstaticnativeStringe(StringparamString1,StringparamString2);
                    
                      publicstaticnativeStringePoll(StringparamString);
                    
                      publicstaticnativeStringea(StringparamString);
                    
                      publicstaticnativebyte[]eg(byte[]paramArrayOfByte);
                    
                      publicstaticnativeStringes(StringparamString);
                    
                      publicstaticnativeintgetCrc32(StringparamString);
                    
                      publicstaticnativeStringgetPayKey();
                    
                      publicstaticnativeStringve(StringparamString);
                    }
                    

                    3.調用so文件函數

                    居然把加密方法寫到了so文件中!難道要去看ARM匯編?

                    既然這個so文件中有加密函數,那是不是就應該有解密函數,那我應該還是可以偷懶的吧。

                    我們在上面看到的e函數肯定是用來加密的,那那個d函數是不是用來解密的(encode和decode)?

                    自己本地創建一個app,并且創建一個XXX.lego.utils包,添加一個Goblin.java文件,把我們剛剛看到的Goblin源碼粘貼進去。然后在app的一個Activity中導入Goblin,并在OnCreate中調用d函數來嘗試解密。部分代碼如下:

                    #!java
                    Stringc=“B946D7CF7B9E5B589F8DE6BC9E5DACF08DA6B35DA7A899DCA5AD5A58ADDAD5E66363589EF2D385B1ACACABDAD5E65F63589EF2D3F5B43EA7ACE36DFCA5383EABE7D4F1403E379FF0DEFCAD9FABA7E6E1F243A7AAAC74E380A4A09E6593D3FEA4ABA8ACF0DDF0AEAAB3A7EAE9FBA4A09E5F97D3FEA49EA09E9B97A9A4B69EADE7E1F6B0AC9EA0DA94A95E57609EF2D3B55E659EA0DA94B55F9EB69EDAD5E668689EB6DA98AA6E576E62939DE6A69E616D868CB673636162DAEBE6AEA2ACA2E486F3AD9EA09EA898A0A4B69EABE8E1F3B29EA09EA998A0A4B69EADE3E385AE3DA7ABDB6FF4B93A9F3DEFE3FBA537ACAD77D486AD37ADABE77383B4B4ACB4DAD5E66E9EB69EA886AF634061599F97E6A69E676394D3FEA4ACACACE8E1F4B2ACACACE8E1F4B2AC9EA0DA9CAAA4B69E9EDCD3B269589EB6DADFF4B2ACABACE3E9E675″;
                    Stringp2=“6000lex”;
                    Stringtest=Goblin.d(c,p2);
                    System.out.println(test);
                    

                    enter image description here

                    天不遂人愿啊!解密出錯,看來真的要去看ARM匯編了。。。。。。

                    4.動態調試so文件

                    由于app自帶的加密數據,我們不知道原來的樣子,所以要自己構造一個字符串加密,來調試。修改上面的app代碼如下:

                    #!java
                    Stringjson=“json{/”test/”:/”test1/”,/”test4/”:/”test1/”,/”test5/”:/”test1/”,/”test6/”:/”test1/”,/”test3/”:/”test1/”,/”test2/”:/”test1/”,/”test1/”:/”test1/”}”;
                    Stringp2=“6000lex”;
                    Stringtest=Goblin.e(c,p2);
                    System.out.println(test);
                    

                    生成代碼如下:

                    C0990850B69C969E92C19C9DC5CDD9F0FAB99AD3960CE3F6D2CFB0AA9AE904F6C0A099A68DFFD0C1EE978CA985CAD2FCF6CD92A7C4FCE106CCC99CC39C11F7F3D0A9BDAD8AE3E0D9C3BC898EB8DCD3F2BB8B8BB3C010D9DAFAB99AD3960FE30CD2CFB0AA9AEC04E0C0A099A68DFFD0D7EE978CA985CED2B5
                    

                    好了準備活動完成了,下面我們開始動態跟蹤之旅吧。在《Android軟件安全與逆向分析》中提供的動態分析工具是IDA pro 6.1以上版本,這個我在調試過程中發現加載很慢。雖然加載完成后,能夠跟著IDA生成的流程圖來調試很爽,但是加載成功率實在是太低了。所以,我放棄了用IDA進行動態調試,而是選擇了 這個號稱移動端Onllydbg的gikdbg來進行調試,同時配合IDA的流程圖。

                    gikdbg使用參考《gikdbg.art系列教程2.1-調試so動態庫》這篇blog很容易上手,這里也就不多說了。

                    調試跟蹤過程很枯燥,也沒什么可以說的,我們直接看結過吧。

                    通過反復的動態跟蹤,確定下面這個循環是加密的關鍵:

                    enter image description here

                    可以看出加密方法比較簡單,對于源數據的每一個字符與0×45進行異或,然后jia0x24,最后再加上硬編碼在so文件的一串key中的一個字符。根據匯編逆向出來的python代碼如下:

                    result=""
                    i=0
                    while(i<len(json)):
                        char=json[i]^69
                        j=i
                        ifj>len(key):
                            j=j%len(key)
                        encode=int(ord(char))+36+key[i]
                        result+=encode
                        i+=1
                    

                    在加密完數據后,會在數據頭部添加一個8個字符(32位)的校驗數據,校驗算法使用的是adler32。由此可以推出解密算法,代碼如下:

                    defdecode():

                    ejson=‘B69C969E92C19C9DC5CDD9F0FAB99AD3960CE3F6D2CFB0AA9AE904F6C0A099A68DFFD0C1EE978CA985CAD2FCF6CD92A7C4FCE106CCC99CC39C11F7F3D0A9BDAD8AE3E0D9C3BC898EB8DCD3F2BB8B8BB3C010D9DAFAB99AD3960FE30CD2CFB0AA9AEC04E0C0A099A68DFFD0D7EE978CA985CED2B5′
                    
                    key=‘cBHO06GYkxNModVyAtXiGzlPETyS5KUL8gE4′
                    i=0
                    result=”
                    while(i<len(ejson)):
                        j=i/2
                        char=ejson[i:i+2]
                        ifj<len(key):
                            k=key[j]
                        else:
                            k=key[j%len(key)]
                        i=i+2
                        c=int(char,16)-int(ord(k))
                        ifc<0:
                            c+=128
                        c=c-36
                        ifc<0:
                            c+=128
                        c=c^69
                        dchar=chr(c)
                        result+=dchar
                    printresult
                    decode()
                    

                    其中ejson中的內容為,我們使用e函數加密后獲得的內容剔除前八位,解密效果如下:

                    0×02 最終結果&分析總結


                    不過悲劇的是用這個解密方法沒辦法解密前面我抓包獲取的數據。。。。。。

                    郁悶之心無以言表啊!!!

                    不過這個過程還是很有意義的,了解了Android各種姿勢的動態調試方法。這里再次回顧一些這個過程。

                    首先通過反編譯獲取smali和java代碼進行靜態分析,發現代碼被混淆后,明確自己的最終目標——找到處理提交請求的方法,然后進行動態跟蹤。動態跟蹤和靜態分析結合定位出處理提交請求的幾個類,翻看這些類的代碼,來找到最終我們想找的方法。

                    在發現處理方法使用了so文件中的函數,通過自己構造app來分別調用so中的各個函數,試圖從中找到直接的解密函數。

                    在so中沒有找到解密函數的情況下,通過動態調試與靜態查看匯編,分析出加密算法,并寫出解密工具。

                    0×03 參考文章


                    【1】《Assembly Programming Principles》 【2】《Android動態逆向分析工具(一)——Andbug之基本操作》 【3】《Android動態逆向分析工具(四)—— Andbug補充調試功能》 【4】《gikdbg.art系列教程2.1-調試so動態庫》

                      <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>

                                      这里只有精品视频