CTF-RE: 安卓逆向 + 加密算法分析爆破 [第一届国城杯 round] 赛后学习

step 1

运行程序,发现是一个登录框,找到主函数

java 复制代码
package com.example.demo;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private EditText Password;  // 密码输入框
    private EditText User;      // 用户名输入框
    private Button button;      // 按钮

    // 验证输入的字符串是否只包含字母和下划线
    private boolean isValidInput(String s) {
        return s.matches("[a-zA-Z_]*");  // 正则表达式:只允许字母和下划线
    }

    @Override
    protected void onCreate(Bundle bundle0) {
        super.onCreate(bundle0);
        this.setContentView(R.layout.activity_main);  // 设置活动布局,使用activity_main.xml作为布局文件

        // 初始化UI组件
        this.button = (Button)this.findViewById(R.id.button);  // 按钮
        this.User = (EditText)this.findViewById(R.id.username);  // 用户名输入框
        this.Password = (EditText)this.findViewById(R.id.password);  // 密码输入框

        // 设置按钮的点击事件监听器
        this.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view0) {
                MakePath makePath0 = new MakePath();  // 创建MakePath对象,用于路径处理
                Round round0 = new Round();  // 创建Round对象,用于密码验证
                
                // 获取输入的用户名和密码
                String s = MainActivity.this.User.getText().toString();  // 获取用户名
                String s1 = MainActivity.this.Password.getText().toString();  // 获取密码
                
                // 检查用户名和密码是否合法
                if (MainActivity.this.isValidInput(s) && MainActivity.this.isValidInput(s1)) {
                    // 如果用户名符合条件,继续处理密码验证
                    if (makePath0.encodeToBase64(s).equals("c9m1bRmfY5Wk")) {  // 检查用户名是否匹配已知的Base64编码值
                        // 如果用户名匹配,再检查密码是否正确
                        if (round0.round(makePath0.encode(MainActivity.this, s), s1)) {
                            // 密码正确,显示成功消息
                            Toast.makeText(MainActivity.this, "That's right! You have found it, the flag is D0g3xGC{" + s + s1 + "}", Toast.LENGTH_LONG).show();
                            return;
                        }
                        // 密码错误,显示失败消息
                        Toast.makeText(MainActivity.this, "Wrong! Your password is incorrect", Toast.LENGTH_LONG).show();
                        return;
                    }
                    // 用户名错误,显示失败消息
                    Toast.makeText(MainActivity.this, "Wrong! Your username is incorrect", Toast.LENGTH_LONG).show();
                    return;
                }
                // 如果输入的用户名或密码无效,显示提示信息
                Toast.makeText(MainActivity.this, "Invalid input! Only lowercase letters and uppercase letters and '_' are allowed.", Toast.LENGTH_LONG).show();
            }
        });
    }
}

看一眼encodeToBase64()

java 复制代码
package com.example.demo;

import android.content.Context;

public class MakePath {
    // 定义一个字符数组,用于Base64编码的字符集
    private static final char[] BASE64_CHARS;

    static {
        // 初始化BASE64字符集,包括大写字母、小写字母、数字和 "+"、"/"
        MakePath.BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    }

    // Makebox 方法,接受一个字符串并生成一个整数数组
    // 该方法的功能是通过某种方式对传入字符串的每个字符进行操作
    public int[] Makebox(String s) {
        // 创建一个长度为0x400(1024)的整数数组
        int[] arr_v = new int[0x400];

        // 将整数0到0x3FF填充到数组的反向位置
        for (int v1 = 0; v1 < 0x400; ++v1) {
            arr_v[0x3FF - v1] = v1;
        }

        // 遍历数组并与字符串中的字符进行异或运算,生成新的数组
        // 这里通过对字符串每个字符的值与当前位置进行异或,得到一个加密后的结果
        for (int v = 0; v < 0x400; ++v) {
            arr_v[v] ^= s.charAt(v % s.length());
        }

        // 返回处理后的整数数组
        return arr_v;
    }

    // encode 方法,接受一个 Context 对象和一个字符串,并返回经过加密处理后的整数数组
    public int[] encode(Context context0, String s) {
        // 先将字符串进行 Base64 编码,再传递给 Makebox 方法生成加密数组
        return this.Makebox(this.encodeToBase64(s));
    }

    // encodeToBase64 方法,将输入字符串编码为 Base64 格式的字符串
    public String encodeToBase64(String s) {
        StringBuilder stringBuilder0 = new StringBuilder();
        // 将字符串转换为字节数组
        byte[] arr_b = s.getBytes();

        // 计算需要补充的字节数,使得字节数组长度是3的倍数
        int v = (3 - arr_b.length % 3) % 3;
        // 计算 Base64 编码后所需的总字节长度
        int v1 = arr_b.length + v;

        // 遍历字节数组,每三字节为一组,进行编码
        for (int v2 = 0; v2 < v1; v2 += 3) {
            // 组合三个字节为一个24位的整数
            int v3 = 0;
            for (int v4 = 0; v4 < 3; ++v4) {
                v3 <<= 8;
                int v5 = v2 + v4;
                if (v5 < arr_b.length) {
                    v3 |= arr_b[v5] & 0xFF; // 将字节拼接成整数
                }
            }

            // 将24位整数分割成4个6位的部分,每部分对应一个 Base64 字符
            for (int v6 = 0; true; ++v6) {
                int v7 = 2;
                if (v6 >= 4) { // 每次循环最多生成4个Base64字符
                    break;
                }
                if (v6 != 1) {
                    v7 = v6 == 2 ? 1 : v6;
                }

                // 根据当前位的值生成Base64字符
                // 当处理的数据比原始数据少时,用 '=' 作为填充字符
                if (v2 * 8 / 6 + v6 < arr_b.length * 8 / 6 + v) {
                    stringBuilder0.append(MakePath.BASE64_CHARS[v3 >> (3 - v7) * 6 & 0x3F]);
                } else {
                    stringBuilder0.append('='); // 填充字符
                }
            }
        }

        // 返回最终的 Base64 编码字符串
        return stringBuilder0.toString();
    }
}

这里很奇怪

java 复制代码
                if (v6 != 1) {
                    v7 = v6 == 2 ? 1 : v6;
                }

如果 v6 != 1 且 v6 = 2 , v7 = 6 ,否则 v7 = 1

代码可以拉到本地直接跑

java 复制代码
class MakePath {  
    private static char[] BASE64_CHARS;  
  
    static {  
        MakePath.BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();  
    }  
  
    public static String encodeToBase64(String s) {  
        StringBuilder stringBuilder0 = new StringBuilder();  
        byte[] arr_b = s.getBytes();  
        int v = (3 - arr_b.length % 3) % 3;  
        int v1 = arr_b.length + v;  
        for(int v2 = 0; v2 < v1; v2 += 3) {  
            int v3 = 0;  
            for(int v4 = 0; v4 < 3; ++v4) {  
                v3 <<= 8;  
                int v5 = v2 + v4;  
                if(v5 < arr_b.length) {  
                    v3 |= arr_b[v5] & 0xFF;  
                }  
            }  
  
            for(int v6 = 0; true; ++v6) {  
                int v7 = 2;  
                if(v6 >= 4) {  
                    break;  
                }  
  
                if(v6 != 1) {  
                    v7 = v6 == 2 ? 1 : v6;  
                }  
  
                if(v2 * 8 / 6 + v6 < arr_b.length * 8 / 6 + v) {  
                    stringBuilder0.append(MakePath.BASE64_CHARS[v3 >> (3 - v7) * 6 & 0x3F]);  
                }  
                else {  
                    stringBuilder0.append('=');  
                }  
            }  
        }  
  
        return stringBuilder0.toString();  
    }  
}  
  
public class Main {  
    public static void main(String[] args) {  
        String input = "Hello World";  
        String encoded = MakePath.encodeToBase64(input);  
        System.out.println("Encoded: " + encoded);  
        }  
}

Hello World常规编码后应该是SGVsbG8gV29ybGQ=

跟程序输出的对比一下

SGVsbG8gV29ybGQ=
SVGsb8GgV92ybQG=

再编一个别的tes 应该是 dGVz

程序出来是

dVGz

不难看出来调换了每四字节的 1,2 位

c9m1bRmfY5Wk

改一下c9m1bRmfY5Wk

cm91bmRfYW5k

解密得到

round_and

step 2

继续来到

java 复制代码
round0.round(makePath0.encode(MainActivity.this, s), s1)

这里把你输入的user生成一个s盒,作为参数和password一起传到round,我们先dump替换盒

java 复制代码
import java.util.Arrays;  
  
class MakePath {  
    private static char[] BASE64_CHARS;  
  
    static {  
        MakePath.BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();  
    }  
  
    public int[] Makebox(String s) {  
        int[] arr_v = new int[0x400];  
        for(int v1 = 0; v1 < 0x400; ++v1) {  
            arr_v[0x3FF - v1] = v1;  
        }  
  
        for(int v = 0; v < 0x400; ++v) {  
            arr_v[v] ^= s.charAt(v % s.length());  
        }  
  
        return arr_v;  
    }  
  
    public int[] encode(String s) {  
        return this.Makebox(this.encodeToBase64(s));  
    }  
  
    public String encodeToBase64(String s) {  
        StringBuilder stringBuilder0 = new StringBuilder();  
        byte[] arr_b = s.getBytes();  
        int v = (3 - arr_b.length % 3) % 3;  
        int v1 = arr_b.length + v;  
        for(int v2 = 0; v2 < v1; v2 += 3) {  
            int v3 = 0;  
            for(int v4 = 0; v4 < 3; ++v4) {  
                v3 <<= 8;  
                int v5 = v2 + v4;  
                if(v5 < arr_b.length) {  
                    v3 |= arr_b[v5] & 0xFF;  
                }  
            }  
  
            for(int v6 = 0; true; ++v6) {  
                int v7 = 2;  
                if(v6 >= 4) {  
                    break;  
                }  
  
                if(v6 != 1) {  
                    v7 = v6 == 2 ? 1 : v6;  
                }  
  
                if(v2 * 8 / 6 + v6 < arr_b.length * 8 / 6 + v) {  
                    stringBuilder0.append(MakePath.BASE64_CHARS[v3 >> (3 - v7) * 6 & 0x3F]);  
                }  
                else {  
                    stringBuilder0.append('=');  
                }  
            }  
        }  
  
        return stringBuilder0.toString();  
    }  
}  
  
public class Main {  
    public static void main(String[] args) {  
        int[] rarry;  
        MakePath MakePath = new MakePath();  
        rarry = MakePath.encode("round_and");  
        System.out.println(Arrays.toString(rarry));  
    }  
}

PS:我个人认为本地测代码比frida方便一点,可以本地单步,所以没用frida.

[924, 967, 912, 973, 921, 936, 916, 926, 942, 963, 930, 927, 912, 971, 924, 961, 909, 956, 896, 906, 946, 991, 958, 899, 900, 991, 904, 981, 897, 944, 908, 902, 902, 1003, 906, 951, 952, 995, 948, 1001, 949, 900, 952, 946, 906, 999, 902, 955, 940, 1015, 928, 1021, 937, 920, 932, 942, 926, 1011, 914, 943, 928, 1019, 940, 1009, 989, 1004, 976, 986, 994, 911, 1006, 979, 980, 911, 984, 901, 977, 992, 988, 982, 1014, 923, 1018, 967, 968, 915, 964, 921, 965, 1012, 968, 962, 1018, 919, 1014, 971, 1020, 935, 1008, 941, 1017, 968, 1012, 1022, 974, 931, 962, 1023, 1008, 939, 1020, 929, 1005, 988, 992, 1002, 978, 959, 990, 995, 996, 959, 1000, 949, 993, 976, 1004, 998, 806, 843, 810, 791, 792, 835, 788, 841, 789, 804, 792, 786, 810, 839, 806, 795, 780, 855, 768, 861, 777, 824, 772, 782, 830, 851, 818, 783, 768, 859, 780, 849, 829, 780, 816, 826, 770, 879, 782, 819, 820, 879, 824, 869, 817, 768, 828, 822, 790, 891, 794, 807, 808, 883, 804, 889, 805, 788, 808, 802, 794, 887, 790, 811, 860, 775, 848, 781, 857, 872, 852, 862, 878, 771, 866, 863, 848, 779, 860, 769, 845, 892, 832, 842, 882, 799, 894, 835, 836, 799, 840, 789, 833, 880, 844, 838, 838, 811, 842, 887, 888, 803, 884, 809, 885, 836, 888, 882, 842, 807, 838, 891, 876, 823, 864, 829, 873, 856, 868, 878, 862, 819, 850, 879, 864, 827, 876, 817, 669, 684, 656, 666, 674, 719, 686, 659, 660, 719, 664, 709, 657, 672, 668, 662, 694, 731, 698, 647, 648, 723, 644, 729, 645, 692, 648, 642, 698, 727, 694, 651, 700, 743, 688, 749, 697, 648, 692, 702, 654, 739, 642, 703, 688, 747, 700, 737, 685, 668, 672, 682, 658, 767, 670, 675, 676, 767, 680, 757, 673, 656, 684, 678, 742, 651, 746, 727, 728, 643, 724, 649, 725, 740, 728, 722, 746, 647, 742, 731, 716, 663, 704, 669, 713, 760, 708, 718, 766, 659, 754, 719, 704, 667, 716, 657, 765, 716, 752, 762, 706, 687, 718, 755, 756, 687, 760, 677, 753, 704, 764, 758, 726, 699, 730, 743, 744, 691, 740, 697, 741, 724, 744, 738, 730, 695, 726, 747, 540, 583, 528, 589, 537, 552, 532, 542, 558, 579, 546, 543, 528, 587, 540, 577, 525, 572, 512, 522, 562, 607, 574, 515, 516, 607, 520, 597, 513, 560, 524, 518, 518, 619, 522, 567, 568, 611, 564, 617, 565, 516, 568, 562, 522, 615, 518, 571, 556, 631, 544, 637, 553, 536, 548, 558, 542, 627, 530, 559, 544, 635, 556, 625, 605, 620, 592, 602, 610, 527, 622, 595, 596, 527, 600, 517, 593, 608, 604, 598, 630, 539, 634, 583, 584, 531, 580, 537, 581, 628, 584, 578, 634, 535, 630, 587, 636, 551, 624, 557, 633, 584, 628, 638, 590, 547, 578, 639, 624, 555, 636, 545, 621, 604, 608, 618, 594, 575, 606, 611, 612, 575, 616, 565, 609, 592, 620, 614, 422, 459, 426, 407, 408, 451, 404, 457, 405, 420, 408, 402, 426, 455, 422, 411, 396, 471, 384, 477, 393, 440, 388, 398, 446, 467, 434, 399, 384, 475, 396, 465, 445, 396, 432, 442, 386, 495, 398, 435, 436, 495, 440, 485, 433, 384, 444, 438, 406, 507, 410, 423, 424, 499, 420, 505, 421, 404, 424, 418, 410, 503, 406, 427, 476, 391, 464, 397, 473, 488, 468, 478, 494, 387, 482, 479, 464, 395, 476, 385, 461, 508, 448, 458, 498, 415, 510, 451, 452, 415, 456, 405, 449, 496, 460, 454, 454, 427, 458, 503, 504, 419, 500, 425, 501, 452, 504, 498, 458, 423, 454, 507, 492, 439, 480, 445, 489, 472, 484, 494, 478, 435, 466, 495, 480, 443, 492, 433, 285, 300, 272, 282, 290, 335, 302, 275, 276, 335, 280, 325, 273, 288, 284, 278, 310, 347, 314, 263, 264, 339, 260, 345, 261, 308, 264, 258, 314, 343, 310, 267, 316, 359, 304, 365, 313, 264, 308, 318, 270, 355, 258, 319, 304, 363, 316, 353, 301, 284, 288, 298, 274, 383, 286, 291, 292, 383, 296, 373, 289, 272, 300, 294, 358, 267, 362, 343, 344, 259, 340, 265, 341, 356, 344, 338, 362, 263, 358, 347, 332, 279, 320, 285, 329, 376, 324, 334, 382, 275, 370, 335, 320, 283, 332, 273, 381, 332, 368, 378, 322, 303, 334, 371, 372, 303, 376, 293, 369, 320, 380, 374, 342, 315, 346, 359, 360, 307, 356, 313, 357, 340, 360, 354, 346, 311, 342, 363, 156, 199, 144, 205, 153, 168, 148, 158, 174, 195, 162, 159, 144, 203, 156, 193, 141, 188, 128, 138, 178, 223, 190, 131, 132, 223, 136, 213, 129, 176, 140, 134, 134, 235, 138, 183, 184, 227, 180, 233, 181, 132, 184, 178, 138, 231, 134, 187, 172, 247, 160, 253, 169, 152, 164, 174, 158, 243, 146, 175, 160, 251, 172, 241, 221, 236, 208, 218, 226, 143, 238, 211, 212, 143, 216, 133, 209, 224, 220, 214, 246, 155, 250, 199, 200, 147, 196, 153, 197, 244, 200, 194, 250, 151, 246, 203, 252, 167, 240, 173, 249, 200, 244, 254, 206, 163, 194, 255, 240, 171, 252, 161, 237, 220, 224, 234, 210, 191, 222, 227, 228, 191, 232, 181, 225, 208, 236, 230, 38, 75, 42, 23, 24, 67, 20, 73, 21, 36, 24, 18, 42, 71, 38, 27, 12, 87, 0, 93, 9, 56, 4, 14, 62, 83, 50, 15, 0, 91, 12, 81, 61, 12, 48, 58, 2, 111, 14, 51, 52, 111, 56, 101, 49, 0, 60, 54, 22, 123, 26, 39, 40, 115, 36, 121, 37, 20, 40, 34, 26, 119, 22, 43, 92, 7, 80, 13, 89, 104, 84, 94, 110, 3, 98, 95, 80, 11, 92, 1, 77, 124, 64, 74, 114, 31, 126, 67, 68, 31, 72, 21, 65, 112, 76, 70, 70, 43, 74, 119, 120, 35, 116, 41, 117, 68, 120, 114, 74, 39, 70, 123, 108, 55, 96, 61, 105, 88, 100, 110, 94, 51, 82, 111, 96, 59, 108, 49]

step 5 逆向算法分析

核心代码再这部分

java 复制代码
int v1 = 33; // 初始化索引值为 33  
// 遍历输入字符串的每个字符  
for(int v2 = 0; v2 < s.length(); ++v2) {  
    int v3 = s.charAt(v2); // 获取当前字符的 ASCII 值  
  
    // 对当前字符执行 32 次操作(可能是加密的一个步骤)  
    for(int v4 = 0; v4 < 0x20; ++v4) {  
        // 根据当前状态数组和字符值决定执行哪种操作  
        switch(((arr_v[v1] ^ v3) % 5 + 5) % 5) {  
            case 0: {  
                // 执行加法操作  
                round$Result0 = this.add(arr_v, v3, v1);  
                break;  
            }  
            case 1: {  
                // 执行减法操作  
                round$Result0 = this.sub(arr_v, v3, v1);  
                break;  
            }  
            case 2: {  
                // 执行异或操作  
                round$Result0 = this.xor(arr_v, v3, v1);  
                break;  
            }  
            case 3: {  
                // 执行左移操作  
                round$Result0 = this.shl(v3, v1);  
                break;  
            }  
            case 4: {  
                // 执行右移操作  
                round$Result0 = this.shr(v3, v1);  
                break;  
            }  
            default: {  
                // 默认情况下,不做任何操作  
                round$Result0 = new Result(v3, v1);  
            }  
        }  
  
        // 更新字符值和索引值为操作后的结果  
        v3 = round$Result0.getNum();  
        v1 = round$Result0.getRip();  
    }  
  
    // 将最终处理后的字符值存储到 arr_v1 数组中  
    arr_v1[v2] = v3;  
}

我们可以侦测每次返回值爆破(爆破好多次都进死路让ai写了个回溯),或者限制字符然后手动pass也行

java 复制代码
import java.util.ArrayList;  
import java.util.List;  
  
class Round {  
    public static class Result {  
        private int num;  
        private int rip;  
  
        public Result(int v, int v1) {  
            this.num = v;  
            this.rip = v1;  
        }  
  
        public int getNum() {  
            return this.num;  
        }  
  
        public int getRip() {  
            return this.rip;  
        }  
    }  
  
    public Result add(int[] arr_v, int v, int v1) {  
        int v2 = ((v + arr_v[v1]) % 0x400 + 0x400) % 0x400;  
        return new Result(v2, (v1 + v2) % 0x400);  
    }  
  
    public int[] round(int[] box, String s) {  
        Result round$Result0;  
        int input_len = s.length();  
        int[] out = new int[input_len];  
        int v1 = 33;  
        for(int i = 0; i < s.length(); ++i) {  
            int v3 = s.charAt(i);  
            for(int v4 = 0; v4 < 0x20; ++v4) {  
                switch(((box[v1] ^ v3) % 5 + 5) % 5) {  
                    case 0: {  
                        round$Result0 = this.add(box, v3, v1);  
                        break;  
                    }  
                    case 1: {  
                        round$Result0 = this.sub(box, v3, v1);  
                        break;  
                    }  
                    case 2: {  
                        round$Result0 = this.xor(box, v3, v1);  
                        break;  
                    }  
                    case 3: {  
                        round$Result0 = this.shl(v3, v1);  
                        break;  
                    }  
                    case 4: {  
                        round$Result0 = this.shr(v3, v1);  
                        break;  
                    }  
                    default: {  
                        round$Result0 = new Result(v3, v1);  
                    }  
                }  
  
                v3 = round$Result0.getNum();  
                v1 = round$Result0.getRip();  
            }  
  
            out[i] = v3;  
        }  
        return out;  
    }  
  
    public Result shl(int v, int v1) {  
        int v2 = (v >> 3) % 0x400;  
        return new Result(v2, (v1 + v2) % 0x400);  
    }  
  
    public Result shr(int v, int v1) {  
        int v2 = (v << 3) % 0x400;  
        return new Result(v2, (v1 + v2) % 0x400);  
    }  
  
    public Result sub(int[] arr_v, int v, int v1) {  
        int v2 = ((v - arr_v[v1]) % 0x400 + 0x400) % 0x400;  
        return new Result(v2, (v1 + v2) % 0x400);  
    }  
  
    public Result xor(int[] arr_v, int v, int v1) {  
        int v2 = (arr_v[v1] ^ v) % 0x400;  
        return new Result(v2, (v1 + v2) % 0x400);  
    }  
}  
  
public class Main {  
    public static void main(String[] args) {  
        // Initialize the Round class  
        Round round = new Round();  
  
        // Target values to match  
        int[] end = {0x160, 646, 0x2F0, 882, 65, 0, 0x7A, 0, 0, 7, 350, 360};  
  
        // Assuming 'box' is not required or is used within Round.round appropriately  
        int[] box = {924, 967, 912, 973, 921, 936, 916, 926, 942, 963, 930, 927, 912, 971, 924, 961, 909, 956, 896, 906, 946, 991, 958, 899, 900, 991, 904, 981, 897, 944, 908, 902, 902, 1003, 906, 951, 952, 995, 948, 1001, 949, 900, 952, 946, 906, 999, 902, 955, 940, 1015, 928, 1021, 937, 920, 932, 942, 926, 1011, 914, 943, 928, 1019, 940, 1009, 989, 1004, 976, 986, 994, 911, 1006, 979, 980, 911, 984, 901, 977, 992, 988, 982, 1014, 923, 1018, 967, 968, 915, 964, 921, 965, 1012, 968, 962, 1018, 919, 1014, 971, 1020, 935, 1008, 941, 1017, 968, 1012, 1022, 974, 931, 962, 1023, 1008, 939, 1020, 929, 1005, 988, 992, 1002, 978, 959, 990, 995, 996, 959, 1000, 949, 993, 976, 1004, 998, 806, 843, 810, 791, 792, 835, 788, 841, 789, 804, 792, 786, 810, 839, 806, 795, 780, 855, 768, 861, 777, 824, 772, 782, 830, 851, 818, 783, 768, 859, 780, 849, 829, 780, 816, 826, 770, 879, 782, 819, 820, 879, 824, 869, 817, 768, 828, 822, 790, 891, 794, 807, 808, 883, 804, 889, 805, 788, 808, 802, 794, 887, 790, 811, 860, 775, 848, 781, 857, 872, 852, 862, 878, 771, 866, 863, 848, 779, 860, 769, 845, 892, 832, 842, 882, 799, 894, 835, 836, 799, 840, 789, 833, 880, 844, 838, 838, 811, 842, 887, 888, 803, 884, 809, 885, 836, 888, 882, 842, 807, 838, 891, 876, 823, 864, 829, 873, 856, 868, 878, 862, 819, 850, 879, 864, 827, 876, 817, 669, 684, 656, 666, 674, 719, 686, 659, 660, 719, 664, 709, 657, 672, 668, 662, 694, 731, 698, 647, 648, 723, 644, 729, 645, 692, 648, 642, 698, 727, 694, 651, 700, 743, 688, 749, 697, 648, 692, 702, 654, 739, 642, 703, 688, 747, 700, 737, 685, 668, 672, 682, 658, 767, 670, 675, 676, 767, 680, 757, 673, 656, 684, 678, 742, 651, 746, 727, 728, 643, 724, 649, 725, 740, 728, 722, 746, 647, 742, 731, 716, 663, 704, 669, 713, 760, 708, 718, 766, 659, 754, 719, 704, 667, 716, 657, 765, 716, 752, 762, 706, 687, 718, 755, 756, 687, 760, 677, 753, 704, 764, 758, 726, 699, 730, 743, 744, 691, 740, 697, 741, 724, 744, 738, 730, 695, 726, 747, 540, 583, 528, 589, 537, 552, 532, 542, 558, 579, 546, 543, 528, 587, 540, 577, 525, 572, 512, 522, 562, 607, 574, 515, 516, 607, 520, 597, 513, 560, 524, 518, 518, 619, 522, 567, 568, 611, 564, 617, 565, 516, 568, 562, 522, 615, 518, 571, 556, 631, 544, 637, 553, 536, 548, 558, 542, 627, 530, 559, 544, 635, 556, 625, 605, 620, 592, 602, 610, 527, 622, 595, 596, 527, 600, 517, 593, 608, 604, 598, 630, 539, 634, 583, 584, 531, 580, 537, 581, 628, 584, 578, 634, 535, 630, 587, 636, 551, 624, 557, 633, 584, 628, 638, 590, 547, 578, 639, 624, 555, 636, 545, 621, 604, 608, 618, 594, 575, 606, 611, 612, 575, 616, 565, 609, 592, 620, 614, 422, 459, 426, 407, 408, 451, 404, 457, 405, 420, 408, 402, 426, 455, 422, 411, 396, 471, 384, 477, 393, 440, 388, 398, 446, 467, 434, 399, 384, 475, 396, 465, 445, 396, 432, 442, 386, 495, 398, 435, 436, 495, 440, 485, 433, 384, 444, 438, 406, 507, 410, 423, 424, 499, 420, 505, 421, 404, 424, 418, 410, 503, 406, 427, 476, 391, 464, 397, 473, 488, 468, 478, 494, 387, 482, 479, 464, 395, 476, 385, 461, 508, 448, 458, 498, 415, 510, 451, 452, 415, 456, 405, 449, 496, 460, 454, 454, 427, 458, 503, 504, 419, 500, 425, 501, 452, 504, 498, 458, 423, 454, 507, 492, 439, 480, 445, 489, 472, 484, 494, 478, 435, 466, 495, 480, 443, 492, 433, 285, 300, 272, 282, 290, 335, 302, 275, 276, 335, 280, 325, 273, 288, 284, 278, 310, 347, 314, 263, 264, 339, 260, 345, 261, 308, 264, 258, 314, 343, 310, 267, 316, 359, 304, 365, 313, 264, 308, 318, 270, 355, 258, 319, 304, 363, 316, 353, 301, 284, 288, 298, 274, 383, 286, 291, 292, 383, 296, 373, 289, 272, 300, 294, 358, 267, 362, 343, 344, 259, 340, 265, 341, 356, 344, 338, 362, 263, 358, 347, 332, 279, 320, 285, 329, 376, 324, 334, 382, 275, 370, 335, 320, 283, 332, 273, 381, 332, 368, 378, 322, 303, 334, 371, 372, 303, 376, 293, 369, 320, 380, 374, 342, 315, 346, 359, 360, 307, 356, 313, 357, 340, 360, 354, 346, 311, 342, 363, 156, 199, 144, 205, 153, 168, 148, 158, 174, 195, 162, 159, 144, 203, 156, 193, 141, 188, 128, 138, 178, 223, 190, 131, 132, 223, 136, 213, 129, 176, 140, 134, 134, 235, 138, 183, 184, 227, 180, 233, 181, 132, 184, 178, 138, 231, 134, 187, 172, 247, 160, 253, 169, 152, 164, 174, 158, 243, 146, 175, 160, 251, 172, 241, 221, 236, 208, 218, 226, 143, 238, 211, 212, 143, 216, 133, 209, 224, 220, 214, 246, 155, 250, 199, 200, 147, 196, 153, 197, 244, 200, 194, 250, 151, 246, 203, 252, 167, 240, 173, 249, 200, 244, 254, 206, 163, 194, 255, 240, 171, 252, 161, 237, 220, 224, 234, 210, 191, 222, 227, 228, 191, 232, 181, 225, 208, 236, 230, 38, 75, 42, 23, 24, 67, 20, 73, 21, 36, 24, 18, 42, 71, 38, 27, 12, 87, 0, 93, 9, 56, 4, 14, 62, 83, 50, 15, 0, 91, 12, 81, 61, 12, 48, 58, 2, 111, 14, 51, 52, 111, 56, 101, 49, 0, 60, 54, 22, 123, 26, 39, 40, 115, 36, 121, 37, 20, 40, 34, 26, 119, 22, 43, 92, 7, 80, 13, 89, 104, 84, 94, 110, 3, 98, 95, 80, 11, 92, 1, 77, 124, 64, 74, 114, 31, 126, 67, 68, 31, 72, 21, 65, 112, 76, 70, 70, 43, 74, 119, 120, 35, 116, 41, 117, 68, 120, 114, 74, 39, 70, 123, 108, 55, 96, 61, 105, 88, 100, 110, 94, 51, 82, 111, 96, 59, 108, 49};  
  
        // StringBuilder to build the current string  
        StringBuilder baseStr = new StringBuilder();  
  
        // List to store all valid results  
        List<String> validResults = new ArrayList<>();  
  
        // Start the backtracking process  
        System.out.println("Starting backtracking search...");  
        backtrack(round, box, baseStr, 0, end, validResults);  
  
        // Output all valid results  
        if (validResults.isEmpty()) {  
            System.out.println("No valid character combinations found.");  
        } else {  
            System.out.println("Valid character combinations found:");  
            for (String result : validResults) {  
                System.out.println(result);  
            }  
        }  
    }  
  
    /**  
     * Backtrack function to generate and validate character combinations.     *     * @param round        Instance of the Round class  
     * @param box          An array required by Round.round method  
     * @param baseStr      StringBuilder to accumulate characters  
     * @param index        Current index (depth) in the backtracking tree  
     * @param end          Target array to match  
     * @param validResults List to store valid combinations  
     */    public static void backtrack(Round round, int[] box, StringBuilder baseStr, int index, int[] end, List<String> validResults) {  
        if (index == 12) {  
            // Base case: Completed a 12-character string  
            String candidate = baseStr.toString();  
            System.out.println("Attempting string: " + candidate);  
            int[] ret = round.round(box, candidate);  
            if (isValidResult(ret, end)) {  
                System.out.println("Valid combination found: " + candidate);  
                validResults.add(candidate);  
            } else {  
                System.out.println("Invalid combination: " + candidate);  
            }  
            return;  
        }  
  
        // Iterate through all printable ASCII characters  
        for (int f = 32; f <= 126; f++) {  
            char tmp = (char) f;    // Current character to attempt  
            baseStr.append(tmp);    // Add character to the current string  
  
            System.out.println("Selected character '" + tmp + "', current string: " + baseStr.toString());  
  
            // Compute 'ret' for the current partial string  
            int[] ret = round.round(box, baseStr.toString());  
  
            // Check if the current 'ret' matches 'end' up to the current index  
            if (isValidPartial(ret, end, index + 1)) {  
                // If valid, proceed to the next character  
                backtrack(round, box, baseStr, index + 1, end, validResults);  
            } else {  
                // If not valid, prune this path  
                System.out.println("Pruning path: " + baseStr.toString());  
            }  
  
            // Backtrack: Remove the last character and continue  
            baseStr.deleteCharAt(baseStr.length() - 1);  
            System.out.println("Backtracking, removed character '" + tmp + "', current string: " + baseStr.toString());  
        }  
    }  
  
    /**  
     * Checks if the computed 'ret' array matches the 'end' array exactly.     *     * @param ret Computed array from Round.round  
     * @param end Target array to match  
     * @return true if all elements match; false otherwise  
     */    public static boolean isValidResult(int[] ret, int[] end) {  
        if (ret.length < end.length) {  
            return false;  
        }  
        for (int i = 0; i < end.length; i++) {  
            if (ret[i] != end[i]) {  
                return false;  // Mismatch found  
            }  
        }  
        return true;  // All elements match  
    }  
  
    /**  
     * Checks if the computed 'ret' array matches the 'end' array up to a certain index.     *     * @param ret    Computed array from Round.round  
     * @param end    Target array to match  
     * @param upto   Number of elements to check (from start)  
     * @return true if the first 'upto' elements match; false otherwise  
     */    public static boolean isValidPartial(int[] ret, int[] end, int upto) {  
        if (ret.length < upto) {  
            return false;  
        }  
        for (int i = 0; i < upto; i++) {  
            if (ret[i] != end[i]) {  
                return false;  // Mismatch found in partial match  
            }  
        }  
        return true;  // Partial match is valid  
    }  
}

拼起来就是flag

java 复制代码
Toast.makeText(MainActivity.this, "That\'s right! You have found it, the flag is D0g3xGC{" + s + s1 + "}", 1).show();
D0g3xGC{round_and_rounD_we_go}
相关推荐
燕雀安知鸿鹄之志哉.4 小时前
攻防世界 easyphp
安全·web安全·网络安全
老鑫安全培训5 小时前
从安全角度看 SEH 和 VEH
java·网络·安全·网络安全·系统安全·安全威胁分析
网络安全工程师老王7 小时前
vulnhub靶机billu_b0x精讲
网络安全·信息安全·渗透测试
cr.sheeper8 小时前
empire靶机
网络安全·vulnhub
anddddoooo9 小时前
Kerberoasting 离线爆破攻击
网络·数据库·安全·microsoft·网络安全
为几何欢9 小时前
【hackmyvm】deba靶机wp
网络安全·渗透·hackmyvm·hmv
为几何欢10 小时前
【hackmyvm】hacked靶机wp
网络安全·渗透·hackmyvm·hmv
HackKong18 小时前
Python与黑客技术
网络·python·web安全·网络安全·php
Autumn.h18 小时前
vulnhub Empire-Lupin-One靶机
网络安全