從零開始學(xué)python(十八)想成為一名APP逆向工程師屑迂,需要掌握那些技術(shù)點(diǎn)诡渴?

作為從零學(xué)python的最后一篇文章捐晶,我們來簡單的回顧一下內(nèi)容

1.編程語法

2.機(jī)器學(xué)習(xí)

3.全棧開發(fā)

4.數(shù)據(jù)分析

5.爬蟲工程師養(yǎng)成

APP逆向工程

一丶java語法編程

一.java環(huán)境搭建

作為一個APP逆向工程師,你需要搭建Java開發(fā)環(huán)境來進(jìn)行Java語法編程妄辩。下面是詳細(xì)的Java環(huán)境搭建步驟:

下載Java開發(fā)工具包(JDK)
下載適用于你的操作系統(tǒng)的JDK版本惑灵。選擇適合你的操作系統(tǒng)和系統(tǒng)架構(gòu)的版本,并下載安裝文件眼耀。

安裝JDK

執(zhí)行下載的JDK安裝文件英支,并按照安裝向?qū)У闹甘具M(jìn)行安裝。在安裝過程中畔塔,你可以自定義安裝路徑潭辈,也可以使用默認(rèn)路徑。

配置環(huán)境變量(Windows系統(tǒng))

  • 打開“控制面板” -> “系統(tǒng)與安全” -> “系統(tǒng)”澈吨,點(diǎn)擊左側(cè)的“高級系統(tǒng)設(shè)置”把敢。
  • 在打開的對話框中,點(diǎn)擊“環(huán)境變量”按鈕谅辣。
  • 在用戶變量部分修赞,點(diǎn)擊“新建”按鈕,添加以下兩個環(huán)境變量:
    • 變量名:JAVA_HOME桑阶,變量值:JDK的安裝路徑(例如:C:\Program Files\Java\jdk-11.0.12)
    • 變量名:PATH柏副,變量值:%JAVA_HOME%\bin
  • 點(diǎn)擊“確定”保存設(shè)置。

配置環(huán)境變量(macOS和Linux系統(tǒng))

  • 打開終端并編輯~/.bash_profile文件蚣录,可以使用任何文本編輯器割择。
  • 添加以下行到文件末尾:
export JAVA_HOME=/Library/Java/JavaVirtualMachines/{jdk版本}/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH

將{jdk版本}替換為你安裝的JDK版本的文件夾名稱(例如:jdk-11.0.12)。

  • 保存文件并執(zhí)行以下命令使配置生效:
    source ~/.bash_profile

驗(yàn)證安裝
打開終端或命令行界面萎河,輸入以下命令檢查是否成功安裝和配置Java環(huán)境:

java -version

如果看到類似于以下輸出的版本信息荔泳,則說明Java環(huán)境已成功搭建:

java version "11.0.12" 2021-07-20 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.12+8-LTS-237)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.12+8-LTS-237, mixed mode)

現(xiàn)在你已經(jīng)成功搭建了Java開發(fā)環(huán)境,可以開始使用Java語言進(jìn)行APP逆向工程的編程任務(wù)了虐杯。你可以使用Java開發(fā)工具(如Eclipse玛歌、IntelliJ IDEA等)來編寫和運(yùn)行Java程序。

二.java基礎(chǔ)語法和數(shù)據(jù)類型

當(dāng)進(jìn)行APP逆向工程時擎椰,了解Java的基礎(chǔ)語法和數(shù)據(jù)類型是非常重要的支子。以下是Java的基礎(chǔ)語法和數(shù)據(jù)類型的詳細(xì)解釋:

標(biāo)識符:在Java中,標(biāo)識符是用來命名變量达舒、類值朋、方法等的名稱叹侄。標(biāo)識符必須以字母、下劃線或美元符號開頭吞歼,后面可以是字母圈膏、數(shù)字、下劃線或美元符號的組合篙骡。

注釋:注釋用于向代碼添加注解或解釋稽坤。在Java中,有三種類型的注釋:

  • 單行注釋:以雙斜線(//)開頭糯俗,注釋內(nèi)容在該行的末尾尿褪。
  • 多行注釋:以斜線加星號(/)開頭,以星號加斜線(/)結(jié)尾得湘,可以跨越多行杖玲。
  • 文檔注釋:以斜線加兩個星號(/*)開頭,以星號加斜線(/)結(jié)尾淘正,用于生成文檔摆马。

關(guān)鍵字:Java有一些保留的關(guān)鍵字,用于表示特定的含義或功能鸿吆。一些常用的關(guān)鍵字包括class囤采、public、private惩淳、static蕉毯、void等。

數(shù)據(jù)類型:Java中的數(shù)據(jù)類型分為兩種:

  • 基本數(shù)據(jù)類型:包括整數(shù)類型(byte思犁、short代虾、int、long)激蹲、浮點(diǎn)數(shù)類型(float棉磨、double)、字符類型(char)学辱、布爾類型(boolean)含蓉。
  • 引用數(shù)據(jù)類型:包括類、接口项郊、數(shù)組等。

變量:在Java中斟赚,變量用于存儲數(shù)據(jù)着降。聲明變量時需要指定數(shù)據(jù)類型,然后可以給變量賦值拗军。變量可以是基本數(shù)據(jù)類型或引用數(shù)據(jù)類型任洞。

運(yùn)算符:Java中有多種類型的運(yùn)算符坝撑,包括算術(shù)運(yùn)算符(+蔚晨、-、*、/惭蟋、%)、賦值運(yùn)算符(=驻襟、+=真友、-=等)、比較運(yùn)算符(==挪鹏、!=见秽、>、<等)讨盒、邏輯運(yùn)算符(&&解取、||、!等)等返顺。

控制流語句:Java提供了幾種控制流語句禀苦,用于控制程序的執(zhí)行流程。

  • 條件語句:if語句遂鹊、switch語句振乏。
  • 循環(huán)語句:for循環(huán)、while循環(huán)稿辙、do-while循環(huán)昆码。
  • 分支語句:break語句、continue語句邻储、return語句赋咽。

數(shù)組:數(shù)組是一種可以容納多個相同類型元素的數(shù)據(jù)結(jié)構(gòu)。在Java中吨娜,數(shù)組的大小在創(chuàng)建時指定脓匿,并且不能改變』略可以通過索引訪問數(shù)組中的元素陪毡。

這些是Java的基礎(chǔ)語法和數(shù)據(jù)類型的主要內(nèi)容。熟悉這些概念可以幫助你理解和編寫Java代碼勾扭,包括在進(jìn)行APP逆向工程時對Java代碼的分析和修改毡琉。

三.java控制流程

Java控制流程是指程序在執(zhí)行過程中,根據(jù)不同的條件或情況妙色,選擇不同的執(zhí)行路徑桅滋。Java中的控制流程主要包括條件語句和循環(huán)語句。

1.條件語句

條件語句用于根據(jù)不同的條件選擇不同的執(zhí)行路徑。Java中的條件語句包括if語句丐谋、if-else語句芍碧、if-else if語句和switch語句。

if語句

if語句用于判斷一個條件是否成立号俐,如果成立則執(zhí)行一段代碼塊泌豆。

語法格式:

if (條件) {
    // 執(zhí)行代碼塊
}

示例代碼:

int a = 10;
if (a > 5) {
    System.out.println("a大于5");
}

if-else語句

if-else語句用于判斷一個條件是否成立,如果成立則執(zhí)行一段代碼塊吏饿,否則執(zhí)行另一段代碼塊踪危。

語法格式:

if (條件) {
    // 執(zhí)行代碼塊1
} else {
    // 執(zhí)行代碼塊2
}

示例代碼:

int a = 3;
if (a > 5) {
    System.out.println("a大于5");
} else {
    System.out.println("a小于等于5");
}

if-else if語句

if-else if語句用于判斷多個條件,如果第一個條件成立則執(zhí)行第一個代碼塊找岖,否則判斷第二個條件陨倡,以此類推。

語法格式:

if (條件1) {
    // 執(zhí)行代碼塊1
} else if (條件2) {
    // 執(zhí)行代碼塊2
} else {
    // 執(zhí)行代碼塊3
}

示例代碼:

int a = 3;
if (a > 5) {
    System.out.println("a大于5");
} else if (a > 0) {
    System.out.println("a大于0许布,小于等于5");
} else {
    System.out.println("a小于等于0");
}

switch語句

switch語句用于根據(jù)不同的條件選擇不同的執(zhí)行路徑兴革,與if-else if語句類似,但是switch語句只能判斷整型蜜唾、字符型和枚舉類型杂曲。

語法格式:

switch (表達(dá)式) {
    case 值1:
        // 執(zhí)行代碼塊1
        break;
    case 值2:
        // 執(zhí)行代碼塊2
        break;
    ...
    default:
        // 執(zhí)行代碼塊n
        break;
}

示例代碼:

int a = 2;
switch (a) {
    case 1:
        System.out.println("a等于1");
        break;
    case 2:
        System.out.println("a等于2");
        break;
    default:
        System.out.println("a不等于1或2");
        break;
}

2.循環(huán)語句

循環(huán)語句用于重復(fù)執(zhí)行一段代碼塊,Java中的循環(huán)語句包括for循環(huán)袁余、while循環(huán)和do-while循環(huán)擎勘。

for循環(huán)

for循環(huán)用于重復(fù)執(zhí)行一段代碼塊,可以指定循環(huán)次數(shù)颖榜。

語法格式:

for (初始化; 條件; 更新) {
    // 執(zhí)行代碼塊
}

示例代碼:

for (int i = 0; i < 5; i++) {
    System.out.println("i的值為:" + i);
}

while循環(huán)

while循環(huán)用于重復(fù)執(zhí)行一段代碼塊棚饵,只要條件成立就一直執(zhí)行。

語法格式:

while (條件) {
    // 執(zhí)行代碼塊
}

示例代碼:

int i = 0;
while (i < 5) {
    System.out.println("i的值為:" + i);
    i++;
}

do-while循環(huán):

do-while循環(huán)用于重復(fù)執(zhí)行一段代碼塊掩完,先執(zhí)行一次代碼塊噪漾,然后判斷條件是否成立,如果成立則繼續(xù)執(zhí)行且蓬,否則退出循環(huán)欣硼。

語法格式:

do {
    // 執(zhí)行代碼塊
} while (條件);

示例代碼:

int i = 0;
do {
    System.out.println("i的值為:" + i);
    i++;
} while (i < 5);

四丶java數(shù)據(jù)類型

Java是一種強(qiáng)類型語言,這意味著在編寫代碼時必須指定變量的數(shù)據(jù)類型恶阴。Java中的數(shù)據(jù)類型可以分為兩類:基本數(shù)據(jù)類型和引用數(shù)據(jù)類型诈胜。

1.基本數(shù)據(jù)類型

Java中的基本數(shù)據(jù)類型包括:

  • 整型:byte、short冯事、int焦匈、long
  • 浮點(diǎn)型:float、double
  • 字符型:char
  • 布爾型:boolean

這些數(shù)據(jù)類型的取值范圍和存儲空間大小不同昵仅,具體如下:


2.引用數(shù)據(jù)類型

Java中的引用數(shù)據(jù)類型包括:

  • 接口
  • 數(shù)組

引用數(shù)據(jù)類型的變量存儲的是對象的引用缓熟,而不是對象本身。對象本身存儲在堆內(nèi)存中,而引用存儲在棧內(nèi)存中荚虚。

3.自動類型轉(zhuǎn)換和強(qiáng)制類型轉(zhuǎn)換

在Java中,如果兩個數(shù)據(jù)類型不同籍茧,可以進(jìn)行自動類型轉(zhuǎn)換或強(qiáng)制類型轉(zhuǎn)換版述。

自動類型轉(zhuǎn)換是指將一個數(shù)據(jù)類型的值賦給另一個數(shù)據(jù)類型的變量時,Java會自動將其轉(zhuǎn)換為目標(biāo)類型寞冯。例如渴析,將一個int類型的值賦給一個double類型的變量時,Java會自動將int類型轉(zhuǎn)換為double類型吮龄。

強(qiáng)制類型轉(zhuǎn)換是指將一個數(shù)據(jù)類型強(qiáng)制轉(zhuǎn)換為另一個數(shù)據(jù)類型俭茧。例如,將一個double類型的值強(qiáng)制轉(zhuǎn)換為int類型時漓帚,需要使用強(qiáng)制類型轉(zhuǎn)換符“()”母债。

4.字符串類型

Java中的字符串類型是引用數(shù)據(jù)類型,但是Java提供了一種特殊的語法來創(chuàng)建字符串尝抖,即使用雙引號將一段文本括起來毡们。例如:

String str = "Hello, world!";

字符串類型還提供了一些常用的方法,例如:

  • length():返回字符串的長度
  • charAt(int index):返回指定位置的字符
  • substring(int beginIndex, int endIndex):返回指定范圍內(nèi)的子字符串
  • equals(String str):比較兩個字符串是否相等

5.數(shù)組類型

Java中的數(shù)組是一種引用數(shù)據(jù)類型昧辽,可以存儲多個相同類型的值衙熔。數(shù)組的聲明方式如下:

數(shù)據(jù)類型[] 數(shù)組名 = new 數(shù)據(jù)類型[數(shù)組長度];

例如,聲明一個長度為5的int類型數(shù)組:

int[] arr = new int[5];

數(shù)組的訪問方式是通過下標(biāo)來訪問搅荞,下標(biāo)從0開始红氯。例如,訪問數(shù)組中的第一個元素:

int first = arr[0];

數(shù)組還提供了一些常用的方法咕痛,例如:

  • length:返回數(shù)組的長度
  • sort:對數(shù)組進(jìn)行排序
  • toString:將數(shù)組轉(zhuǎn)換為字符串

五丶java數(shù)據(jù)結(jié)構(gòu)

Java是一種面向?qū)ο蟮木幊陶Z言痢甘,因此它提供了許多數(shù)據(jù)結(jié)構(gòu)來處理和組織數(shù)據(jù)。以下是Java中常用的數(shù)據(jù)結(jié)構(gòu):

  • 數(shù)組(Array):數(shù)組是一組相同類型的數(shù)據(jù)元素的集合暇检。它們在內(nèi)存中是連續(xù)存儲的产阱,并且可以通過索引訪問。Java中的數(shù)組可以是一維或多維的块仆。

  • 集合(Collection):集合是一組對象的容器构蹬,可以動態(tài)地增加或減少元素。Java中的集合框架包括List悔据、Set和Map等庄敛。

  • 列表(List):列表是一種有序的集合,可以包含重復(fù)的元素科汗。Java中的ArrayList和LinkedList是常用的列表實(shí)現(xiàn)藻烤。

  • 集(Set):集是一種不允許重復(fù)元素的集合。Java中的HashSet和TreeSet是常用的集實(shí)現(xiàn)。

  • 映射(Map):映射是一種鍵值對的集合怖亭。Java中的HashMap和TreeMap是常用的映射實(shí)現(xiàn)涎显。

  • 棧(Stack):棧是一種后進(jìn)先出(LIFO)的數(shù)據(jù)結(jié)構(gòu)。Java中的Stack類實(shí)現(xiàn)了棧的基本操作兴猩。

  • 隊列(Queue):隊列是一種先進(jìn)先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)期吓。Java中的LinkedList類實(shí)現(xiàn)了隊列的基本操作。

  • 樹(Tree):樹是一種層次結(jié)構(gòu)倾芝,每個節(jié)點(diǎn)可以有多個子節(jié)點(diǎn)讨勤。Java中的TreeSet和TreeMap是基于樹的集和映射實(shí)現(xiàn)。

  • 圖(Graph):圖是一種由節(jié)點(diǎn)和邊組成的數(shù)據(jù)結(jié)構(gòu)晨另。Java中沒有內(nèi)置的圖實(shí)現(xiàn)潭千,但可以使用第三方庫來實(shí)現(xiàn)。

以上是Java中常用的數(shù)據(jù)結(jié)構(gòu)借尿,了解它們的特點(diǎn)和用法可以幫助我們更好地處理和組織數(shù)據(jù)刨晴。

六丶java面向?qū)ο?/h3>

作為APP逆向工程師,理解Java的面向?qū)ο缶幊淌侵陵P(guān)重要的垛玻。面向?qū)ο缶幊蹋∣bject-Oriented Programming割捅,簡稱OOP)是一種編程范式,它將程序中的對象作為基本單元帚桩,通過對象之間的交互來完成任務(wù)亿驾。以下是Java面向?qū)ο缶幊痰脑敿?xì)解釋:

  • 類和對象:類是定義對象的藍(lán)圖或模板,而對象是類的實(shí)例账嚎。類中包含了對象的屬性(字段/成員變量)和行為(方法/成員函數(shù))莫瞬。通過創(chuàng)建類的實(shí)例(對象),我們可以調(diào)用類中定義的方法來操作對象郭蕉。

  • 封裝:封裝是一種將數(shù)據(jù)和方法組合在一起的概念疼邀,目的是隱藏數(shù)據(jù)的具體實(shí)現(xiàn)細(xì)節(jié)并提供公共的接口訪問數(shù)據(jù)。通過訪問器(getter)和修改器(setter)方法召锈,可以控制對對象內(nèi)部數(shù)據(jù)的訪問和修改旁振。

  • 繼承:繼承是指一個類(子類/派生類)可以繼承另一個類(父類/基類)的屬性和方法。子類可以使用父類中的方法涨岁,并且可以在其中添加新的屬性和方法拐袜,或者修改父類的方法。繼承有助于實(shí)現(xiàn)代碼的重用和擴(kuò)展梢薪。

  • 多態(tài):多態(tài)是指一個對象可以以多種形式出現(xiàn)蹬铺。通過多態(tài),可以使用父類類型的引用變量來引用子類的實(shí)例對象秉撇。多態(tài)允許調(diào)用相同的方法在不同的對象上產(chǎn)生不同的行為甜攀,提供了代碼的靈活性和擴(kuò)展性秋泄。

  • 抽象類:抽象類是指不能被實(shí)例化的類,它定義了一組抽象的方法规阀,子類必須實(shí)現(xiàn)這些方法才能實(shí)例化恒序。抽象類提供了一種模板或約束,用于定義類的通用特性谁撼。

  • 接口:接口是一種純粹的抽象類奸焙,它只包含方法的聲明而沒有方法的實(shí)現(xiàn)。類可以實(shí)現(xiàn)一個或多個接口彤敛,從而獲得接口中定義的方法。接口提供了一種行為契約了赌,用于實(shí)現(xiàn)多態(tài)和代碼的解耦墨榄。

  • 構(gòu)造方法:構(gòu)造方法是一種特殊的方法,用于創(chuàng)建和初始化對象勿她。它與類同名袄秩,并且沒有返回類型。通過構(gòu)造方法逢并,可以設(shè)置對象的初始狀態(tài)和屬性之剧。

  • 成員變量和局部變量:成員變量是定義在類中的變量,可以被類的所有方法訪問砍聊。局部變量是定義在方法中的變量背稼,只能在方法內(nèi)部訪問。

通過理解和應(yīng)用面向?qū)ο缶幊痰母拍畈r颍憧梢愿玫亟M織和設(shè)計代碼蟹肘,使其更容易理解、擴(kuò)展和維護(hù)俯树。這對于進(jìn)行APP逆向工程和編寫高質(zhì)量的Java代碼都是至關(guān)重要的帘腹。

七丶java繼承關(guān)系鏈

在Java語言中,繼承關(guān)系是面向?qū)ο缶幊痰闹匾拍钪恍矶觥@^承關(guān)系形成一個類的層次結(jié)構(gòu)阳欲,被稱為繼承關(guān)系鏈。讓我詳細(xì)解釋一下Java繼承關(guān)系鏈的相關(guān)概念:

  • 類(Class):類是Java中基本的編程單元陋率,用于定義對象的屬性和方法球化。一個類可以作為另一個類的父類或超類,并可以被其他類繼承翘贮。

  • 父類(Superclass)和子類(Subclass):在繼承關(guān)系中赊窥,父類是指被繼承的類,子類是指繼承父類的類狸页。父類也稱為超類或基類锨能,子類也稱為派生類扯再。

  • 繼承(Inheritance):通過使用關(guān)鍵字extends,一個類可以繼承另一個類的屬性和方法址遇。子類繼承父類的特性熄阻,包括字段、方法和構(gòu)造函數(shù)倔约。繼承實(shí)現(xiàn)了代碼的重用和擴(kuò)展秃殉。

  • 單繼承(Single Inheritance):Java中的類只支持單繼承,即一個類只能有一個直接的父類浸剩。這是為了避免多繼承可能引發(fā)的復(fù)雜性和沖突問題钾军。

  • 多層繼承(Multilevel Inheritance):多層繼承是指繼承關(guān)系可以形成多個層次。一個類可以是另一個類的子類绢要,同時作為其他類的父類吏恭,形成一個繼承關(guān)系鏈。

  • 覆蓋(Override):子類可以通過定義相同的方法名和參數(shù)列表來覆蓋父類中已有的方法重罪。當(dāng)調(diào)用子類對象的該方法時樱哼,將執(zhí)行子類中定義的方法而不是父類中的方法。

  • 調(diào)用父類方法(Superclass Method Invocation):子類可以使用super關(guān)鍵字來調(diào)用父類的方法剿配。這通常用于子類想要在覆蓋方法中使用父類的實(shí)現(xiàn)時搅幅。

  • 抽象類(Abstract Class):抽象類是不能被實(shí)例化的類,它定義了一組抽象的方法呼胚。抽象類可以作為父類被其他類繼承茄唐,子類必須實(shí)現(xiàn)抽象類中的抽象方法。

  • 接口(Interface):接口是一種純粹的抽象類蝇更,它只包含方法的聲明而沒有方法的實(shí)現(xiàn)琢融。類可以實(shí)現(xiàn)一個或多個接口,從而獲得接口中定義的方法簿寂。

通過繼承關(guān)系鏈漾抬,我們可以建立不同類之間的層次結(jié)構(gòu),從而實(shí)現(xiàn)代碼的重用和擴(kuò)展常遂。子類可以繼承父類的屬性和方法纳令,并且可以添加新的屬性和方法,或者覆蓋父類的方法克胳。這使得代碼更加有組織平绩,易于理解和維護(hù)。作為APP逆向工程師漠另,了解和應(yīng)用繼承關(guān)系鏈將有助于您分析和修改Java代碼捏雌。

八丶java包的概念

Java中的包(Package)是一種組織類和接口的機(jī)制,它將相關(guān)的類和接口組織在一起笆搓,以便更好地管理和使用性湿。包可以看作是一個文件夾纬傲,其中包含了一組相關(guān)的類和接口。

Java中的包有以下幾個作用:

  • 避免命名沖突:Java中的包可以避免命名沖突肤频,因?yàn)椴煌陌锌梢杂邢嗤念惷?/p>

  • 組織類和接口:Java中的包可以將相關(guān)的類和接口組織在一起叹括,方便管理和使用。

  • 訪問控制:Java中的包可以使用訪問修飾符來控制類和接口的訪問權(quán)限宵荒。

  • 提供命名空間:Java中的包提供了命名空間汁雷,可以避免不同的類和接口之間的命名沖突。

Java中的包的命名規(guī)則是使用小寫字母报咳,多個單詞之間使用點(diǎn)號(.)分隔侠讯,例如:com.example.mypackage。

在Java中暑刃,使用包的語法是在類的開頭使用package語句來聲明所屬的包继低,例如:

package com.example.mypackage;

public class MyClass {
    // 類的代碼
}

在使用其他包中的類時,需要使用import語句來導(dǎo)入該類稍走,例如:

import java.util.ArrayList;

public class MyClass {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        // 使用ArrayList類
    }
}

需要注意的是,Java中的包是按照文件夾的形式組織的柴底,因此包名和文件夾的名稱要保持一致婿脸。例如,包名為com.example.mypackage的類應(yīng)該存放在com/example/mypackage目錄下的.java文件中柄驻。

NDK開發(fā)專題

一丶NDK數(shù)據(jù)類型

NDK(Native Development Kit)是Android提供的一種開發(fā)工具狐树,可以讓開發(fā)者使用C/C++語言編寫Android應(yīng)用程序。在NDK中鸿脓,數(shù)據(jù)類型是非常重要的概念抑钟,下面詳細(xì)介紹一下NDK中的數(shù)據(jù)類型。

1.基本數(shù)據(jù)類型

在NDK中野哭,基本數(shù)據(jù)類型與C/C++語言中的基本數(shù)據(jù)類型相同在塔,包括int、float拨黔、double蛔溃、char等。這些數(shù)據(jù)類型在NDK中的使用方法與C/C++語言中的使用方法相同篱蝇。

2.指針類型

指針類型在NDK中也是非常重要的數(shù)據(jù)類型贺待,它可以指向任何類型的數(shù)據(jù)。在NDK中零截,指針類型的聲明方式與C/C++語言中的聲明方式相同麸塞,例如:

int *p;

3.結(jié)構(gòu)體類型

結(jié)構(gòu)體類型在NDK中也是非常常見的數(shù)據(jù)類型,它可以將多個不同類型的數(shù)據(jù)組合在一起涧衙。在NDK中哪工,結(jié)構(gòu)體類型的聲明方式與C/C++語言中的聲明方式相同奥此,例如:

struct Person {
    char name[20];
    int age;
    float height;
};

4.枚舉類型

枚舉類型在NDK中也是非常常見的數(shù)據(jù)類型,它可以將一組相關(guān)的常量組合在一起正勒。在NDK中得院,枚舉類型的聲明方式與C/C++語言中的聲明方式相同,例如:

enum Color {
    RED,
    GREEN,
    BLUE
};

5.數(shù)組類型

數(shù)組類型在NDK中也是非常常見的數(shù)據(jù)類型章贞,它可以將多個相同類型的數(shù)據(jù)組合在一起祥绞。在NDK中,數(shù)組類型的聲明方式與C/C++語言中的聲明方式相同鸭限,例如:

int arr[10];

6.指向函數(shù)的指針類型

指向函數(shù)的指針類型在NDK中也是非常常見的數(shù)據(jù)類型蜕径,它可以指向任何類型的函數(shù)。在NDK中败京,指向函數(shù)的指針類型的聲明方式與C/C++語言中的聲明方式相同兜喻,例如:

int (*p)(int, int);

以上就是NDK中常見的數(shù)據(jù)類型,開發(fā)者在使用NDK進(jìn)行開發(fā)時赡麦,需要熟練掌握這些數(shù)據(jù)類型的使用方法朴皆。

二丶java反射和NDK結(jié)合

Java反射和NDK結(jié)合可以實(shí)現(xiàn)一些高級的功能,比如在NDK層面調(diào)用Java類的方法或者獲取Java類的屬性值泛粹。下面詳細(xì)介紹一下Java反射和NDK結(jié)合的實(shí)現(xiàn)方法遂铡。

1.在Java層面定義需要調(diào)用的方法或?qū)傩?/h4>

首先,在Java層面定義需要調(diào)用的方法或?qū)傩跃фⅲ纾?/p>

public class Test {
    private int value;

    public Test(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

2.使用反射獲取Java類的Class對象

在NDK層面扒接,需要使用反射獲取Java類的Class對象,例如:

jclass clazz = env->FindClass("com/example/Test");

3.獲取Java類的構(gòu)造方法

使用反射獲取Java類的構(gòu)造方法们衙,例如:

jmethodID constructor = env->GetMethodID(clazz, "<init>", "(I)V");

4.創(chuàng)建Java對象

使用反射創(chuàng)建Java對象钾怔,例如:

jobject obj = env->NewObject(clazz, constructor, 10);

5.獲取Java類的方法

使用反射獲取Java類的方法,例如:

jmethodID getValueMethod = env->GetMethodID(clazz, "getValue", "()I");
jmethodID setValueMethod = env->GetMethodID(clazz, "setValue", "(I)V");

6.調(diào)用Java類的方法

使用反射調(diào)用Java類的方法蒙挑,例如:

int value = env->CallIntMethod(obj, getValueMethod);
env->CallVoidMethod(obj, setValueMethod, 20);

7.獲取Java類的屬性值

使用反射獲取Java類的屬性值宗侦,例如:

jfieldID valueField = env->GetFieldID(clazz, "value", "I");
int value = env->GetIntField(obj, valueField);

8.設(shè)置Java類的屬性值

使用反射設(shè)置Java類的屬性值,例如:

env->SetIntField(obj, valueField, 30);

三丶JNI調(diào)用java函數(shù)對象和訪問java方法和類

一.JNI調(diào)用java函數(shù)對象

在JNI中忆蚀,可以通過JNIEnv對象調(diào)用Java中的方法凝垛。JNIEnv對象是一個指向JNI環(huán)境的指針,它提供了一組函數(shù)蜓谋,可以用來訪問Java對象和調(diào)用Java方法梦皮。

在調(diào)用Java方法之前,需要先獲取Java方法的ID桃焕〗?希可以通過調(diào)用JNIEnv對象的GetMethodID函數(shù)來獲取Java方法的ID。GetMethodID函數(shù)的參數(shù)包括Java類的對象观堂、Java方法的名稱和Java方法的簽名让网。

獲取到Java方法的ID之后呀忧,就可以通過調(diào)用JNIEnv對象的CallXXXMethod函數(shù)來調(diào)用Java方法了。其中溃睹,XXX表示Java方法的返回值類型而账,可以是Int、Boolean因篇、Object等泞辐。

下面是一個JNI調(diào)用Java方法的示例代碼:

JNIEXPORT void JNICALL Java_com_example_test_TestJNI_callJavaMethod(JNIEnv *env, jobject obj) {
    jclass clazz = env->GetObjectClass(obj);
    jmethodID methodId = env->GetMethodID(clazz, "javaMethod", "()V");
    env->CallVoidMethod(obj, methodId);
}

在上面的代碼中,首先通過GetObjectClass函數(shù)獲取Java類的對象竞滓,然后通過GetMethodID函數(shù)獲取Java方法的ID咐吼,最后通過CallVoidMethod函數(shù)調(diào)用Java方法。

需要注意的是商佑,JNI調(diào)用Java方法時锯茄,需要保證Java方法的訪問權(quán)限是public。否則茶没,在調(diào)用Java方法時會拋出IllegalAccessException異常肌幽。

二.訪問java方法和類

作為APP逆向工程師,掌握J(rèn)ava方法和類的概念是非常重要的抓半。下面我將詳細(xì)解釋Java中方法和類的相關(guān)內(nèi)容:

1.方法(Method)

  • 方法是一段可重用的代碼塊喂急,用于執(zhí)行特定的動作或完成特定的任務(wù)。
  • 方法包含方法名琅关、參數(shù)列表(可選)、返回類型和方法體讥蔽。
  • 方法名用于唯一識別方法涣易,參數(shù)列表是傳遞給方法的值,返回類型指定了方法返回的值類型冶伞。
  • 方法可以接受參數(shù)并返回一個值新症,也可以是無參的無返回值方法。
  • 方法通過調(diào)用(使用方法名和參數(shù)列表)來執(zhí)行响禽。

2.類(Class)

  • 類是用于創(chuàng)建對象的模板或藍(lán)圖徒爹,它定義了對象的屬性和行為。
  • 類是Java中的基本編程單元芋类,所有對象都是由類實(shí)例化而來隆嗅。
  • 類由字段(成員變量)和方法(成員函數(shù))組成。
  • 字段是類中的變量侯繁,用于存儲對象的狀態(tài)信息胖喳。
  • 方法是類中定義的行為,用于操作對象的狀態(tài)贮竟。

3.對象(Object)

  • 對象是類的實(shí)例丽焊,通過使用關(guān)鍵字new和類的構(gòu)造方法來創(chuàng)建。
  • 對象具有類定義的屬性和行為,可以訪問和操作對象的字段和方法掀潮。
  • 對象可以獨(dú)立地存在浪汪、具有唯一的狀態(tài),并且可以與其他對象進(jìn)行交互雌贱。

4.構(gòu)造方法(Constructor)

  • 構(gòu)造方法是一種特殊的方法啊送,用于創(chuàng)建和初始化對象。
  • 構(gòu)造方法具有與類相同的名稱帽芽,但沒有返回類型删掀。
  • 構(gòu)造方法在使用new關(guān)鍵字創(chuàng)建對象時被調(diào)用,用于初始化對象的狀態(tài)导街。
  • Java中可以定義多個構(gòu)造方法披泪,通過不同的參數(shù)列表來區(qū)分。

5.靜態(tài)方法(Static Method)

  • 靜態(tài)方法是類級別的方法搬瑰,不需要創(chuàng)建類的實(shí)例即可調(diào)用款票。
  • 靜態(tài)方法使用關(guān)鍵字static修飾,并且只能訪問靜態(tài)成員變量和調(diào)用其他靜態(tài)方法泽论。
  • 靜態(tài)方法通常用于實(shí)用功能和工具方法艾少,不需要依賴于特定的對象。

6.封裝(Encapsulation)

  • 封裝是將數(shù)據(jù)和方法組合在一個單元中翼悴,用于數(shù)據(jù)隱藏和訪問控制缚够。
  • 封裝通過使用訪問修飾符(如private、public等)來限制對類的字段和方法的訪問鹦赎。

7.繼承(Inheritance)

  • 繼承是一種面向?qū)ο蟮母拍畹危试S一個類繼承另一個類的屬性和方法。
  • 繼承通過使用關(guān)鍵字extends來實(shí)現(xiàn)古话,子類繼承父類的特性并可以添加自己的功能雏吭。
  • 繼承提供了代碼重用和擴(kuò)展的機(jī)制,實(shí)現(xiàn)了類之間的層次結(jié)構(gòu)陪踩。

8.多態(tài)(Polymorphism)

  • 多態(tài)是面向?qū)ο缶幊痰囊粋€重要概念杖们,允許使用一個對象以多種不同的方式呈現(xiàn)。
  • 多態(tài)通過使用父類類型的引用變量來引用子類的對象來實(shí)現(xiàn)肩狂。
  • 多態(tài)提供了靈活性和可擴(kuò)展性摘完,通過方法的動態(tài)綁定,可以在運(yùn)行時決定調(diào)用哪個方法傻谁。

掌握方法和類的概念是進(jìn)行APP逆向工程的關(guān)鍵描焰。了解如何創(chuàng)建和使用方法,如何定義和實(shí)例化類以及如何使用封裝、繼承和多態(tài)等概念荆秦,將使您能夠更好地分析和修改Java代碼篱竭。

三丶Hook框架專題

Frida Hook

Frida是一款強(qiáng)大的動態(tài)分析工具,可以用于APP逆向工程步绸、安全測試掺逼、漏洞挖掘等方面。本文將從Frida開發(fā)和調(diào)試環(huán)境搭建瓤介、Frida構(gòu)造數(shù)組-對象吕喘、Frida與脫殼、Frida Hook殼與插件dex刑桑、Frida編譯源碼氯质、Frida檢測反調(diào)試等六個方向展開詳細(xì)講解。

一祠斧、Frida開發(fā)和調(diào)試環(huán)境搭建

1.安裝Frida

Frida支持Windows闻察、macOS、Linux等多個平臺琢锋,可以在官網(wǎng)下載對應(yīng)平臺的安裝包進(jìn)行安裝辕漂。安裝完成后,可以在命令行中輸入frida -version命令來檢查Frida是否安裝成功吴超。

2.安裝Frida-Server

Frida-Server是Frida的核心組件钉嘹,它運(yùn)行在目標(biāo)設(shè)備上,與Frida客戶端進(jìn)行通信鲸阻。在使用Frida進(jìn)行APP逆向工程時跋涣,需要將Frida-Server安裝到目標(biāo)設(shè)備上。Frida-Server的安裝方法可以參考官方文檔鸟悴。

3.安裝Frida-Tools

Frida-Tools是Frida的一組工具陈辱,包括frida-ps、frida-trace遣臼、frida-discover等性置。這些工具可以幫助我們更方便地使用Frida進(jìn)行動態(tài)分析拾并∽嵫撸可以在命令行中輸入pip install frida-tools命令來安裝Frida-Tools。

二嗅义、Frida構(gòu)造數(shù)組-對象

在使用Frida進(jìn)行APP逆向工程時屏歹,經(jīng)常需要構(gòu)造一些數(shù)組和對象來進(jìn)行數(shù)據(jù)操作。下面介紹一些常用的構(gòu)造方法之碗。

1.構(gòu)造數(shù)組

可以使用Frida提供的Java.array()方法來構(gòu)造數(shù)組蝙眶。例如,構(gòu)造一個長度為3的int數(shù)組:

var arr = Java.array("int", [1, 2, 3]);

2.構(gòu)造對象

可以使用Frida提供的Java.use()方法來構(gòu)造對象。例如幽纷,構(gòu)造一個java.lang.String對象:

var str = Java.use("java.lang.String").$new("hello");

三式塌、Frida與脫殼

在進(jìn)行APP逆向工程時,經(jīng)常需要對APP進(jìn)行脫殼操作友浸,以便更好地進(jìn)行分析峰尝。下面介紹一些常用的脫殼方法。

1.使用Frida-Objection進(jìn)行脫殼

Frida-Objection是一款基于Frida的移動設(shè)備安全測試框架收恢,可以用于APP脫殼武学、數(shù)據(jù)泄露檢測、SSL Pinning繞過等方面伦意』鹬希可以在命令行中輸入pip install objection命令來安裝Frida-Objection。

使用Frida-Objection進(jìn)行脫殼的步驟如下

  • 1)啟動Frida-Server

在目標(biāo)設(shè)備上啟動Frida-Server驮肉。

  • 2)啟動Frida-Objection

在命令行中輸入objection explore命令來啟動Frida-Objection熏矿。

  • 3)進(jìn)入APP

在Frida-Objection的命令行中輸入android jailbreak命令來進(jìn)入APP。

  • 4)脫殼

在Frida-Objection的命令行中輸入dump -h命令來進(jìn)行脫殼操作缆八。

2.使用Frida進(jìn)行脫殼

可以使用Frida提供的Java.perform()方法來進(jìn)行脫殼操作曲掰。下面是一個簡單的脫殼腳本示例:

Java.perform(function () {
    var BaseDexClassLoader = Java.use("dalvik.system.BaseDexClassLoader");
    var DexFile = Java.use("dalvik.system.DexFile");
    var PathClassLoader = Java.use("dalvik.system.PathClassLoader");
    var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
    var File = Java.use("java.io.File");
    var System = Java.use("java.lang.System");
    var Runtime = Java.use("java.lang.Runtime");
    var Process = Java.use("android.os.Process");
    var String = Java.use("java.lang.String");
    var Arrays = Java.use("java.util.Arrays");

    var classLoader = Java.classFactory.loader;
    var dexFile = DexFile.$new("/data/app/com.example.app-1/base.apk");
    var dexElements = BaseDexClassLoader.getDeclaredField("dexElements");
    dexElements.setAccessible(true);
    var elements = dexElements.get(classLoader);
    var newElements = Arrays.copyOf(elements, elements.length);
    var dex = DexFile.$new(File.$new("/data/data/com.example.app/files/1.dex"));
    var newElement = DexClassLoader.$new("/data/data/com.example.app/files", null, null, classLoader).findClass("com.example.app.MainActivity").getDeclaredMethod("main", String.arrayOf(String));
    newElements[newElements.length - 1] = newElement.invoke(null, null);
    dexElements.set(classLoader, newElements);

    System.loadLibrary("native-lib");
    var handle = Runtime.getRuntime().exec("su");
    var outputStream = handle.getOutputStream();
    outputStream.write(("kill " + Process.myPid() + "\n").getBytes());
    outputStream.flush();
    outputStream.close();
});

四、Frida Hook殼與插件dex

在進(jìn)行APP逆向工程時奈辰,經(jīng)常需要Hook一些殼和插件dex栏妖,以便更好地進(jìn)行分析。下面介紹一些常用的Hook方法奖恰。

1.Hook殼

可以使用Frida提供的Java.use()方法來Hook殼吊趾。下面是一個簡單的Hook殼腳本示例:

Java.perform(function () {
    var BaseDexClassLoader = Java.use("dalvik.system.BaseDexClassLoader");
    var DexFile = Java.use("dalvik.system.DexFile");
    var PathClassLoader = Java.use("dalvik.system.PathClassLoader");
    var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
    var File = Java.use("java.io.File");
    var System = Java.use("java.lang.System");
    var Runtime = Java.use("java.lang.Runtime");
    var Process = Java.use("android.os.Process");
    var String = Java.use("java.lang.String");
    var Arrays = Java.use("java.util.Arrays");

    var classLoader = Java.classFactory.loader;
    var dexFile = DexFile.$new("/data/app/com.example.app-1/base.apk");
    var dexElements = BaseDexClassLoader.getDeclaredField("dexElements");
    dexElements.setAccessible(true);
    var elements = dexElements.get(classLoader);
    var newElements = Arrays.copyOf(elements, elements.length);
    var dex = DexFile.$new(File.$new("/data/data/com.example.app/files/1.dex"));
    var newElement = DexClassLoader.$new("/data/data/com.example.app/files", null, null, classLoader).findClass("com.example.app.MainActivity").getDeclaredMethod("main", String.arrayOf(String));
    newElements[newElements.length - 1] = newElement.invoke(null, null);
    dexElements.set(classLoader, newElements);

    System.loadLibrary("native-lib");
    var handle = Runtime.getRuntime().exec("su");
    var outputStream = handle.getOutputStream();
    outputStream.write(("kill " + Process.myPid() + "\n").getBytes());
    outputStream.flush();
    outputStream.close();
});

2.Hook插件dex

可以使用Frida提供的Java.use()方法來Hook插件dex。下面是一個簡單的Hook插件dex腳本示例:

Java.perform(function () {
    var classLoader = Java.classFactory.loader;
    var dexFile = Java.use("dalvik.system.DexFile").$new("/data/data/com.example.app/files/1.dex");
    var dexElements = Java.cast(classLoader, Java.use("dalvik.system.BaseDexClassLoader")).pathList.dexElements;
    var newElements = Java.array("dalvik.system.DexFile$Element", [dexFile.entries()[0]]);
    var elements = Java.array("dalvik.system.DexFile$Element", dexElements);
    var allElements = Java.array("dalvik.system.DexFile$Element", [newElements[0], elements[0]]);
    Java.cast(classLoader, Java.use("dalvik.system.BaseDexClassLoader")).pathList.dexElements = allElements;
});

五瑟啃、Frida編譯源碼

在進(jìn)行APP逆向工程時论泛,經(jīng)常需要編譯一些源碼,以便更好地進(jìn)行分析蛹屿。下面介紹一些常用的編譯方法屁奏。

1.使用dex2jar進(jìn)行編譯

dex2jar是一款將dex文件轉(zhuǎn)換為jar文件的工具,可以用于將APP的dex文件轉(zhuǎn)換為可讀的jar文件错负》仄埃可以在命令行中輸入dex2jar.sh命令來使用dex2jar進(jìn)行編譯。

2.使用apktool進(jìn)行編譯

apktool是一款反編譯和重新打包APK的工具犹撒,可以用于對APP進(jìn)行反編譯和重新打包折联。可以在命令行中輸入apktool d命令來對APP進(jìn)行反編譯识颊,輸入apktool b命令來重新打包APP诚镰。

六、Frida檢測反調(diào)試

在進(jìn)行APP逆向工程時,經(jīng)常需要檢測APP是否被反調(diào)試清笨。下面介紹一些常用的檢測方法月杉。

1.Hook反調(diào)試方法

可以使用Frida提供的Java.use()方法來Hook反調(diào)試方法。下面是一個簡單的Hook反調(diào)試方法腳本示例:

Java.perform(function () {
    var Debug = Java.use("android.os.Debug");
    Debug.isDebuggerConnected.implementation = function () {
        return false;
    };
});

2.Hook反調(diào)試變量

可以使用Frida提供的Java.use()方法來Hook反調(diào)試變量抠艾。下面是一個簡單的Hook反調(diào)試變量腳本示例:

Java.perform(function () {
    var ActivityThread = Java.use("android.app.ActivityThread");
    ActivityThread.currentApplication.implementation = function () {
        var app = this.currentApplication();
        var packageName = app.getPackageName();
        if (packageName.indexOf("com.example.app") != -1) {
            return null;
        } else {
            return app;
        }
    };
});

以上就是Frida HOOk專題的詳細(xì)講解沙合,希望對APP逆向工程愛好者有所幫助。

APP殼處理

一丶APP加固原理講解

APP加固是指對移動應(yīng)用程序進(jìn)行安全性加固處理跌帐,以提高其防護(hù)能力首懈,防止被攻擊者進(jìn)行逆向工程、篡改谨敛、數(shù)據(jù)泄露等惡意行為究履。下面是APP加固的原理詳解:

  • 代碼混淆:代碼混淆是通過對源代碼進(jìn)行修改和優(yōu)化,使得代碼結(jié)構(gòu)和邏輯變得復(fù)雜和難以理解脸狸,從而增加攻擊者對應(yīng)用程序的逆向分析難度最仑。通過重命名變量和函數(shù)名、添加冗余代碼炊甲、修改控制流程等手段泥彤,使得攻擊者很難理解代碼的含義和執(zhí)行流程,從而降低逆向工程的成功率卿啡。

  • 加密保護(hù):加密保護(hù)是將敏感數(shù)據(jù)或關(guān)鍵代碼進(jìn)行加密處理吟吝,以防止攻擊者直接獲取和修改數(shù)據(jù)。常見的加密手段包括對字符串颈娜、配置文件剑逃、算法、網(wǎng)絡(luò)通信等進(jìn)行加密處理官辽,使用對稱加密或非對稱加密算法來保護(hù)數(shù)據(jù)的機(jī)密性和完整性蛹磺。

  • 安全存儲:安全存儲是指將應(yīng)用程序的敏感數(shù)據(jù)(如用戶憑證、密鑰同仆、證書等)存儲在加密容器中萤捆,防止被惡意應(yīng)用或攻擊者獲取。加固工具可以提供安全存儲功能俗批,對敏感數(shù)據(jù)進(jìn)行加密處理俗或,并使用密鑰保護(hù)數(shù)據(jù)的安全。

  • 反調(diào)試與反動態(tài)分析:為了防止攻擊者對應(yīng)用程序進(jìn)行調(diào)試和動態(tài)分析以獲取關(guān)鍵信息扶镀,加固工具可以采用反調(diào)試和反動態(tài)分析的技術(shù)手段蕴侣。例如焰轻,檢測調(diào)試器的存在臭觉、監(jiān)控運(yùn)行環(huán)境的變化、阻止動態(tài)加載庫的注入等方法,來防止應(yīng)用程序被攻擊者分析和修改蝠筑。

  • 安全認(rèn)證與防篡改:為了確保應(yīng)用程序的合法性和完整性狞膘,加固工具可以通過數(shù)字簽名、摘要校驗(yàn)什乙、代碼完整性校驗(yàn)等方式進(jìn)行安全認(rèn)證挽封。這些技術(shù)可以防止應(yīng)用程序被篡改、植入惡意代碼或被惡意應(yīng)用替換臣镣。

  • 運(yùn)行時保護(hù):針對應(yīng)用程序在運(yùn)行過程中的安全問題辅愿,加固工具可以提供運(yùn)行時保護(hù)機(jī)制。例如忆某,檢測和阻止內(nèi)存溢出点待、緩沖區(qū)溢出、代碼注入弃舒、動態(tài)加載庫的非法調(diào)用等運(yùn)行時攻擊行為癞埠,以提高應(yīng)用程序的安全性。

需要注意的是聋呢,雖然APP加固可以提高應(yīng)用程序的安全性苗踪,但并不能完全消除所有的安全風(fēng)險。攻擊者可能利用高級的逆向工程技術(shù)和漏洞來攻擊加固后的應(yīng)用程序削锰。因此通铲,開發(fā)人員和安全團(tuán)隊?wèi)?yīng)綜合使用多種安全技術(shù)和策略,以實(shí)現(xiàn)全面的應(yīng)用程序安全保護(hù)器贩。

二丶如何識別APP是否加殼

要識別一個APP是否加殼测暗,可以考慮以下幾個方法:

  • 靜態(tài)分析:通過對APP進(jìn)行靜態(tài)分析,查看APP的文件結(jié)構(gòu)和內(nèi)容磨澡,可以初步判斷是否存在加殼情況碗啄。加殼通常會在原始二進(jìn)制文件中插入一段代碼用于解殼,因此可以嘗試查找類似解殼代碼的特征稳摄。

  • 動態(tài)調(diào)試:使用調(diào)試工具對APP進(jìn)行動態(tài)調(diào)試稚字,觀察其行為和運(yùn)行過程,可以獲取更多的信息來判斷是否加殼厦酬。例如胆描,檢測是否有解殼行為、查看內(nèi)存中的解殼代碼仗阅、監(jiān)控應(yīng)用程序加載的動態(tài)庫等昌讲。

  • 反編譯分析:將APP進(jìn)行反編譯,還原出源代碼或近似源代碼减噪,可以通過分析反編譯后的代碼來判斷是否經(jīng)過加殼處理短绸。加殼工具通常會在代碼中插入一些特殊的指令和函數(shù)調(diào)用车吹,或者對代碼進(jìn)行重寫、重構(gòu)醋闭,這些都可以通過反編譯分析來判斷窄驹。

  • 使用專用工具:有一些專門用于檢測和識別加殼的工具可以輔助進(jìn)行判斷。例如证逻,AndroGuard乐埠、Frida、IDA Pro等工具都具有一定的能力來分析和識別加殼情況囚企。

需要注意的是丈咐,加殼技術(shù)不斷發(fā)展和演進(jìn),新的加殼方式可能會繞過某些常規(guī)的識別方法龙宏。因此扯罐,識別加殼是一個持續(xù)的挑戰(zhàn),需要結(jié)合多種方法和工具烦衣,并不依賴于單一的判斷依據(jù)歹河。

三丶frida-dump脫殼技巧

Frida-dump是基于Frida框架開發(fā)的一種用于脫殼(dump)加殼應(yīng)用程序的工具。它可以在運(yùn)行時對應(yīng)用程序進(jìn)行動態(tài)注入花吟,繞過加固防護(hù)措施秸歧,提取應(yīng)用程序的解密后的內(nèi)存鏡像。下面是對Frida-dump脫殼技巧的詳細(xì)解釋:

  • Frida環(huán)境搭建:首先需要在計算機(jī)上安裝Frida框架衅澈,并配置好運(yùn)行環(huán)境键菱。具體可以參考Frida官方文檔提供的安裝和配置指南。

  • 定位加固點(diǎn):在使用Frida-dump之前今布,需要了解目標(biāo)應(yīng)用程序所使用的加固方案经备。對于不同的加固方案,可能需要使用特定的Frida腳本來繞過保護(hù)機(jī)制部默。對于常見的加固方案如DEX加密侵蒙、函數(shù)解密等,可以通過靜態(tài)分析或調(diào)試等手段來確認(rèn)加固點(diǎn)的位置傅蹂。

  • 編寫Frida腳本:根據(jù)目標(biāo)應(yīng)用程序的加固方案纷闺,可以編寫自定義的Frida腳本,用于在應(yīng)用程序運(yùn)行時定位和繞過加固點(diǎn)份蝴。Frida腳本使用JavaScript編寫犁功,可以利用Frida提供的API來進(jìn)行代碼注入和動態(tài)調(diào)試。

  • 動態(tài)注入和脫殼:使用編寫好的Frida腳本婚夫,運(yùn)行目標(biāo)應(yīng)用程序浸卦,并將腳本注入到應(yīng)用程序的進(jìn)程中。腳本會在應(yīng)用程序運(yùn)行時自動執(zhí)行案糙,通過監(jiān)控內(nèi)存訪問來定位解密后的內(nèi)存區(qū)域限嫌,并將其導(dǎo)出到本地文件系統(tǒng)中靴庆,完成脫殼操作。

需要注意的是萤皂,使用Frida-dump進(jìn)行脫殼是一項高級技術(shù),需要對移動應(yīng)用程序的內(nèi)部結(jié)構(gòu)和加固機(jī)制有一定的了解匣椰。此外裆熙,脫殼行為可能涉及到法律和道德問題,因此應(yīng)謹(jǐn)慎使用禽笑,并僅用于合法的安全評估和研究目的入录。此外,使用Frida-dump等脫殼工具進(jìn)行脫殼操作也有可能觸發(fā)應(yīng)用程序自身的反調(diào)試和反破解機(jī)制佳镜,導(dǎo)致脫殼失敗或應(yīng)用程序異常終止僚稿。

smail語法

當(dāng)進(jìn)行APP逆向工程時,了解smali語法是非常重要的蟀伸。下面我將詳細(xì)介紹smali語法中的數(shù)據(jù)類型蚀同、常量聲明語法、方法函數(shù)的聲明啊掏、構(gòu)造函數(shù)的聲明蠢络、靜態(tài)代碼塊的聲明以及基于接口的調(diào)用。

一丶數(shù)據(jù)類型:

smali支持以下數(shù)據(jù)類型:

  • Z: boolean類型
  • B: byte類型
  • S: short類型
  • C: char類型
  • I: int類型
  • J: long類型
  • F: float類型
  • D: double類型
    L 類名 ;: 引用類型
    例如迟蜜,I 表示int類型刹孔,Ljava/lang/String; 表示引用類型為 java.lang.String。

二丶常量聲明語法:

常量聲明使用 const 指令娜睛,語法如下:

const 寄存器, 常量值

例如髓霞,const v0, 10 表示將寄存器 v0 設(shè)置為整數(shù)常量值 10。

三丶方法函數(shù)的聲明:

方法函數(shù)的聲明使用 method 關(guān)鍵字畦戒,語法如下:

.method 修飾符 返回類型 方法名(參數(shù)列表) [異常列表]

    方法體

.end method

修飾符包括:public方库、private、protected障斋、abstract薪捍、static 等。
返回類型可以是任意的數(shù)據(jù)類型配喳,使用上述提到的數(shù)據(jù)類型表示酪穿。
參數(shù)列表使用逗號分隔,每個參數(shù)由類型和變量名組成晴裹。

例如被济,

.method public static add(II)I

    ...

.end method

表示一個名為 add 的公開靜態(tài)方法,接受兩個 int 類型的參數(shù)涧团,并返回一個 int 類型的值只磷。

四丶構(gòu)造函數(shù)的聲明:

構(gòu)造函數(shù)的聲明使用 constructor 關(guān)鍵字经磅,語法如下:

.constructor 修飾符(參數(shù)列表) [異常列表]

    構(gòu)造函數(shù)體

.end constructor

構(gòu)造函數(shù)和方法函數(shù)的聲明類似,但沒有返回類型钮追。

例如预厌,

.constructor public <init>()V

    ...

.end constructor

表示一個公開的無參數(shù)構(gòu)造函數(shù)。

五丶靜態(tài)代碼塊的聲明:

靜態(tài)代碼塊的聲明使用 clinit 關(guān)鍵字元媚,語法如下:

.clinit

    靜態(tài)代碼塊體

.end clinit

靜態(tài)代碼塊在類加載時執(zhí)行轧叽,并且只會執(zhí)行一次。

例如刊棕,

.clinit

    # 執(zhí)行靜態(tài)代碼塊的操作

.end clinit

表示一個靜態(tài)代碼塊炭晒。

六丶基于接口的調(diào)用:

在smali中,使用 invoke-interface 指令來調(diào)用接口的方法甥角。語法如下:

invoke-interface {參數(shù)列表}, 接口類型->方法簽名

其中网严,參數(shù)列表 是傳遞給方法的參數(shù),接口類型 是要調(diào)用的接口類型嗤无,方法簽名 是要調(diào)用的方法的描述符震束。

例如,

invoke-interface {v0}, Ljava/util/Iterator;->next()Ljava/lang/Object;

表示調(diào)用 java.util.Iterator 接口的 next 方法当犯。

這些是smali語法中關(guān)于數(shù)據(jù)類型驴一、常量聲明、方法函數(shù)聲明灶壶、構(gòu)造函數(shù)聲明肝断、靜態(tài)代碼塊聲明和基于接口的調(diào)用的詳細(xì)介紹。通過理解這些語法規(guī)則驰凛,你可以更好地分析和修改逆向工程中的smali代碼胸懈。記住,smali語法是龐大而復(fù)雜的恰响,需要不斷實(shí)踐和探索才能熟練掌握趣钱。

APP RPC專題

一丶frida rpc插件編寫

Frida是一款強(qiáng)大的動態(tài)分析工具,可以用于對移動應(yīng)用程序進(jìn)行逆向分析和調(diào)試胚宦。其中首有,RPC(Remote Procedure Call)是Frida的一個重要功能,可以讓我們在Frida客戶端和Frida服務(wù)器之間進(jìn)行通信枢劝,從而實(shí)現(xiàn)更加靈活和高效的動態(tài)分析井联。

在本文中,我們將介紹如何編寫Frida RPC插件您旁,以便在Frida客戶端和Frida服務(wù)器之間進(jìn)行通信烙常。

1.創(chuàng)建Frida RPC插件

首先,我們需要創(chuàng)建一個Frida RPC插件鹤盒。在Frida中蚕脏,RPC插件是一個JavaScript文件侦副,可以通過Frida客戶端和Frida服務(wù)器之間的RPC通信進(jìn)行加載和執(zhí)行。

下面是一個簡單的Frida RPC插件示例:

rpc.exports = {
  add: function (a, b) {
    return a + b;
  },
  sub: function (a, b) {
    return a - b;
  }
};

在這個示例中驼鞭,我們定義了兩個函數(shù)add和sub秦驯,它們分別實(shí)現(xiàn)了加法和減法運(yùn)算。這些函數(shù)可以通過Frida客戶端和Frida服務(wù)器之間的RPC通信進(jìn)行調(diào)用挣棕。

2.加載Frida RPC插件

一旦我們創(chuàng)建了Frida RPC插件译隘,我們就可以將其加載到Frida客戶端和Frida服務(wù)器之間進(jìn)行通信。在Frida客戶端中穴张,我們可以使用以下代碼加載Frida RPC插件:

const rpc = new frida.Rpc(peer);
const plugin = rpc.use("path/to/plugin.js");

在這個示例中细燎,我們首先創(chuàng)建了一個Frida RPC對象rpc两曼,然后使用use方法加載了Frida RPC插件皂甘。peer參數(shù)指定了Frida服務(wù)器的地址和端口號。

3.調(diào)用Frida RPC插件

一旦我們加載了Frida RPC插件悼凑,我們就可以通過Frida客戶端和Frida服務(wù)器之間的RPC通信調(diào)用其中定義的函數(shù)偿枕。在Frida客戶端中,我們可以使用以下代碼調(diào)用Frida RPC插件中的函數(shù):

const result = await plugin.add(1, 2);
console.log(result);

在這個示例中户辫,我們調(diào)用了Frida RPC插件中的add函數(shù)渐夸,并傳遞了兩個參數(shù)1和2。調(diào)用結(jié)果將被存儲在result變量中渔欢,并打印到控制臺上墓塌。

4.總結(jié)

在本文中,我們介紹了如何編寫Frida RPC插件奥额,并在Frida客戶端和Frida服務(wù)器之間進(jìn)行通信苫幢。通過使用Frida RPC插件,我們可以實(shí)現(xiàn)更加靈活和高效的動態(tài)分析垫挨,從而更好地理解和保護(hù)移動應(yīng)用程序韩肝。

二丶使用frida遠(yuǎn)程調(diào)用java代碼

在使用Frida進(jìn)行遠(yuǎn)程調(diào)用Java代碼之前,需要先了解一些基本概念和步驟九榔。

1.什么是Frida哀峻?

Frida是一款基于JavaScript的動態(tài)代碼注入工具,可以用于對Android和iOS應(yīng)用進(jìn)行動態(tài)分析和修改哲泊。Frida可以在運(yùn)行時修改應(yīng)用程序的行為剩蟀,包括函數(shù)調(diào)用、參數(shù)傳遞切威、返回值等喻旷。

2.什么是RPC?

RPC(Remote Procedure Call)是一種遠(yuǎn)程過程調(diào)用協(xié)議牢屋,可以讓客戶端調(diào)用遠(yuǎn)程服務(wù)器上的函數(shù)且预,就像調(diào)用本地函數(shù)一樣槽袄。RPC可以讓應(yīng)用程序在不同的計算機(jī)上進(jìn)行通信,實(shí)現(xiàn)分布式計算锋谐。

3.如何使用Frida進(jìn)行RPC遍尺?

Frida提供了一個RPC機(jī)制,可以讓JavaScript代碼在本地和遠(yuǎn)程設(shè)備之間進(jìn)行通信涮拗。通過RPC乾戏,可以在本地設(shè)備上編寫JavaScript代碼,然后在遠(yuǎn)程設(shè)備上執(zhí)行該代碼三热,實(shí)現(xiàn)遠(yuǎn)程調(diào)用Java代碼的功能鼓择。

下面是使用Frida進(jìn)行遠(yuǎn)程調(diào)用Java代碼的詳細(xì)步驟:

  • 在本地設(shè)備上編寫JavaScript代碼,實(shí)現(xiàn)遠(yuǎn)程調(diào)用Java代碼的功能就漾。例如呐能,可以使用Java.perform()方法獲取Java虛擬機(jī)實(shí)例,并調(diào)用Java類的方法抑堡。
Java.perform(function() {
    var MainActivity = Java.use("com.example.MainActivity");
    MainActivity.sayHello.implementation = function() {
        console.log("Hello from Frida!");
        return this.sayHello();
    }
});
  • 將JavaScript代碼保存為一個文件摆出,例如rpc.js。

  • 在遠(yuǎn)程設(shè)備上安裝Frida首妖,并啟動Frida服務(wù)偎漫。可以使用以下命令啟動Frida服務(wù):

frida-server -l 0.0.0.0
  • 在遠(yuǎn)程設(shè)備上啟動目標(biāo)應(yīng)用程序有缆,并記錄應(yīng)用程序的進(jìn)程ID象踊。

  • 在本地設(shè)備上使用Frida命令行工具連接到遠(yuǎn)程設(shè)備,并加載JavaScript代碼棚壁”兀可以使用以下命令連接到遠(yuǎn)程設(shè)備:

frida -U -f com.example.app -l rpc.js

其中,com.example.app是目標(biāo)應(yīng)用程序的包名灌曙,rpc.js是保存JavaScript代碼的文件名菊碟。

  • 在本地設(shè)備上執(zhí)行JavaScript代碼,實(shí)現(xiàn)遠(yuǎn)程調(diào)用Java代碼的功能在刺∧婧Γ可以使用以下命令在Frida控制臺中執(zhí)行JavaScript代碼:
rpc.exports.sayHello();

其中,sayHello()是在JavaScript代碼中定義的函數(shù)名蚣驼。

通過以上步驟魄幕,就可以使用Frida進(jìn)行遠(yuǎn)程調(diào)用Java代碼了。需要注意的是颖杏,F(xiàn)rida的RPC機(jī)制只能在本地和遠(yuǎn)程設(shè)備之間進(jìn)行通信纯陨,不能在不同的遠(yuǎn)程設(shè)備之間進(jìn)行通信。

三丶sekiro框架源碼拆解

Sekiro框架是一個基于Netty和SpringBoot的遠(yuǎn)程調(diào)用框架,它可以實(shí)現(xiàn)跨進(jìn)程翼抠、跨平臺的遠(yuǎn)程調(diào)用咙轩。在使用Sekiro框架時,我們需要編寫服務(wù)端和客戶端的代碼阴颖,其中服務(wù)端需要實(shí)現(xiàn)SekiroServer接口活喊,客戶端需要實(shí)現(xiàn)SekiroClient接口。

下面我們來拆解一下Sekiro框架的源碼量愧,了解它是如何實(shí)現(xiàn)遠(yuǎn)程調(diào)用的钾菊。

1.SekiroServer

SekiroServer是Sekiro框架的服務(wù)端接口,它定義了如下方法:

public interface SekiroServer {
    void start() throws Exception;
    void stop() throws Exception;
    void registerHandler(String uri, SekiroRequestHandler handler);
}

其中偎肃,start()方法用于啟動服務(wù)端煞烫,stop()方法用于停止服務(wù)端,registerHandler()方法用于注冊請求處理器累颂。

2.SekiroRequestHandler

SekiroRequestHandler是Sekiro框架的請求處理器接口滞详,它定義了如下方法:

public interface SekiroRequestHandler {
    void handle(SekiroRequest sekiroRequest, SekiroResponse sekiroResponse);
}

其中,handle()方法用于處理請求喘落,并將處理結(jié)果寫入響應(yīng)中茵宪。

3.SekiroClient

SekiroClient是Sekiro框架的客戶端接口最冰,它定義了如下方法:

public interface SekiroClient {
    void start() throws Exception;
    void stop() throws Exception;
    SekiroResponse invokeSync(String uri, SekiroRequest sekiroRequest) throws Exception;
}

其中,start()方法用于啟動客戶端,stop()方法用于停止客戶端亭敢,invokeSync()方法用于同步調(diào)用遠(yuǎn)程服務(wù)培慌。

4.SekiroRequest

SekiroRequest是Sekiro框架的請求對象,它包含了請求的URI篇裁、請求參數(shù)等信息沛慢。

5.SekiroResponse

SekiroResponse是Sekiro框架的響應(yīng)對象,它包含了響應(yīng)的狀態(tài)碼达布、響應(yīng)數(shù)據(jù)等信息团甲。

6.SekiroServerHandler

SekiroServerHandler是Sekiro框架的服務(wù)端處理器,它繼承了Netty的SimpleChannelInboundHandler類黍聂,用于處理客戶端請求躺苦。

在SekiroServerHandler中,我們可以看到如下代碼:

@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if (msg instanceof FullHttpRequest) {
        FullHttpRequest request = (FullHttpRequest) msg;
        String uri = request.uri();
        if (uri.startsWith("/api/")) {
            handleHttpRequest(ctx, request);
        } else {
            ctx.fireChannelRead(msg);
        }
    } else {
        ctx.fireChannelRead(msg);
    }
}

這段代碼用于判斷請求的URI是否以“/api/”開頭产还,如果是匹厘,則調(diào)用handleHttpRequest()方法處理請求,否則將請求傳遞給下一個處理器脐区。

handleHttpRequest()方法中愈诚,我們可以看到如下代碼:

SekiroRequest sekiroRequest = SekiroRequestParser.parse(request);
SekiroResponse sekiroResponse = new SekiroResponse();
sekiroResponse.setRequestId(sekiroRequest.getRequestId());
sekiroResponse.setVersion(sekiroRequest.getVersion());
sekiroResponse.setStatusCode(200);
sekiroResponse.setContentType("application/json;charset=UTF-8");
try {
    SekiroRequestHandler handler = handlerMap.get(sekiroRequest.getUri());
    if (handler != null) {
        handler.handle(sekiroRequest, sekiroResponse);
    } else {
        sekiroResponse.setStatusCode(404);
        sekiroResponse.setContent("{\"message\":\"No handler found for uri: " + sekiroRequest.getUri() + "\"}");
    }
} catch (Exception e) {
    sekiroResponse.setStatusCode(500);
    sekiroResponse.setContent("{\"message\":\"" + e.getMessage() + "\"}");
}

這段代碼用于解析請求,創(chuàng)建響應(yīng)對象,并根據(jù)請求的URI查找對應(yīng)的請求處理器炕柔,如果找到酌泰,則調(diào)用請求處理器處理請求,否則返回404錯誤匕累。

7.SekiroClientHandler

SekiroClientHandler是Sekiro框架的客戶端處理器宫莱,它繼承了Netty的SimpleChannelInboundHandler類,用于處理服務(wù)端響應(yīng)哩罪。

在SekiroClientHandler中授霸,我們可以看到如下代碼:

@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if (msg instanceof FullHttpResponse) {
        FullHttpResponse response = (FullHttpResponse) msg;
        String content = response.content().toString(CharsetUtil.UTF_8);
        SekiroResponse sekiroResponse = JSON.parseObject(content, SekiroResponse.class);
        if (sekiroResponse.getStatusCode() == 200) {
            promise.setSuccess(sekiroResponse);
        } else {
            promise.setFailure(new SekiroException(sekiroResponse.getStatusCode(), sekiroResponse.getContent()));
        }
    } else {
        ctx.fireChannelRead(msg);
    }
}

這段代碼用于判斷響應(yīng)的狀態(tài)碼是否為200,如果是际插,則將響應(yīng)轉(zhuǎn)換為SekiroResponse對象碘耳,并將其設(shè)置為Promise的成功結(jié)果,否則將響應(yīng)內(nèi)容作為異常信息框弛,設(shè)置為Promise的失敗結(jié)果辛辨。

通過以上代碼的分析,我們可以了解到Sekiro框架是如何實(shí)現(xiàn)遠(yuǎn)程調(diào)用的瑟枫。在服務(wù)端斗搞,它通過Netty接收客戶端請求,并根據(jù)請求的URI查找對應(yīng)的請求處理器慷妙,然后調(diào)用請求處理器處理請求僻焚,并將處理結(jié)果寫入響應(yīng)中。在客戶端膝擂,它通過Netty發(fā)送請求到服務(wù)端虑啤,并等待服務(wù)端的響應(yīng),然后將響應(yīng)轉(zhuǎn)換為SekiroResponse對象架馋,并將其設(shè)置為Promise的成功結(jié)果或失敗結(jié)果狞山。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市叉寂,隨后出現(xiàn)的幾起案子萍启,更是在濱河造成了極大的恐慌,老刑警劉巖屏鳍,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勘纯,死亡現(xiàn)場離奇詭異,居然都是意外死亡孕蝉,警方通過查閱死者的電腦和手機(jī)屡律,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來降淮,“玉大人超埋,你說我怎么就攤上這事搏讶。” “怎么了霍殴?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵媒惕,是天一觀的道長。 經(jīng)常有香客問我来庭,道長妒蔚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任月弛,我火速辦了婚禮肴盏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘帽衙。我一直安慰自己菜皂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布厉萝。 她就那樣靜靜地躺著恍飘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谴垫。 梳的紋絲不亂的頭發(fā)上章母,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音翩剪,去河邊找鬼乳怎。 笑死,一個胖子當(dāng)著我的面吹牛肢专,可吹牛的內(nèi)容都是我干的舞肆。 我是一名探鬼主播焦辅,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼博杖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了筷登?” 一聲冷哼從身側(cè)響起剃根,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎前方,沒想到半個月后狈醉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惠险,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年苗傅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片班巩。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡渣慕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逊桦,我是刑警寧澤眨猎,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站强经,受9級特大地震影響睡陪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匿情,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一兰迫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炬称,春花似錦逮矛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至府蔗,卻和暖如春晋控,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姓赤。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工赡译, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人不铆。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓蝌焚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親誓斥。 傳聞我的和親對象是個殘疾皇子只洒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容