• TSCTF2022(先挖个坑,太忙了呜呜呜)


    happy_mota

    先玩游戏,玩两局发现魔王很难打,通过npc对话发现flag被分成四段,并且玩到第11层的时候对话中有提示:11-19层的墙壁有点奇怪,发现第一串flag是TSCTF{enj

    有了玩游戏的基础,发现flag都隐藏在NPC对话中,用pyinstxtractor.py反编译main.exe得到main.py,但是main函数中的字符串经过decode后发现都是操作相关,并不涉及于NPC的对话,于是先扒文件,在\happy_mota\main\scripts目录下找到人物对话源代码,发现三段可疑代码:

    s = b''
    f2 = self.parameter['2wsxdr5']
    for i in range(len(f2)):
    	s += bytes([f2[i] ^ i ^ 0xC8])
    self.conversation_control.print_word("商人L3m0nade", "爽快!我这儿捡了个字符串:\"" + s.decode() + '\"你看有没有用.',"npc_2")
    
    s = b''
    f3 = self.parameter['3edcft6']
    for i in range(len(f3)):
        s += bytes([f3[i] ^ i ^ 0xB4])
    self.parameter['answer3'] = s.decode()
    
    s = b''
    f4 = self.parameter['4rfvgy7']
    for i in range(len(f4)):
        s += bytes([(f4[i] ^ (len(f4) - i) ^ 0xA9)])
    self.parameter['answer4'] = s.decode()
    

    一开始以为f2,f3,f4就是2wsxdr5,3edcft6,4rfvgy7,搞了很久,最后才发现应该只是类似于C++STL库中的map映射,翻了翻人物对话发现找不到这个映射,于是回到main函数:

    '2wsxdr5': b'\xf8\xb0\x95\xfc\x84\x88',
    '3edcft6': b'\xeb\xe7\x85\xe1\xd5\xc3\x87\xd6\x85\xdc\xd3\xda\x9e',
    '4rfvgy7': b'\xee\x97\xd4\xcc\xe7\x91\xf7\xd4\x92\xdc\xe3\xc5\xcb\xcf\x8a\xd5',
    'answer2': '',
    'answer3': '',
    'answer4': '',
    

    然后写exp得到后三段flag:

    #include<iostream>
    using namespace std;
    char s2[6];
    char s3[13];
    char s4[16];
    int f2[6] = {0xf8,0xb0,0x95,0xfc,0x84,0x88};
    int f3[13] = {0xeb,0xe7,0x85,0xe1,0xd5,0xc3,0x87,0xd6,0x85,0xdc,0xd3,0xda,0x9e};
    int f4[16] = {0xee,0x97,0xd4,0xcc,0xe7,0x91,0xf7,0xd4,0x92,0xdc,0xe3,0xc5,0xcb,0xcf,0x8a,0xd5};
    int main()
    {
    	for(int i=0;i<6;i++)s2[i] = f2[i] ^ i ^ 0xC8,cout<<s2[i];
    	for(int i=0;i<13;i++)s3[i] = f3[i] ^ i ^ 0xB4,cout<<s3[i];
    	for(int i=0;i<16;i++)s4[i] = f4[i] ^ (16 - i) ^ 0xA9,cout<<s4[i];
    }
    

    最后合并一下得到完整flag:TSCTF{enj0y_7HE_R3Ver5e9ame&W1shB3Tt3rLife!}

    Patternlock

    安卓逆向,check函数是native方法,想到逆向SO文件,然后尝试分析SO文件,通过交叉引用找到

    jint JNI_OnLoad(JavaVM *vm, void *reserved)
    {
      int v2; // r0
      int i; // [sp+24h] [bp-4Ch]
      int j; // [sp+24h] [bp-4Ch]
      int v8; // [sp+34h] [bp-3Ch] BYREF
      char v9[8]; // [sp+38h] [bp-38h] BYREF
      int v10[3]; // [sp+40h] [bp-30h] BYREF
      char v11[34]; // [sp+4Eh] [bp-22h] BYREF
    
      ptrace(PTRACE_TRACEME, 0, 0, 0);
      if ( sub_1AF0(vm, &v8, 65542) )
        return -1;
      strcpy(v9, "cig`o");
      strcpy(v11, "(Mhbrd)kigm$_y|f~v):N");
      for ( i = 0; i <= 4; ++i )
        v9[i] ^= i;
      for ( j = 0; j <= 20; ++j )
        v11[j] ^= j;
      v10[0] = (int)v9;
      v10[1] = (int)v11;
      v10[2] = (int)sub_179C;
      if ( !sub_1B24(v8, "com/crackme/tsctf/TsUtil", v10, 1) )
        return -1;
      v2 = sub_159C();
      sub_1670(v2);
      return 65542;
    }
    

    发现可以和java逆出来的check函数对的上,但是比赛的时候由于这里读不懂直接寄了

    后来发现可以变成

    当场去世

    #include<bits/stdc++.h>
    using namespace std;
    
    char v9[] = "cig`o";
    char v11[] = "(Mhbrd)kigm$_y|f~v):N";
    char a[] = "\r<6\x12)G^VfIDjDX";
    char TSCTF[] = "TSCTF2022!!!!!";
    char key[50];
    char inputbytes[50];
    int cmp[40] = {97, 14, 20, 35, 10, 68, 11, 86, 55, 91, 4, 42, 4, 76, 107, 89, 68, 32, 95, 77, 15, 6, 55, 9, 86, 47, 87, 26, 109, 86, 68, 116, 11, 19, 11, 5, 54, 12, 87, 122};
    
    int main()
    {
    	int v12[3];
    	
    	for (int i = 0; i <= 4; ++i)
    		v9[i] ^= i;
        	
    	for (int j = 0; j <= 20; ++j)
    		v11[j] ^= j;
    	cout<<v9<<endl<<v11<<endl;
    	
    	for(int i=0;i<=13;i++) key[i] = TSCTF[i] ^ a[i];
    	cout<<key<<endl; 
    		
    	for(int i=0;i<40;i++) inputbytes[i] = key[i%strlen(key)] ^ cmp[i];
    	cout<<inputbytes<<endl;
    }
    

    happy_string

    虚拟机连内网后从远端接收一个base64传输的文件,接收后,先拖入IDA分析,查字符串发现关机键字符串没有交叉引用

    进入main函数发现有一个数据块需要恢复,猜测D3F开始为关键函数,一般启用动态调试即可自动恢复。

    但是前面有ptrace反调试,需要先patch掉,尝试修改 == -1为 != -1(把jnz改成jz),这里需要注意:未启用动态调试的时候patch的结果不会带入启用动调后的程序,也就是说需要在该语句前下断点,在动调的同时patch汇编语句。

    然后一路F8到D3F处,F7步入函数,会出现一个恢复code的弹窗

    按快捷键P即可创建函数得到关键代码。

    __int64 sub_55EDD8A00D3F()
    {
      __int64 v1; // [rsp+20h] [rbp-130h] BYREF
      __int64 v2; // [rsp+28h] [rbp-128h] BYREF
      __int64 v3[2]; // [rsp+30h] [rbp-120h] BYREF
      char s[8]; // [rsp+40h] [rbp-110h] BYREF
      unsigned __int64 v5; // [rsp+148h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      puts("PLease input an interesting string!\x00Wrong!\x00Right!");
      fgets(s, 256, stdin);
      if ( s[strlen(s) - 1] == 10 )
        s[strlen(s) - 1] = 0;
      if ( strlen(s) != 8 )
        exit(0);
      v1 = *(_QWORD *)s;
      v2 = qword_55EDD8C040B8;
      qmemcpy(v3, "W3lc0meT0TSCTF!!", sizeof(v3));
      sub_55EDD8A013B5(&v1, v3);
      sub_55EDD8A00F31(&v2, v3);
      if ( v2 != v1 )
      {
        puts("Wrong!\x00Right!");
        exit(0);
      }
      puts(&::s[43]);
      return 0LL;
    }
    

    代码逻辑就是把输入的flagV1通过sub_55EDD8A013B5(一个改了delta的tea)加密,再把v2通过sub_55EDD8A00F31加密。

    对于对v2的加密函数

    unsigned __int64 __fastcall sub_55EDD8A00F31(__int64 a1, _DWORD *a2)
    {
      _DWORD v3[1042]; // [rsp+20h] [rbp-1050h] BYREF
      unsigned __int64 v4; // [rsp+1068h] [rbp-8h]
    
      v4 = __readfsqword(0x28u);
      sub_55EDD8A01493((unsigned int *)a1, -2, a2);
      sub_55EDD8A01C1C((__int64)v3, (__int64)a2, 16);
      sub_55EDD8A01B6B(v3, (int *)a1, (int *)(a1 + 4));
      return __readfsqword(0x28u) ^ v4;
    }
    

    这里可以使用hook把源代码直接复制出来(493可以直接复制,C1C和B6B应该是BLOWFISH,也可以用findcrypt插件(edit->plugins->findcrypt)
    注意这里的密钥是个SMC,具体可以在init处找到一个叫src串的交叉引用,然后在D3F之前找到一个RC4加密,不过一路动调到D3F处就可以直接获得密钥W3lc0meT0TSCTF!!(雾)

    下面就是写解密脚本了,由于式动态flag,先写到这,开摆。

  • 相关阅读:
    [SQL Server] sysobjects的介紹
    [R] [Johns Hopkins] R Programming -- week 3
    [R] 繪圖 Par 函数
    [R] [Johns Hopkins] R Programming -- week 4
    [Ms SQL] 基本創建、修改與刪除
    [R] Lexical & Dynamic Scoping / Execution & Calling environments / Closures
    [R] [Johns Hopkins] R Programming 作業 Week 2
    Python之路【第十七篇】:Django【进阶篇 】
    Django之Form组件
    Python之路【第十六篇】:Django【基础篇】
  • 原文地址:https://www.cnblogs.com/THRANDUil/p/16252153.html
Copyright © 2020-2023  润新知