美文网首页CTF Re&&Pwn
看雪CTF.TSRC 2018 团队赛 第五题 『交响曲』 解题

看雪CTF.TSRC 2018 团队赛 第五题 『交响曲』 解题

作者: 看雪学院 | 来源:发表于2018-12-11 18:01 被阅读5次

    第五题《交响曲》的攻击时间在今天(12月11日)中午12:00结束,意味着KCTF此次攻击赛势已经走过了三分之一的进程。

    那么,是谁获得了第一个重要节点的第一名呢?

    Ta就是tekkens团队!

    3353s夺得本题第一名!

    成功攻击第五题《交响曲》的团队此次也创下赛事小高峰,共计31支团队:


    最新赛况战况一览

    比赛到1/3进程后的新排名是什么样的呢?我们一起来看一下。

    我们可以发现,第一名到第四名的团队和上一场总排名保持一致,而第五名到第六名则全部变动。那么,前四名能在后面的比赛继续守擂成功吗?又会有新的黑马团队杀出来搅乱整个局势吗?我们拭目以待!

    第五题 点评

    crownless:

    交响曲这道题是一道安卓逆向分析题,采用了称骨算命的算法设计。题目要求参赛者通过“命运值”反推出“生辰八字”,因此需要在理解算法的基础上通过编写脚本来破解“生辰八字”,饶有趣味。

    第五题 出题团队简介

    出题团队: 妳的名稱過長  

    第五题 设计思路

    由看雪论坛KwaiChing 原创

    0x00. 注册码: 1995020305to07


    0x01. 思路:

    程序根据称骨算命的算法设计, 以3.4两的命运反推出生日期(农历), 并排除一九九五年二月初三之外相同出生日期。

    0x02. 称骨算命:

    出生 年 月 日 时 八字对应不同骨重:

    int[] yearWeightArray = {7,7,9,12,8,7,13,5,14,5,9,17,5,7,12,8,8,6,19,6,8,16,10,6,12,9,6,7,12,5,9,8,7,8,15,9,16,8,8,19,12,6,8,7,5,15,6,16,15,7,9,12,10,7,15,6,5,14,14,9};

    int[] monthWeightArray = {6,7,18,9,5,16,9,15,18,8,9,5};

    int[] dayWeightArray = {5,10,8,15,16,15,8,16,8,16,9,17,8,17,10,8,9,18,5,15,10,9,8,9,15,18,7,8,16,6};

    int[] hourWeightArray = {16,6,7,10,9,16,10,8,8,9,6,6};

    骨重相加之和对应不同命运(见附件)

    0x03. 注册码就是出生年+月+日+时

    年份限制在1983-2007之间

    getTempDate():

    if((this.tempY <=1983) || (this.tempY >=2007)) {

    this.tempY =0;

    }

    其中各时刻:

    子(23:00~01:00)

    丑(01:00~03:00)

    寅(03:00~05:00)

    卯(05:00~07:00)

    辰(07:00~09:00)

    巳(09:00~11:00)

    午(11:00~13:00)

    未(13:00~15:00)

    申(15:00~17:00)

    酉(17:00~19:00)

    戌(19:00~21:00)

    亥(21:00~23:00)

    以字符串替代:

    String[] hour = {

    "23to01",

    "01to03",

    "03to05",

    "05to07",

    "07to09",

    "09to11",

    "11to13",

    "13to15",

    "15to17",

    "17to19",

    "19to21",

    "21to23"}


    0x04.getLunar函数排除符合结果中除特定年份和月份之外的注册码

    if((this.tempY ==1989) || (this.tempY ==2004)) {

    this.tempD =31;// 排除1989 和2004年

    }

    // 排除特定月份

    if((this.tempM ==1) || (this.tempM ==4) || (this.tempM ==5) || (this.tempM ==7) || (this.tempM ==10) || (this.tempM ==11) || (this.tempM ==12)) {

    this.tempY =1999;// 排除特定月份 注:1999年没有3.4两的骨重

    }

    if((this.tempY <=1994) && ((this.tempM ==2) ||(this.tempM ==6) || (this.tempM ==8))) {

    this.tempM =3;// 除1989和2004外 其他年份3月没有3.4两骨重

    }

    if((this.tempY >=1996) && ((this.tempM ==2) ||(this.tempM ==6) || (this.tempM ==8))){

    this.tempM =9;// 除1989和2004外 其他年份9月没有3.4两骨重

    }

    if((this.tempY ==1995) && ((this.tempD > (this.tempM +2)) ||this.tempM ==this.tempD )){

    this.tempM =6;// 1995年9月没有3.4两骨重

    }

    0x05. getHourWeight()函数中亦排除二月份卯时出生的八字:

    if((j ==2) && tempH.equals(hour[6])){

    return63;// 6.3两的时骨重和其他骨重(0.5*3=1.5, )相加超出骨重值7.2钱

    }


    0x06. 相关称骨计算详情见附件FateMe源码

    原文链接:

    https://bbs.pediy.com/thread-228479.htm(含附件)


    第五题 交响曲 解题思路

    本题解析依然由看雪论坛顾言庭 原创。

    环境配置

    系统 : Windows 7、nexus 5

    程序 : CrackMe.KwaiChing.apk

    要求 : 输入口令

    使用工具 : 称骨算命、Android Studio、jadx、apktool

    搜集线索

    在测试机中安装该apk,发现程序需求一个key,如果以123这样的随机数作为输入的话,会输出failed!!!。

    我们使用apktool反编译这个程序:

    apktooldCrackMe.KwaiChing.apk

    然后在\res\values\strings.xml位置查看字符串信息:

    "

    s \u0009\u0009u \u0009\u0009\u0009c \u0009\u0009\u0009\u0009c \u0009\u0009\u0009\u0009\u0009e \u0009\u0009\u0009\u0009\u0009\u0009s \u0009\u0009\u0009\u0009\u0009\u0009\u0009s \u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009! \u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009!

    "

    f \u0009a \u0009\u0009i \u0009\u0009\u0009l \u0009\u0009\u0009\u0009e \u0009\u0009\u0009\u0009\u0009d \u0009\u0009\u0009\u0009\u0009\u0009! \u0009\u0009\u0009\u0009\u0009\u0009\u0009! \u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009!

    驗證

    Search

    999+

    "

    財穀有餘主得內助富貴之命

    此命福氣果如何,僧道門中衣祿多;

    離祖出家方為妙,朝晚拜佛念彌陀。

    此命推來為人性躁;

    與人做事反為不美;

    離祖成家;

    三番四次自成自立安享福;

    直自三十六至四十六;

    財不謀而自至;

    福不求而自得;

    有貴人助;

    家庭安寧;

    妻宮若要無刑;

    猴、豬、羊、蛇不可配;

    龍、虎、馬、牛方得安;

    雖有二子;

    終生帶暗方可.

    兄弟六親如冰碳;

    在家不得安然;

    初限駁雜多端;

    勞碌奔波不能聚錢;

    常有憂愁.

    壽元七十八歲;

    死於三月中."

    看来存在有一个提示正确和一个提示错误的字符串,还有就是success34的迷之字符串。我搜索了一下,居然发现了存有相关信息的网页。

    看来是一个玄学题目(笑。

    开始分析

    我们运行jadx:

    D:\software\jadx-0.8.0(1)\lib>java-jarjadx-gui-0.8.0.jar

    定位到p007cn.kwaiching.crackme包下的CrackMe类,发现这就是算法的主流程:

    protectedvoidonCreate(Bundle bundle){

    super.onCreate(bundle);

    setContentView((int) R.layout.activity_fate_me);

    b();

    this.n = (TextView) findViewById(R.id.fate);

    ((Button) findViewById(R.id.ok)).setOnClickListener(newOnClickListener() {

    publicvoidonClick(View view){

    try{

    CrackMe.this.a();

    }catch(Exception unused) {

    CrackMe.this.n.setText(CrackMe.this.getString(R.string.notMe));

    }

    }

    });

    }

    来看看a的流程:

    privatevoida(){

    try{

    c();

    if(this.j ==0||this.i ==0||this.h ==0) {

    this.n.setText(getString(R.string.notMe));

    return;

    }

    d();

    a(((e() + f()) + g()) + h());

    }catch(Exception unused) {

    this.n.setText(getString(R.string.notMe));

    }

    }

    我们先来查看h函数的部分代码:

    /* JADX WARNING: Removed duplicated region for block: B:17:0x0050 A:{Catch:{ Exception -> 0x005c }} */

    privateinth(){

    /*

    r6 = this;

    r0 = 2131165227; // 0x7f07002b float:1.7944665E38 double:1.0529355243E-314;

    r1 = 2131427370; // 0x7f0b002a float:1.8476354E38 double:1.05306504E-314;

    r2 = 0;

    r0 = r6.findViewById(r0);     Catch:{ Exception -> 0x005c }

    这样的代码就是jadx的反编译出了点问题,我们需要设置一下选项,让代码的可读性变高。

    代码修饰

    这里,在jadx的菜单中点击文件->设置->启用反混淆,接着选择

    显示不一致的代码。这时,可以发现代码漂亮了不少。

    导入as查看

    在jadx中查看,不如导入到android stdio(下文中简称为as)这样专业的ide中查看,这样能修改变量名,快速查看引用位置等等。

    这里,在jadx的菜单中点击文件->另存为 gradle项目,选中一个任意一个空的文件夹,然后保存就可以。

    流程分析

    在as中,发现主流程是:

    private void m162a() {

    try {

    m166c();

    if (this.year == 0 || this.month == 0 || this.day == 0) {

    this.f125n.setText(getString(C0252R.string.notMe));

    return;

    }

    m167d();

    m163a(((m168e() + m169f()) + m170g()) + m171h());

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    }

    }

    m166c函数的流程是:

    /* renamed from: c */

    private void m166c() {

    try {

    String obj = ((EditText) findViewById(C0252R.id.code)).getText().toString();

    this.year = 0;

    this.month = 0;

    this.day = 0;

    this.year = Integer.parseInt(obj.length() > 4 ? obj.substring(0, 4) : obj);

    if (this.year > 0 && this.year < 189) {

    this.year = 0;

    }

    if (this.year <= 1983 || this.year >= 2007) {

    this.year = 0;

    }

    this.month = Integer.parseInt(obj.length() > 6 ? obj.substring(4, 6) : obj);

    if (this.month < 1 || this.month > 12) {

    this.month = 0;

    }

    if (obj.length() > 8) {

    obj = obj.substring(6, 8);

    }

    this.day = Integer.parseInt(obj);

    if (this.day < 1 || this.day > 31) {

    this.day = 0;

    }

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    }

    }

    稍微分析一下,可知程序把输入限定在:

    年份:1983后,2007前。

    月份:1-12

    日期:1-31

    再来看看其他的函数:

    private void m167d() {

    try {

    if (this.year == 1989 || this.year == 2004) {

    this.day = 31;

    }

    if (this.month == 1 || this.month == 4 || this.month == 5 || this.month == 7 || this.month == 10 || this.month == 11 || this.month == 12) {

    this.year = 1999;

    }

    if (this.year <= 1994 && (this.month == 2 || this.month == 6 || this.month == 8)) {

    this.month = 3;

    }

    if (this.year >= 1996 && (this.month == 2 || this.month == 6 || this.month == 8)) {

    this.month = 9;

    }

    if (this.year == 1995 && (this.day > this.month + 2 || this.month == this.day)) {

    this.month = 6;

    }

    this.my_year = this.year;

    this.my_month = this.month;

    this.my_day = this.day;

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    }

    }

    这里其实是将可能的输入范围减少了,比如第一个判断,2004和1989年的所有可选日期都是31号。以此类推。

    四个小函数

    private int m168e() {

    try {

    return this.f115d[(this.my_year - 1900) % 60];

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    return 0;

    }

    }

    /* renamed from: f */

    private int m169f() {

    try {

    return this.f114c[this.my_month - 1];

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    return 0;

    }

    }

    /* renamed from: g */

    private int m170g() {

    try {

    return this.f113b[this.my_day - 1];

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    return 0;

    }

    }

    /* JADX WARNING: Removed duplicated region for block: B:17:0x0050 A:{Catch:{ Exception -> 0x005c }} */

    /* Code decompiled incorrectly, please refer to instructions dump. */

    /* renamed from: h */

    private int m171h() {

    try {

    Object obj;

    String other_str = ((EditText) findViewById(C0252R.id.code)).getText().toString();

    other_str = other_str.substring(8, other_str.length());

    int MONTH = this.my_month;

    int i2 = 0;

    while (i2 < this.f124m.length) {

    if (!other_str.equals(this.f124m[i2])) {

    i2++;

    } else if (MONTH == 2 && other_str.equals(this.f124m[6])) {

    return 63;

    } else {

    this.f122k = this.f112a[i2];

    obj = 1;

    if (obj == null) {

    this.f125n.setText(getString(C0252R.string.notMe));

    }

    return this.f122k;

    }

    }

    obj = null;

    if (obj == null) {

    }

    return this.f122k;

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    return 0;

    }

    }

    }

    最本质的操作都是从固定数组中取值,然后返回相关值。以下是相关数组:

    /* renamed from: a */

    int[] f112a = new int[]{16, 6, 7, 10, 9, 16, 10, 8, 8, 9, 6, 6};

    /* renamed from: b */

    int[] f113b = new int[]{5, 10, 8, 15, 16, 15, 8, 16, 8, 16, 9, 17, 8, 17, 10, 8, 9, 18, 5, 15, 10, 9, 8, 9, 15, 18, 7, 8, 16, 6};

    /* renamed from: c */

    int[] f114c = new int[]{6, 7, 18, 9, 5, 16, 9, 15, 18, 8, 9, 5};

    /* renamed from: d */

    int[] f115d = new int[]{7, 7, 9, 12, 8, 7, 13, 5, 14, 5, 9, 17, 5, 7, 12, 8, 8, 6, 19, 6, 8, 16, 10, 6, 12, 9, 6, 7, 12, 5, 9, 8, 7, 8, 15, 9, 16, 8, 8, 19, 12, 6, 8, 7, 5, 15, 6, 16, 15, 7, 9, 12, 10, 7, 15, 6, 5, 14, 14, 9};

    String[] f124m = new String[]{"23to01", "01to03", "03to0


    判断函数

    这个判断函数,emmm...

    private void m163a(int i) {

    if (i > 34 || i < 34) {

    this.f125n.setText(getString(C0252R.string.notMe));

    return;

    }

    try {

    this.f125n.setText(String.format("%s%s", new Object[]{getString(C0252R.string.me), this.f123l[i]}));

    ((Button) findViewById(C0252R.id.ok)).setEnabled(false);

    } catch (Exception unused) {

    this.f125n.setText(getString(C0252R.string.notMe));

    }

    }

    可知需求i值为34了。

    逻辑推理

    我们知道,作者需求我们输入生辰八字,如果生辰八字按某种特定方式取值相加的结果为34,那么夺旗成功。

    计算一下遍历所有可能的计算次数:

    2007-1983=24

    24 * 12 * 31 * 12 = 107136

    十万次的运算对于计算机来说不算什么,如果发现可能的结果太多,再添加m167d函数中的判断缩小取值范围,还有m171h函数中的这一句:

    MONTH == 2 && other_str.equals(this.f124m[6])

    其实表示,月份为2时,时辰索引值不能取6,否则失败。

    编写代码

    根据以上逻辑,我们做python代码如下:

    hour_weight_list = [16, 6, 7, 10, 9, 16, 10, 8, 8, 9, 6, 6]

    day_weight_list = [5, 10, 8, 15, 16, 15, 8, 16, 8, 16, 9, 17, 8, 17, 10, 8, 9, 18, 5, 15, 10, 9, 8, 9, 15, 18, 7, 8, 16, 6]

    month_weight_list = [6, 7, 18, 9, 5, 16, 9, 15, 18, 8, 9, 5]

    # begin at 1960

    year_weight_list = [7, 7, 9, 12, 8, 7, 13, 5, 14, 5, 9, 17, 5, 7, 12, 8, 8, 6, 19, 6, 8, 16, 10, 6, 12, 9, 6, 7, 12, 5, 9, 8, 7, 8, 15, 9, 16, 8, 8, 19, 12, 6, 8, 7, 5, 15, 6, 16, 15, 7, 9, 12, 10, 7, 15, 6, 5, 14, 14, 9]

    date_list = []

    for year_index in range(1984,2007):

    for month_index in range(1,len(month_weight_list)+1):

    for day_index in range(1,len(day_weight_list)+1):

    for hour_index in range(0,len(hour_weight_list)):

    year_weight = year_weight_list[(year_index - 1900) % 60]

    month_weight = month_weight_list[month_index-1]

    day_weight = day_weight_list[day_index-1]

    hour_weight = hour_weight_list[hour_index]

    year = year_index

    month = month_index

    if year == 1989 or year == 2004:

    continue

    if month == 1 or month == 4 or month == 5 or month == 7 or month == 10 or month == 11 or month == 12:

    year = 1999

    year_weight = year_weight_list[(year - 1900) % 60]

    if year <= 1994 and (month == 2 or month == 6 or month == 8):

    month = 3

    month_weight = month_weight_list[month-1]

    if year >= 1996 and (month == 2 or month == 6 or month == 8):

    month = 9

    month_weight = month_weight_list[month-1]

    if year == 1995 and (day_index > month + 2 or month == day_index):

    month = 6

    month_weight = month_weight_list[month-1]

    if ( year_weight + month_weight + day_weight + hour_weight ) == 34:

    date = str(year_index) + '/' + str(month_index) + '/' + str(day_index) + '/' + str(hour_index)

    if date_list.count(date) == 0:

    date_list.append(date)

    print "find_times = " + str(len(date_list))

    print date_list[0]

    print date_list[1]

    夺旗成功

    最终运算结果如下:

    ➜  play_ground python count_date.py

    find_times = 2

    1995/2/3/3

    1995/2/3/6

    根据月份为2,索引值不能为6的推论,我们得到了唯一的flag1995020305to07,输入flag,程序提示成功,并弹出一段迷之算命结果。

    95年的小伙,和我同龄,还不错的。不过,果然还是少看些算命,好好学习,天天向上比较好哦。

    参考

    1、Android 反编译神器jadx的使用

    文章链接如下:

    https://blog.csdn.net/Fisher_3/article/details/78654450

    WP原文链接:

    https://bbs.pediy.com/thread-248308.htm


    第六题【追凶者也】正在火热进行中

    第6题/共15题

    《追凶者也》将于12月13日中午12:00结束

    赶紧参与进来吧~!


    合作伙伴

    腾讯安全应急响应中心 

    TSRC,腾讯安全的先头兵,肩负腾讯公司安全漏洞、黑客入侵的发现和处理工作。这是个没有硝烟的战场,我们与两万多名安全专家并肩而行,捍卫全球亿万用户的信息、财产安全。一直以来,我们怀揣感恩之心,努力构建开放的TSRC交流平台,回馈安全社区。未来,我们将继续携手安全行业精英,探索互联网安全新方向,建设互联网生态安全,共铸“互联网+”新时代。

    相关文章

      网友评论

        本文标题:看雪CTF.TSRC 2018 团队赛 第五题 『交响曲』 解题

        本文链接:https://www.haomeiwen.com/subject/jxeehqtx.html