看雪CTF.TSRC 2018 團隊賽 第五題 『交響曲』 解題思路
第五題《交響曲》的攻擊時間在今天(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
合作伙伴
騰訊安全應急響應中心
TSRC,騰訊安全的先頭兵,肩負騰訊公司安全漏洞、駭客入侵的發現和處理工作。這是個沒有硝煙的戰場,我們與兩萬多名安全專家並肩而行,捍衛全球億萬使用者的資訊、財產安全。一直以來,我們懷揣感恩之心,努力構建開放的TSRC交流平臺,回饋安全社群。未來,我們將繼續攜手安全行業精英,探索網際網路安全新方向,建設網際網路生態安全,共鑄“網際網路+”新時代。
轉載請註明:轉自看雪學院
看雪CTF.TSRC 2018 團隊賽 解題思路彙總:
相關文章
- 看雪CTF.TSRC 2018 團隊賽 第七題 『魔法森林』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第九題『諜戰』 解題思路2018-12-19
- 看雪CTF.TSRC 2018 團隊賽 第一題 『初世紀』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第二題 『半加器』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第六題 『追凶者也』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第八題 『二向箔』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第十題『俠義雙雄』 解題思路2018-12-21
- 看雪CTF.TSRC 2018 團隊賽 第三題 『七十二疑冢』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第四題 『盜夢空間』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第十一題『伊甸園』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第十五題『 密碼風雲』 解題思路2019-01-02密碼
- 看雪CTF.TSRC 2018 團隊賽 第十四題『 你眼中的世界』 解題思路2018-12-29
- 看雪CTF.TSRC 2018 團隊賽 第十二題『移動迷宮』 解題思路2018-12-25
- 看雪CTF.TSRC 2018 團隊賽 第十三題『 機器人歷險記』 解題思路2018-12-27機器人
- 看雪CTF.TSRC 2018 團隊賽 獲獎名單公示2019-01-02
- 看雪·深信服 2021 KCTF 春季賽 | 第五題設計思路及解析2021-05-17
- 看雪·眾安 2021 KCTF 秋季賽 | 第五題設計思路及解析2021-11-29
- 看雪.騰訊TSRC 2017 CTF 秋季賽 第五題點評及解析思路2017-11-03
- 看雪.WiFi萬能鑰匙 CTF 2017第五題 點評及解題思路2017-06-28WiFi
- 看雪.紐盾 KCTF 2019 Q2 | 第五題點評及解題思路2019-07-01
- 2020 KCTF秋季賽 | 第五題設計及解題思路2020-11-30
- 看雪.萬能鑰匙 CTF 2017第一題 WannaLOL 解題思路2017-06-29
- 看雪·深信服 2021 KCTF 春季賽 | 第七題設計思路及解析2021-05-25
- 看雪·深信服 2021 KCTF 春季賽 | 第八題設計思路及解析2021-05-25
- 看雪·深信服 2021 KCTF 春季賽 | 第九題設計思路及解析2021-05-28
- 看雪·深信服 2021 KCTF 春季賽 | 第四題設計思路及解析2021-05-17
- 看雪·深信服 2021 KCTF 春季賽 | 第六題設計思路及解析2021-05-21
- 看雪·深信服 2021 KCTF 春季賽 | 第二題設計思路及解析2021-05-12
- 看雪·深信服 2021 KCTF 春季賽 | 第三題設計思路及解析2021-05-14
- 看雪·深信服 2021 KCTF 春季賽 | 第十題設計思路及解析2021-05-31
- 看雪·眾安 2021 KCTF 秋季賽 | 第十題設計思路及解析2021-12-16
- 看雪·眾安 2021 KCTF 秋季賽 | 第九題設計思路及解析2021-12-09
- 看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析2021-12-03
- 看雪·眾安 2021 KCTF 秋季賽 | 第三題設計思路及解析2021-11-22
- 看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析2021-12-01
- 看雪·眾安 2021 KCTF 秋季賽 | 第四題設計思路及解析2021-11-25
- 看雪.騰訊TSRC 2017 CTF 秋季賽 第二題點評及解析思路2017-10-28
- 看雪.騰訊TSRC 2017 CTF 秋季賽 第三題點評及解析思路2017-10-30