91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android逆向Challenge Write Up(1)

發布時間:2020-07-21 22:41:39 來源:網絡 閱讀:897 作者:heeeeen 欄目:移動開發

Google Play上的一道逆向題,一共有5關難度,選擇相應的難度,輸入Name和Serial后,點擊submit后,可提示是否通關成功。如圖。

Android逆向Challenge Write Up(1)

程序總體結構分析

利用ApkIDE對com.me.keygen.activity進行逆向后,發現MainActivity.smali的validateSerial()方法用于判斷是否通關,該方法又調用KeyVerifier.isValid接口進行判斷,如果返回值為1,則通關成功,為0則通關失敗。代碼如下

invoke-interface {v6, v7, v8}, Lcom/me/keygen/verifiers/KeyVerifier;->isValid(Ljava/lang/String;Ljava/lang/String;)Z 
 #調用com.me.keygen.verifiers.KeyVerifier.isValid(String,String)接口
 #其中v6為KeyVerifier對象,v7為Name文本框中的String,v8為Serial文本框中的String

而KeyVerifier.isValid()最終會調用ChallengeXVerifier.isValid(),其中X為1-5,代表了用戶選擇的難度,這個難度是在MainActivity.smali中根據用戶的選擇,初始化的currentChallenge參數,進而調用getVerifierForChallenge()方法,構造相應的ChallengeXVerifier類。注意Challenge5除了currentChallenge參數,還會多傳一些參數。

因此針對每一關,關鍵的判斷邏輯都在ChallengeXVerifier.isValid()中。

為了熟悉smali,我們盡量還原原始算法,而不采用暴破和smali注入的方法。


level1:Beginner

直接根據Challenge1Verifier.smali編寫注冊機

public class challenge1 {
    public static void main(String[] args) {
        
        String name = args[0];
        int answer = 0; // v0
        
        for(int v3 = 0; v3 < name.length(); v3 = v3+1) {
            char v1 = name.charAt(v3);
            int v4 = v1 * v1;
            answer = answer + v4;
            answer = answer ^ v1;
        }
                
        System.out.println("The answer is "+answer);
    }
}

level2:Easy

分析Challenge2Verifier.smali

# virtual methods
.method public isValid(Ljava/lang/String;Ljava/lang/String;)Z
    .locals 8
    .param p1, "name"    # Ljava/lang/String;
    .param p2, "serial"    # Ljava/lang/String;

    .prologue
    const/4 v5, 0x0

    .line 16
    invoke-virtual {p1}, Ljava/lang/String;->length()I

    move-result v6 # v6: name.length()

    const/4 v7, 0x4 # v7=4

    if-ge v6, v7, :cond_1 #如果v6>=4,則到cond1.否則返回v5,此時值為0,注冊未成功!

    .line 42
    :cond_0
    :goto_0
    return v5

    .line 21
    :cond_1
    invoke-virtual {p1}, Ljava/lang/String;->toUpperCase()Ljava/lang/String; #name的字符轉成大寫

    move-result-object p1 

    .line 22
    const-wide/16 v1, 0x0 #long v1(nameSum)初始為0

    .line 23
    .local v1, "nameSum":J
    const/4 v4, 0x0 #v4初始為0

    .local v4, "x":I
    :goto_1
    invoke-virtual {p1}, Ljava/lang/String;->length()I

    move-result v6 #v6=name.length()
    

    if-ge v4, v6, :cond_2 # 如果v4大于等于v6,循環結束

    .line 25
    invoke-virtual {p1, v4}, Ljava/lang/String;->charAt(I)C 

    move-result v6 # v6=name.charAt(v4)

    int-to-long v6, v6

    add-long/2addr v1, v6 #v1=v1+v6

    .line 26
    const-wide/16 v6, 0x3 #將0x3擴展為64位, v6=0x3

    mul-long/2addr v1, v6 # v1 = v1*v6

    .line 27
    const-wide/16 v6, 0x40 #將0x40擴展為64位, v6=0x40

    sub-long/2addr v1, v6 #v1=v1-v6

    .line 23
    add-int/lit8 v4, v4, 0x1 #v4=v4+1

    goto :goto_1

    .line 30
    :cond_2   #循環結束
    invoke-static {v1, v2}, Ljava/lang/Long;->toString(J)Ljava/lang/String; #v1轉為string, 即為serial

    move-result-object v3 # v3=sumString

    .line 31
    .local v3, "sumString":Ljava/lang/String;
    const/4 v0, 0x0

    .line 32
    .local v0, "finalSum":I
    const/4 v4, 0x0

    :goto_2  # 第二個循環體
    invoke-virtual {v3}, Ljava/lang/String;->length()I 

    move-result v6 # v6 = sumString.length()

    if-ge v4, v6, :cond_3 #if v4 >= v6到cond3

    .line 34
    invoke-virtual {v3, v4}, Ljava/lang/String;->charAt(I)C #

    move-result v6 #v6=v3.charAt(v4)

    add-int/lit8 v6, v6, -0x30 #v6=v6-0x30 

    add-int/2addr v0, v6 #v0=v0+v6

    .line 32
    add-int/lit8 v4, v4, 0x1 #v4++

    goto :goto_2

    .line 37
    :cond_3 #第二個循環體結束
    const/4 v4, 0x0

    :goto_3 # 第三個循環體開始
    invoke-virtual {p2}, Ljava/lang/String;->length()I

    move-result v6 # v6 = serial.length()

    if-ge v4, v6, :cond_4 #判斷v4是否小于serial的長度,是才循環,否則到cond4。 

    .line 39
    invoke-virtual {p2, v4}, Ljava/lang/String;->charAt(I)C

    move-result v6 # v6 = serial.charAt(v4)

    add-int/lit8 v6, v6, -0x40 # v6 = v6 - 0x40

    sub-int/2addr v0, v6 # v0 = v0+v6

    .line 37
    add-int/lit8 v4, v4, 0x1

    goto :goto_3 

    .line 42
    :cond_4
    if-nez v0, :cond_0 #第三個循環體結束,如果v0不為0,則注冊未成功!這個循環似乎是對serial進行一些特殊的判斷,注冊成功必須確保v0為0;

    const/4 v5, 0x1

    goto :goto_0
.end method

上面的代碼首先判斷name的長度是否大于等于4,如果否則直接返回0,注冊不成功。如果是,則將name轉化為大寫后,開始三次循環。

前兩次循環只對name進行操作,最后得到一個finalSum的int變量。算法如下,

public class challenge2 {
    public static void main(String[] args) {
        
        String name = args[0];
        long serial = 0; 
        int v5 = 0;
        int v6 = name.length();
        long v1 = 0;
        
        if ( v6 >= 4) {
            name = name.toUpperCase();
        
            for(int v4 = 0; v4 < name.length(); v4 = v4+1) {
                v6 = name.charAt(v4);
                v1 = v1 + (long)v6;
                v1 = v1*0x3;
                v1 = v1 - (long)0x40;
            }
            String sumString = Long.toString(v1);
            
            int finalSum = 0; //v0 
            for (int v4 = 0; v4 < sumString.length(); v4 = v4+1) {
                v6 = sumString.charAt(v4);
                v6 = v6 - 0x30;
                finalSum = finalSum + v6;
            }
            System.out.println("The answer is "+finalSum);
        }
        else
            System.exit(0);
    }
}

根據上面的算法,我們運行得到finalSum為23。

e:\heen\practise\com.me.keygen.activity>java challenge2 heen
The answer is 23

而第三次循環是將finalSum與serial進行某種運算,最終判斷是否注冊成功。算法如下

        for (v4 = 0; v4 < serial.length(); v4 = v4+1) {
            v6 = serial.charAt(v4);
            v6 = v6 - 0x40;
            finalSum = finalSum - v6;
        }
        
        if (finalSum == 0)
            System.out.println("The answer is "+serial);

只要使finalSum為0,serial就正確,因此只要滿足finalSum-(serial.charAt(i)-0x40)=0的Serial都成立,可以有多個解。在finalSum為23時,讓finalSum減去23個1,就為0。對應23個1,那么serial可為23個0x41(A).也可為21個0x41(A)和1個0x42(B)



Android逆向Challenge Write Up(1)

level3:Hard

分析Challenge3Verifier.smali。

 .line 25
    const-string v9, "-"

    invoke-virtual {p2, v9}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String; #對serial進行分割,根據其中的'-'

    move-result-object v5 # 結果parts=v5為String[]

    .line 26
    .local v5, "parts":[Ljava/lang/String;
    array-length v9, v5 #數組長度為v9

    const/16 v10, 0x8

    if-eq v9, v10, :cond_1 #如果v9為0x8,跳轉到cond_1

    .line 92
    :cond_0
    :goto_0
    return v8 # 返回0,注冊不成功

    .line 31
    :cond_1
    const/4 v7, 0x0

    .local v7, "x":I
    :goto_1    # 循環開始
    array-length v9, v5
 
    if-ge v7, v9, :cond_2

    .line 33
    aget-object v9, v5, v7 #將v9 = v5[v7]

    const-string v10, "[0-9A-F][0-9A-F][0-9A-F][0-9A-F]"

    invoke-virtual {v9, v10}, Ljava/lang/String;->matches(Ljava/lang/String;)Z

    move-result v9 #判斷v9是否匹配v10代表的正則表達式,推斷serial應該為XXXX-XXXX-XXXX-...的形式,X為0-9或大寫字母

    if-eqz v9, :cond_0 #如果不滿足,注冊不成功

    .line 31
    add-int/lit8 v7, v7, 0x1 # v7=v7+1

    goto :goto_1 # 循環體結束

上述代碼對serial進行判斷和處理,serial的形式應為XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX的形式,其中X為[0-9A-F]中的字符。處理后,每一個XXXX存在名為parts,長度為8的String數組中。


接下來的代碼,都在對名為baos的ByteArrayOutputStream進行操作,得到一個String foo和String lastHalf。其中foo是根據parts的前4個元素作為輸入對baos進行操作得到的,而lastHalf是parts的后4個元素,最后判斷foo的奇數位字符是否與lastHalf的每一位字符相同,相同則注冊成功。整個過程與name無關。


注冊算法如下

import java.nio.charset.Charset;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;

public class challenge3 {
            
    
    public static String bytesToHex(byte[] bytes) {
        char[] hexArray = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46};
        char[] hexChars = new char[(bytes.length)*2];
        int v;
        
        for(int j = 0; j < bytes.length;j=j+1){
             v = bytes[j] & 0xff;
             hexChars[2*j] = hexArray[v>>>0x4];
             hexChars[2*j+1] = hexArray[v&0xf];
            
        }
        return new String(hexChars);
    }
    
    public static void main(String[] args)
    {
        byte[] secretBytes;
        String secretKey;
        
        String v0 = new String("KeygenChallengeNumber3");
        secretBytes = v0.getBytes(Charset.forName("US-ASCII"));
        
        String[] parts = {"AAAA","AAAA","AAAA","AAAA"}; //array of length 8, every element is XXXX
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(0x31);
        int v7;
        
        for(v7 = 0;v7 < secretBytes.length; v7=v7+2)
        {
            baos.write(secretBytes[v7]);
            baos.write(v7+1);
        }
                
        for(v7 = 1;v7 < secretBytes.length; v7=v7+2)
        {
            baos.write(secretBytes[v7]);
            baos.write(v7+1);
        }
        baos.write(0x30);
        baos.write(0x30);
                       
        for(v7 = 0;v7 < 4; v7 = v7+1)
        {
            try {//suppose the first 4 parts is "AAAA-AAAA-AAAA-AAAA"
                byte[] bs = parts[v7].getBytes(Charset.forName("US-ASCII"));
                baos.write(bs);
                baos.write(0x2d);
            }
            catch(IOException ioe) {
            }
            
        }
        
        try {
            baos.write(secretBytes);
        }
        catch(IOException ioe){
        }
        
        System.out.println("baos is: "+baos.toString());
        byte[] result = new byte[0x20];
        
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(baos.toByteArray());
            result = md.digest();
        }
        catch(NoSuchAlgorithmException nsae){
        }
              
        String foo = bytesToHex(result).toUpperCase();
        System.out.println(foo+" length:" +foo.length());
        /*
        for(v7 = 0; v7 < foo.length(); v7=v7+2)
        {
            if (foo.charAt(v7) !=  lastHalf.charAt(v7/2))
                System.out.println("Register failed!");
        }
        */
    }

}

上述代碼中,我們假定注冊碼的前半部分為AAAA-AAAA-AAAA-AAAA,運算得到foo,選取foo的奇數位字符進行拼接,得到最后的注冊碼為AAAA-AAAA-AAAA-AAAA-446E-D772-6CD4-052A

e:\heen\practise\com.me.keygen.activity>java challenge3
4C476AE8DF72742463C9D242065324AB length:32


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

彩票| 金华市| 商都县| 朝阳县| 涞源县| 开原市| 长治县| 罗甸县| 福清市| 新宾| 郎溪县| 玉田县| 岳普湖县| 紫金县| 印江| 兴化市| 鹤峰县| 宜川县| 文水县| 铜陵市| 平舆县| 仁化县| 大邑县| 黄石市| 邹平县| 兰西县| 奎屯市| 鄂托克前旗| 奉贤区| 克什克腾旗| 台南市| 遵义市| 惠东县| 通辽市| 鸡泽县| 保康县| 汤原县| 沅陵县| 读书| 乐平市| 松潘县|