• 
    

    
    

      99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

      基于Smali-Java關(guān)鍵代碼修復(fù)的Android程序逆向分析方法

      2019-06-27 05:50:28徐國天張明星
      關(guān)鍵詞:源代碼逆向語句

      徐國天 張明星

      (1 中國刑事警察學(xué)院網(wǎng)絡(luò)犯罪偵查系 遼寧 沈陽 110035;2 龍巖市公安局新羅分局 福建 龍巖 364000)

      1 引言

      2018年8月20日,中國互聯(lián)網(wǎng)絡(luò)信息中心(CNNIC)發(fā)布了第42次《中國互聯(lián)網(wǎng)絡(luò)發(fā)展?fàn)顩r統(tǒng)計(jì)報(bào)告》(簡稱《報(bào)告》)?!秷?bào)告》顯示截至2018年6月,我國手機(jī)網(wǎng)民規(guī)模達(dá)7.88億。隨著移動互聯(lián)網(wǎng)應(yīng)用的普及,利用智能手機(jī)惡意程序?qū)嵤┑倪`法犯罪活動也在逐漸增多。Android系統(tǒng)是目前市場占有率最高的一款智能手機(jī)終端操作系統(tǒng),它允許用戶隨意在智能終端上安裝應(yīng)用程序,這種靈活性在給用戶帶來便捷體驗(yàn)的同時(shí),也給犯罪分子提供了可乘之機(jī)。目前,針對Android系統(tǒng)的各類惡意程序,如盜號木馬、遠(yuǎn)程控制木馬、勒索病毒、間諜軟件等種類繁多,危害極大[1]。

      目前Android程序取證主要采用以下三種方法。第一種是動態(tài)測試法。這種方法是將送檢的Android程序安裝到取證專用智能終端上,觸發(fā)程序運(yùn)行,通過觀察送檢程序運(yùn)行之后,表現(xiàn)出的一些外在行為(如彈出一個信息提示界面,或控制用戶的鍵盤輸入等),完成取證分析工作。這種方法實(shí)施起來難度較低,但是對一些沒有外在表現(xiàn)的行為(如內(nèi)存訪問、數(shù)據(jù)庫讀寫行為),不能有效察覺。第二種是通過沙箱環(huán)境進(jìn)行檢驗(yàn)分析。沙箱系統(tǒng)一般布置在Android虛擬機(jī)上,將送檢程序安裝到沙箱內(nèi)運(yùn)行,沙箱系統(tǒng)會自動監(jiān)控送檢程序的函數(shù)調(diào)用行為,如磁盤讀寫函數(shù)、網(wǎng)絡(luò)通信函數(shù)、數(shù)據(jù)庫訪問函數(shù)、藍(lán)牙訪問函數(shù)等。通過監(jiān)控關(guān)鍵函數(shù)的調(diào)用情況,對程序功能進(jìn)行全面分析?;ヂ?lián)網(wǎng)上此類公開平臺較多,但是此類平臺通常不會免費(fèi)提供完整的檢驗(yàn)報(bào)告,其給出的檢驗(yàn)分析結(jié)果僅能用于取證結(jié)論的輔助參考。第三種是通過靜態(tài)源代碼逆向分析方法對Android程序進(jìn)行檢驗(yàn)分析。這種方法是將APK格式的Android程序轉(zhuǎn)換為Smali或Java源代碼,再通過逐行閱讀源代碼的方式分析程序的邏輯功能,進(jìn)而完成取證分析工作。此類方法可以對送檢程序進(jìn)行全面分析,但需要檢驗(yàn)人員具備Android程序設(shè)計(jì)語言基礎(chǔ),需要閱讀的代碼量比較大,同時(shí),當(dāng)程序采用了加殼或加密等反取證措施時(shí),檢驗(yàn)分析的難度進(jìn)一步加大。在檢驗(yàn)鑒定工作中,通常是采用多種方法對送檢程序進(jìn)行檢驗(yàn)分析,參考每種方法得到的取證結(jié)果,綜合給出鑒定意見。

      本文針對上述第三種靜態(tài)源代碼逆向分析方法進(jìn)行研究,這種方法通過將APK格式的Android程序轉(zhuǎn)換為Java代碼,再通過閱讀Java源代碼分析程序的邏輯功能,提取出嫌疑人電子郵箱、手機(jī)號碼等關(guān)鍵線索。但是在將APK程序轉(zhuǎn)換為Java源代碼的過程中,部分核心代碼因?yàn)榻Y(jié)構(gòu)比較復(fù)雜,現(xiàn)有取證工具無法自動完成轉(zhuǎn)換,會將這部分核心代碼轉(zhuǎn)換為錯誤的Java源程序,進(jìn)而給取證分析工作造成嚴(yán)重影響。本文提出一種基于Smali-Java關(guān)鍵代碼修復(fù)的Android程序逆向分析方法,它采用人工分析方式,根據(jù)核心代碼對應(yīng)的Smali源程序完成受損Java代碼的修復(fù),進(jìn)而完成Android程序的取證分析工作。

      2 APK程序反編譯方法

      2.1 兩種逆向分析方法比較

      APK程序使用Java語言開發(fā)。編程人員寫好Java源程序之后,需要使用編譯器將所有Java源代碼編譯、生成一個APK程序。Java源程序可讀性好,通過分析Java源代碼可以準(zhǔn)確掌握程序的執(zhí)行流程。APK程序由一系列二進(jìn)制0、1數(shù)據(jù)組成,無法直接閱讀。因此,為了準(zhǔn)確獲知APK程序的主要功能,需要將APK程序還原為對應(yīng)的Java源代碼,這一過程就是APK程序的反編譯[2]。

      如圖1所示,APK程序的反編譯主要有兩種方法。第一種是先將APK程序轉(zhuǎn)換成Smali源代碼,再將Smali源代碼轉(zhuǎn)化為Java源程序[3]。Smali語言是Davlik寄存器語言,類似于臺式計(jì)算機(jī)上的匯編語言,是一種低級語言,可讀性較差,它的書寫規(guī)范與Java等高級語言完全不同。調(diào)查人員直接閱讀Smali源代碼來分析程序功能難度較大,且效率較低[4]。但是APK轉(zhuǎn)換成Smali的過程沒有損失,Smali源代碼可以完整再現(xiàn)APK程序的相關(guān)功能。

      圖1 反編譯方法

      Smali代碼在轉(zhuǎn)換成Java代碼過程中會出現(xiàn)10%左右轉(zhuǎn)換錯誤,這10%的程序代碼通常是Smali源文件中最復(fù)雜的多分支語句或循環(huán)語句,這部分代碼通常是APK程序的核心代碼。因此調(diào)查人員在進(jìn)行分析時(shí),對于簡單的程序函數(shù)可以參考Java源代碼,對于無法轉(zhuǎn)換的那10%左右的代碼可以參考Smali源文件。

      第二種反編譯方法是先通過APK程序獲得dex文件,再將dex文件轉(zhuǎn)化為jar文件,最后將jar文件轉(zhuǎn)化為Java源代碼[5]。這種方式的轉(zhuǎn)換過程比第一種方法多了一個環(huán)節(jié),但是只有5%左右的代碼損失,并且得到的Java源代碼更規(guī)范,可讀性更好。本文提出的逆向分析方法總體思路是利用第一種方法獲得Smali源文件,再以第二種方法獲得Java源文件,對于未能正常解析的Java源代碼,利用人工分析方式,將對應(yīng)的Smali代碼轉(zhuǎn)換為Java源代碼,進(jìn)而完成逆向分析工作。

      2.2 反編譯APK程序得到Smali源代碼

      為了進(jìn)行功能鑒定需要對APK程序執(zhí)行反編譯,將其轉(zhuǎn)化為可以直接閱讀的源程序。目前現(xiàn)有工具軟件無法直接將APK程序還原為對應(yīng)的Java源程序,只能轉(zhuǎn)化為一種名為Smali的中間語言。這種語言與臺式計(jì)算機(jī)上的匯編語言類似,可讀性較差,但是可以完整還原程序功能。這里使用一種名為apktools的工具執(zhí)行反編譯,下面介紹具體的逆向方法。

      首先,安裝Java運(yùn)行環(huán)境。apktools需要Java運(yùn)行環(huán)境支持,因此需要先安裝Java工具包。之后,安裝、運(yùn)行apktools,反編譯目標(biāo)APK程序。Apktools工具有三項(xiàng)主要功能。第一是將APK程序反編譯成Smali代碼。第二是允許用戶修改Smali代碼,并將修改后的Smali代碼重新編譯為APK程序。第三是提供了簽名功能,重新編譯形成的APK程序需要簽名之后才能使用。

      本文以一款名為9555.apk的手機(jī)木馬程序?yàn)槔M(jìn)行逆向分析,利用apktools工具反編譯成功之后,在目標(biāo)路徑下會形成一個名為“9555.apk.decode”的文件夾。圖2是將測試文件9555.apk反編譯得到的22個Smali程序文件。如圖2所示,每個文件代表一個類,文件名就是類名。例如,A.Smali就是名為A的類,其中存儲的就是這個類的Smali源代碼。還有一些文件中帶有$符號。例如,R$id.Smali。它表示在名為R的類中又定義了名為id的子類,這個文件中存儲的就是id子類的Smali源代碼。如圖2所示,9555.apk共包含14個類定義。

      圖2 9555.apk反編譯得到的22個Smali文件

      2.3 反編譯APK程序得到Java源代碼

      通過第二種方法反編譯APK程序獲得Java源代碼的具體方法。首先,解壓縮APK程序獲得dex文件。APK程序默認(rèn)采用的是ZIP壓縮格式,只要將程序的擴(kuò)展名由apk修改為zip,之后進(jìn)行解壓縮即可獲得dex文件。請注意解壓之后也會產(chǎn)生一些xml文件。例如AndroidManifest.xml,但是這些xml文件通常無法正常查看。

      之后,使用dex2jar軟件將dex文件轉(zhuǎn)化為jar文件,這個軟件也需要提前安裝好Java運(yùn)行環(huán)境。Dex2jar是一個DOS環(huán)境下運(yùn)行的應(yīng)用程序。將dex文件拷貝到dex2jar程序根目錄下,之后在DOS窗口中輸入dex2jar classes.dex命令。轉(zhuǎn)換完成之后,在dex2jar根目錄下就會生成一個名為classes_dex2jar.jar的結(jié)果文件。

      將jar文件轉(zhuǎn)化為Java源程序。使用jd-gui軟件可以將jar文件轉(zhuǎn)換為對應(yīng)的Java源程序。使用jdgui軟件查看classes_dex2jar.jar,會出現(xiàn)圖3所示結(jié)果。共出現(xiàn)14個Java類文件,目錄結(jié)構(gòu)與前面獲得的Smali源代碼可以一一對應(yīng),便于分析。不同的是主類和子類的定義在Smali中是分開書寫的,而在Java源代碼中子類的程序代碼已經(jīng)寫入到對應(yīng)的主類中??梢钥吹皆贘ava源程序中字符串?dāng)?shù)據(jù)不在是原始的十六進(jìn)制數(shù)值而是已經(jīng)轉(zhuǎn)化為可以直接閱讀的文字,并且Java源程序的可讀性更強(qiáng)。

      圖3 由jar文件轉(zhuǎn)換得到Java源程序

      3 Smali和Java源代碼的相互轉(zhuǎn)換方法

      Smali是Dalvik的寄存器語言,它與Java的關(guān)系就像匯編語言與C語言的關(guān)系。在臺式計(jì)算機(jī)上,一個用C語言開發(fā)的exe文件在進(jìn)行逆向分析時(shí),首先會轉(zhuǎn)化為匯編語言,之后再由匯編語言轉(zhuǎn)化為C語言代碼,通過分析C語言代碼可以了解程序的相關(guān)功能[6]。

      APK程序與exe程序的反編譯過程極為相似,兩者的對應(yīng)關(guān)系如圖4所示。不同的是匯編語言比較復(fù)雜,短時(shí)間內(nèi)不易掌握。Smali語言相對比較簡單,便于轉(zhuǎn)化為Java源程序。但是Smali在轉(zhuǎn)化為Java源代碼的過程中會出現(xiàn)5%~10%左右代碼轉(zhuǎn)換錯誤,這部分代碼通常是APK程序的核心功能,因此需要直接分析Smali文件。這就需要了解Smali的語法規(guī)則,下面我們采用Smali和Java源程序?qū)Ρ确治龅姆椒▉磉M(jìn)行介紹。理解這部分內(nèi)容的前提條件是讀者已經(jīng)具備Java語言的基礎(chǔ)知識。

      圖4 exe和APK程序反編譯對應(yīng)關(guān)系

      3.1 Smali和Java的方法定義轉(zhuǎn)換

      下面給出的是使用Java語言編寫的一個方法。這個方法名稱是onCreate;有一個Bundle類型的傳入?yún)?shù),參數(shù)名為paramBundle;方法返回值為void類型、protected屬性。方法中只有一條語句,調(diào)用了父類的onCreate方法。

      這個Java方法可以轉(zhuǎn)化為如下Smali代碼。#是Smali中的注釋語句;.method和.end method相當(dāng)于Java中的{ },定義了方法的起始和結(jié)束位置。Landroid/os/Bundle定義了參數(shù)類型,.parameter定義了參數(shù)名,本例值為paramBundle。如果有多個參數(shù),可以在列表中依次增加。在Smali中參數(shù)使用p0~pn來表示,其中p0固定代表this,參數(shù)從p1開始。在本例中參數(shù)paramBundle對應(yīng)的就是p1。V表示方法返回值為void類型。

      .locals定義了方法用到的寄存器數(shù)量為4,寄存器名稱為v0~v3。.prologue定義了代碼的起始位置。.line定義了這條Smali語句是Java源程序中的第幾條語句,這個值主要用于調(diào)試,不起關(guān)鍵作用。invoke-super {p0, p1}, La ndroid/app/Activity;-〉onCreate(Landroid/os/Bundle;)V表示調(diào)用父類的onCreate方法,V表示返回值為void類型,p1為參數(shù),即paramBundle。Return-void表示返回值為void類型。

      通過對比可以看到,Smali和Java的對應(yīng)關(guān)系比較清晰,只是Smali代碼比Java代碼復(fù)雜一些,Java的一條語句可能對應(yīng)Smali的多條語句。

      3.2 Smali和Java的方法調(diào)用轉(zhuǎn)換

      下面這條Java語句是調(diào)用makeText方法,生成一組值為“hello,Smali”的字符串,然后調(diào)用show方法彈出這組提示信息。

      這條語句在進(jìn)行APK逆向分析時(shí)經(jīng)常用來測試程序功能,這里以它為例來對比分析Smali和Java的方法調(diào)用。這條Java語句對應(yīng)的Smali代碼如下:

      第一條語句const-string v0, "hello,Smali"定義了值為"hello,Smali"的字符串變量,變量值存儲在v0寄存器中。

      第二條語句const/4 v1, 0x1定義了值為0x1的整數(shù)變量,變量值存儲在v1寄存器中。

      第三條語句以靜態(tài)方式調(diào)用了makeText方法,這個方法包含3個參數(shù),類型分別是context、CharSequence和整數(shù)類型。參數(shù)值分別是p0、v0和v1,p0代表this,v0代表"hello,Smali",v1代表0x1。返回值是一個Toast類型的對象。

      第四條語句是將makeText方法調(diào)用的結(jié)果送到v0寄存器中。之前v0寄存器存儲的是字符串類型變量,這條語句執(zhí)行之后,v0寄存器中存儲的是Toast類型對象。

      第五條語句是以virtual方式調(diào)用show()方法,這個方法沒有參數(shù),這里v0代表上一條語句獲得的Toast對象,show()方法的返回值為void類型。通過分析,可以看到原本一條Java語句轉(zhuǎn)換為四條Smali代碼。

      3.3 Smali和Java的switch多條件分支語句轉(zhuǎn)換

      下面是使用Java語言編寫的一組switch多條件分支語句,這類語句在程序設(shè)計(jì)中也經(jīng)常使用。這段代碼的邏輯功能非常簡單,根據(jù)變量i的取值,分1、2、3和其他共4種情況處理。

      上述switch語句對應(yīng)的Smali代碼如下所示。V0寄存器表示變量i,初值為3。:cond_0是switch語句開始位置,標(biāo)志就是packed-switch v0, :pswitch_data_0。Switch的結(jié)束標(biāo)志是:pswitch_data_0,.packed-switch 0x1表示case匹配值從1開始遞增,每次加1。即滿足case 1:時(shí),執(zhí)行:pswitch_1處代碼;滿足case 2:時(shí),執(zhí)行:pswitch_2處代碼;滿足case 3:時(shí),執(zhí)行:pswitch_3處代碼;如這3個條件都不滿足,則執(zhí)行default代碼,即:goto_1位置代碼。所有分支最終都跳轉(zhuǎn)到:goto_0位置,執(zhí)行return-void返回。Nop代表空指令。

      3.4 Smali和Java的循環(huán)語句轉(zhuǎn)換

      循環(huán)語句主要有while和for兩種類型,下面是一段while循環(huán)結(jié)構(gòu),程序的一些關(guān)鍵功能通常包含在循環(huán)語句中。經(jīng)過實(shí)際檢測,發(fā)現(xiàn)在將APK程序反編譯為Java源代碼的過程中,循環(huán)語句和switch分支語句最容易出現(xiàn)問題。因此,調(diào)查人員必須掌握Smali語言編寫的循環(huán)語句到Java代碼的手工轉(zhuǎn)換方法。

      下面是上述Java代碼對應(yīng)的Smali語句。V0表示變量i,初值為0。V2存儲的是循環(huán)結(jié)束條件10。當(dāng)i值小于10時(shí),執(zhí)行循環(huán)體i = i + 1。否則執(zhí)行returnvoid結(jié)束循環(huán)。

      4 基于Smali-Java關(guān)鍵代碼修復(fù)的Android程序逆向分析方法

      APK程序的逆向分析方法目前主要有以下幾種:

      方法一:信息反饋法。這種方法是指先運(yùn)行目標(biāo)程序,然后根據(jù)程序運(yùn)行時(shí)給出的反饋信息作為突破口尋找關(guān)鍵代碼。通常情況下,程序中用到的字符串會存儲在String.xml文件或者直接寫入程序代碼中。如果是前者的話,字符串在程序中會以id的形式訪問,只需要在反編譯代碼中搜索字符串的id值就可以找到代碼調(diào)用處;如果是后者的話,可以在反編譯代碼中直接搜索字符串即可。

      方法二:順序查看法。這種方法是指從軟件的啟動代碼開始,逐行向下分析,掌握軟件的執(zhí)行流程。

      方法三:代碼注入法。代碼注入法屬于動態(tài)調(diào)試方法,它的原理是手工修改APK文件的反編譯代碼,加入Toast.MakeText.Show()語句,查看程序執(zhí)行到特定點(diǎn)的狀態(tài)數(shù)據(jù)。

      本文提出的APK程序逆向分析方法總體流程如圖5所示。這種方法以AndroidManifest.xml文件為分析入口,每次從中取出一個intent對象分析,循環(huán)進(jìn)行,直至所有intent對象分析完成。對于每個intent對象,首先獲得其相應(yīng)處理程序的Java代碼。如果Java代碼完好,則直接分析,否則獲取對應(yīng)的Smali代碼,再將Smali代碼以人工方式轉(zhuǎn)換為Java代碼,然后分析Java代碼。之后將APK程序安裝在Android系統(tǒng)手機(jī)上測試這個intent功能。如果程序設(shè)計(jì)存在邏輯錯誤,則修改原始Smali代碼,再重新編譯成APK程序,安裝測試。

      圖5 基于Smali-Java關(guān)鍵代碼修復(fù)Android程序逆向分析方法的總體流程

      這種分析方法綜合運(yùn)用了Smali、Java代碼分析,APK程序的修改、編譯和測試,雖然需要大量時(shí)間用于分析,但是可以完全獲得程序的執(zhí)行邏輯,甚至發(fā)現(xiàn)程序設(shè)計(jì)上的邏輯錯誤。

      5 基于Smali-Java關(guān)鍵代碼修復(fù)的Android程序逆向分析方法應(yīng)用

      5.1 無損Java代碼分析

      以9555.apk程序?yàn)槔?555.apk程序的AndroidManifest.xml文件中,圖6代碼定義了APK程序的名稱和顯示圖標(biāo)。Label變量表示應(yīng)用程序名稱,@string/app_name是名稱字符串所在的路徑,打開values文件夾下的string.xml,可以看到app_name字符串對應(yīng)的值為“招商客戶端”。Icon變量定義了應(yīng)用程序顯示的圖標(biāo),@drawable/ic_launcher是圖標(biāo)的存儲位置,打開drawable文件夾,可以看到名為ic_launcher.png的圖片文件,如圖6所示。

      圖6 定義應(yīng)用程序名稱和運(yùn)行圖標(biāo)

      AndroidManifest.xml文件的第一組代碼規(guī)定了APK程序的入口。它的標(biāo)志是字符串“android.intent.action.MAIN”,對應(yīng)的代碼對象名稱為“A”。如圖7所示,它表示當(dāng)9555.apk程序被點(diǎn)擊運(yùn)行時(shí),A中的代碼將被調(diào)用執(zhí)行。

      圖7 APK程序入口

      APK程序的入口代碼存儲在名為A的類中,下面顯示的是A類中的onCreate方法。函數(shù)首先調(diào)用了父類的onCreate方法。setContentView(0x7f030000)負(fù)責(zé)設(shè)置當(dāng)前activity的顯示界面,其執(zhí)行過程如下所示。Java程序代碼中的0x7f030000是一個id值,根據(jù)這個數(shù)值在public.xml文件中可以定位一條匹配記錄。這條記錄的名字是main,類型是layout布局文件。根據(jù)這兩個數(shù)值可以定位到layout文件夾下的main.xml文件,在這個文件中定義了一個webview類型控件,但是運(yùn)行時(shí)不顯示任何內(nèi)容。onResume方法負(fù)責(zé)將程序注冊為一個設(shè)備管理器。具體代碼此處不進(jìn)行分析。這段代碼在轉(zhuǎn)換過程中未受到損傷,調(diào)查人員可以直接分析。

      5.2 有損Java代碼分析方法

      9555.apk程序逆向之后得到的Java代碼中,S服務(wù)的onCreate方法代碼如下所示。當(dāng)S服務(wù)被激活時(shí),onCreate方法將被觸發(fā)運(yùn)行。這組代碼實(shí)際上完成了兩大功能:響應(yīng)接收短信廣播和當(dāng)短信箱發(fā)生變化時(shí)處理短信。通過逆向得到S服務(wù)onCreate方法的Java代碼,但是通過閱讀程序發(fā)現(xiàn)存在如下局部代碼,不難發(fā)現(xiàn)這段代碼存在邏輯錯誤。這是因?yàn)樵糞mali代碼中存在循環(huán)或多分支語句造成的,需要通過人工修正錯誤。

      循環(huán)語句的提取、轉(zhuǎn)換方法如下所示。const/4 v1, 0x0是將v1寄存器的值設(shè)置為0,這個寄存器就是變量i,之后v3寄存器被設(shè)置為20,這是循環(huán)結(jié)束值。if-lt v1, v3, :cond_0比較v1和v3的大小,如果v1小于20,則跳轉(zhuǎn)到:cond_0標(biāo)簽地址。否則執(zhí)行return-void結(jié)束程序的執(zhí)行。:cond_0標(biāo)簽地址的packed-switch v1, :pswitch_data_0是switch語句的起始標(biāo)志。Switch語句中的多個case子句這里不進(jìn)行提取。add-int/lit8 v1, v1, 0x1對應(yīng)的是i = i + 1命令。goto :goto_0重新返回到循環(huán)起始位置。修正之后的Java循環(huán)結(jié)構(gòu)如下所示。利用人工分析方法,對受損的Java代碼進(jìn)行修復(fù)。

      6 結(jié)語

      本文提出的基于Smali-Java關(guān)鍵代碼修復(fù)的Android程序逆向分析方法,可以對受損的核心代碼進(jìn)行修復(fù),完成取證分析工作。但是在分析過程中,要求取證人員通過人工分析方式定位受損代碼,在完成受損代碼的修復(fù)。工作效率偏低,且難度較大,下一步計(jì)劃研究受損代碼的自動定位和修復(fù)方法,以提高取證分析效率。

      猜你喜歡
      源代碼逆向語句
      人工智能下復(fù)雜軟件源代碼缺陷精準(zhǔn)校正
      逆向而行
      基于TXL的源代碼插樁技術(shù)研究
      重點(diǎn):語句銜接
      軟件源代碼非公知性司法鑒定方法探析
      精彩語句
      逆向解答
      揭秘龍湖產(chǎn)品“源代碼”
      如何搞定語句銜接題
      語文知識(2014年4期)2014-02-28 21:59:52
      逆向工程技術(shù)及應(yīng)用
      团风县| 蓬莱市| 乐亭县| 呼图壁县| 太仆寺旗| 阳曲县| 桐乡市| 鹤峰县| 西青区| 浦城县| 万年县| 廉江市| 横山县| 龙州县| 台南县| 浮山县| 承德市| 仪征市| 枞阳县| 台安县| 凤台县| 平陆县| 涞水县| 吴忠市| 正安县| 茂名市| 娱乐| 雷山县| 株洲市| 大悟县| 平顶山市| 新丰县| 西平县| 石城县| 孟村| 江油市| 新泰市| 南京市| 陇西县| 瑞金市| 广昌县|