SHCTF | 山河CTF Re&&Misc个人题解 进度:Week2

凉猹 / 2024-10-23 / 原文

SHCTF-2024

主做Re&& Misc

Week1

ezrc4

IDA反汇编

image-20241008132638415

分析可知v4是密文,v5是密钥,数据均以小端序存储,因此在解密时需要调整端序。

进入RC4加密函数,发现最后魔改了一下,做了一个异或0x66的操作,因此要更改解密函数

image-20241008132928669

脚本如下:

#include <stdio.h>
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
	int i = 0, j = 0;
	char k[256] = { 0 };
	unsigned char tmp = 0;
	for (i = 0; i < 256; i++) {
		s[i] = i;
		k[i] = key[i % Len_k];
	}
	for (i = 0; i < 256; i++) {
		j = (j + s[i] + k[i]) % 256;
		tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
	}
}


void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //解密
{
	unsigned char s[256];
	rc4_init(s, key, Len_k);
	int i = 0, j = 0, t = 0;
	unsigned long k = 0;
	unsigned char tmp;
	for (k = 0; k < Len_D; k++) {
		i = (i + 1) % 256;
		j = (j + s[i]) % 256;
		tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
		t = (s[i] + s[j]) % 256;
		Data[k] = Data[k]  ^ s[t] ^ 0x66;
	}
}
int main()
{
	unsigned char key[] ="FenKey!!";
	unsigned long key_len = sizeof(key) - 1;

unsigned char enc[] = {0x21,0xAB,0x3F,0x42,0x65,0x8F,0x3C,0x5B,
					   0x0C,0x17,0x05,0x6E,0x84,0xE7,0x1A,0x69,
					   0xC3,0x77,0x70,0x1F,0x11};


	rc4_crypt(enc, sizeof(enc), key, key_len);
    for(int i = 0;i<21;i++)printf("%c",enc[i]);

	return 0;
}

SHCTF{rc4_nice_ez!!!}

xor

IDA分析,异或

image-20241008135959502

脚本如下:

#include <stdio.h>

int main(){
    unsigned char enc[] = {0xC3,0x69,0x72,0xC4,0x67,0x4A,0xE8,0x11,
                           0x43,0xCF,0x6F,0xA,0xF3,0x44,0x6E,0xF8,
                           0x59,0x49,0xE8,0x4E,0x5E,0xE2,0x53,0x43,
                           0xB1,0x5C};
     for ( int j = 0; j < 26; ++j )
  {
    int v13 = j % 3;
    if ( j % 3 == 1 )
    {
      enc[j] ^= 0x21u;
    }
    else if ( v13 == 2 )
    {
      enc[j] ^= 0x31u;
    }
    else
    {
      enc[j] ^= 0x90u;
    }
    printf("%c",enc[j]);
  }
  //SHCTF{x0r_N1ce_hxxxoorrr!}

}

SHCTF{x0r_N1ce_hxxxoorrr!}

EzDBG

使用WinDbg进行调试。首先输入符号链接文件的路径:

image-20241008175747236

使用命令lm显示所有模块

image-20241008175828435

点击EzDbg->functions->M显示EzDBG模块下的M开头的函数,其中包括main函数

image-20241008175932216

找到main函数的地址00007ff6d4f31a10,WinDbg不便分析函数,使用IDA打开.dmp文件进一步分析

image-20241008180104228

找到main函数的加密逻辑:

image-20241008180136733

密文:

image-20241008180219447

解题脚本:

#include <stdio.h>

int main(){

    int enc[] = {0x35,0x2E,0x25,0x32,0x20,0x1D,0x3,
                 0x5E,0x7,0x56,0,0x3,0x57,0x57,0x53,
                 0x50,0,0x54,7,0,7,7,0,3,0x50,2,0x51,0x5E,0x5E,
                 3,0x5F,2,0x56,3,0x57,0,0x50,0x50,0x1B};
    for(int i =0;i<39;i++){
        int j = 0;
        j = enc[i] ^0x66;
        printf("%c",j);
    }
}

SHCTF{e8a0fe1156f2afaafe6d788e9d0e1f66}

GameGame

一个数独游戏

用 数独计算器得到:468912723481342575971422657913948591537428763345261

因此flag:SHCTF{468912723481342575971422657913948591537428763345261}

ezapk

base64解密以及简单的四则运算逆向。本题EXP丢失,所以不能展示了= =

Week2

Re

cancanneed

首先搭建好frida的环境

pip install frida
pip install frida-tools

再去frida的官网下载frida-server,确保二者版本一致

我使用的模拟器,架构为x86_64

部署好之后连接本机与模拟器

分析源代码:

image-20241009205845057

只需要让if语句执行到这里就会弹出flag,考虑到check使用了不可逆的md5加密,所以选择用frida动态hook掉函数来执行

js代码如下:

Java.perform(function() {
    var MainActivity = Java.use("com.example.test.MainActivity");
    
    MainActivity.check.implementation = function(trim) {
        return 1;  // 直接返回 1
    };
});

非常简单,只需要hook掉check使其永远为1即可执行下面的代码

在vscode的终端使用命令:frida -U -f "com.example.test" -l ezhook.js

其中""包裹的为包名,在AndroidManifest.xml中可以看到

2b188928-8684-4173-86f5-f60ada78b307

最后输入任意内容即可得到flag

SHCTF{Yu_@re_Very_N8_oF_@nDr01d}

比较有意思的是本题AES加密的密文以及密钥其实已经给出了

密钥就是取了一张Jpg图片的前16个字节,但是通过这种方式解密我没有解出来。后来的师傅可以尝试一下。

babytea

被魔改了一部分的XTEA算法

IDA字符串搜索找到主函数

密钥和密文都很明显。XTEA算法
image-20241010100953989

轮数,sum初值,轮函数以及Delta值都被魔改了,对着写解密脚本即可:

#include <stdio.h>
#include <stdint.h>
 
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x61C88747, sum=delta*num_rounds+0x8DDE2E40;
    for (i=0; i < num_rounds; i++) {   
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]) ^ v1;
        sum -= delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]) ^ v0;
    }
    v[0]=v0; v[1]=v1;
}
int main()
{
    uint32_t enflag[] = {0x18C2E339, 0xE9550982, 0x108A30F7, 0x018430DD, 0xD5DE57B0, 0xD43E0740, 0xF42FDDE4, 0x968886E8,0xE5D77B79,0x685D758F};
    uint32_t key[4] = {1,1,2,3};
    for(int i=0;i<10;i+=2)
    {
        uint32_t temp[2];        
        temp[0] = enflag[i];
        temp[1] = enflag[i+1];
        decipher(64,temp,key);
        printf("%c%c%c%c%c%c%c%c",*((char*)&temp[0]+0),*((char*)&temp[0]+1),*((char*)&temp[0]+2),*((char*)&temp[0]+3),*((char*)&temp[1]+0),*((char*)&temp[1]+1),*((char*)&temp[1]+2),*((char*)&temp[1]+3));
    }
    return 0;
}

shctf{962fd-464d-8f4f-f1fd-a6a0c987c569}

Loader

jadx反编译,审计代码,发现用inMemoryDexClassLoader动态加载了一个类

so层加载后只使用了一个函数loadData,从enc文件中载入Dex的字节码,用于动态加载com.android.loader.GetFlag这个类。

另外OnClick类中判断了Build.VERSION.SDK_INT,即安卓版本,大于10才可以使用

分析完以上信息,用一个工具frida-dexdump把动态加载的类给dump下来。

注意因为类的加载是在OnClick方法中,所以得先点一下按钮才能在内存中加载出需要的类

dump下来20多个dex,逐一转成.jar,发现第一个就是我们需要的,代码如下:

package com.android.loader;

import java.util.Random;

public class GetFlag {
  private static final String CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  
  public static boolean VerifyFlag(String paramString) {
    return paramString.equals(generateRandomString(4310, 12));
  }
  
  public static String generateRandomString(int paramInt1, int paramInt2) {
    Random random = new Random(paramInt1);
    StringBuilder stringBuilder = new StringBuilder(paramInt2);
    for (paramInt1 = 0;; paramInt1++) {
      if (paramInt1 >= paramInt2)
        return stringBuilder.toString(); 
      int i = random.nextInt(CHARACTERS.length());
      stringBuilder.append(CHARACTERS.charAt(i));
    } 
  }
}

这是一段伪随机数生成代码,随机数种子已经给定,在java中复现这段代码打印出生成的随机字串:

image-20241010161353141

在apk中检查一下正确性:

image-20241010161821198

因此flag:

SHCTF{QdUOJ7V7Xruo}

注:如果你在做这道题的时候发现点击按钮闪退,原因是安卓版本小于10.大多数模拟器默认的版本是安卓9,因此要另外装一个高版本进行调试

花语

image-20241010202408114

初次接触花指令,实在是搞不动,费半天劲去掉了一部分花。

image-20241010213354784

不过这个地方加密的算法已经暴露出来了,猜测后面是cmp判断flag的正误。

这里补一下解花后得到的代码:

image-20241010220807687

感觉自己纸张了,因为刚开始没解出来这部分,直接逆了前面的算法,解出来的flag明显不对。使劲解花后才发现原来不需要逆向,正向输出就是flag。

= =还需要学习,这块真是我的软肋,做安卓逆向都没这么吃力

话说这题的flag是自然语言,眼力够好的话直接能看出flag

SHCTF{keY_is_hua_fffllllaggg!}

Android?Harmony!

最红温的一题

网上找了一个可以看鸿蒙字节码的jadx

解出口令:

b4c4S20331H3cf208Cb9Tbebc2a83a1a6d4F96b45-8942-8{e55503d5c-1abe-18d99d75fd7e4463978a1a1b2995093d6db9cf922b-332642719-16451c451c512da4ae516a618-f5bf4dc1e10}8844d18-d5dae11b-b5d4da4736fc

鸿蒙模拟器输入之后创建了一个What文件,其实是一个迷宫

EXP如下:

由于迷宫太大这里就不展示maze的初始化了:

def bfs(start, end, barrier):
    directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]  # 定义四个方向的移动
    for i in range(len(maze)):  # 获取起点和终点在列表中的索引
        for j in range(len(maze[i])):
            if (maze[i][j] == start):
                start = (i, j)
            if (maze[i][j] == end):
                end = (i, j)
    # 以下均是bfs算法套路
    queue = deque()
    queue.append((start, [start]))  # (当前位置, 路径)
    visited = set()
    visited.add(start)
    while queue:
        position, path = queue.popleft()
        if position == end:
            return path
        elif len(path) == path_len:
            return path
        for d in directions:
            next_position = (position[0] + d[0], position[1] + d[1])
            if 0 <= next_position[0] < len(maze) and 0 <= next_position[1] < len(maze[0]) and \
                    maze[next_position[0]][next_position[1]] != barrier and next_position not in visited:
                queue.append((next_position, path + [next_position]))
                visited.add(next_position)
    return None


# 执行BFS搜索并打印结果
if __name__ == '__main__':
    # maze[77][1] = 'S' #如果题目给了起点终点的坐标,在这里直接给起点和终点添加特征
    # maze[1][83] = 'E'

    path = bfs('S', 'E', '#')  # bfs函数传入参数代表起点、终点、障碍的特征(若题目给出的数据无特征, 手动添加特征即可, 通常障碍是1也有可能是0或其它字符如'#')
    print("移动路径坐标:", path)
    print("移动路径方位:{", end='')

    for i in range(1, len(path)):
         x1, y1 = path[i - 1][0], path[i - 1][1]
         if(maze[x1][y1] != ' '):
            print(maze[x1][y1],end = '')

    for i in range(1, len(path)):
        x1, y1, x2, y2 = path[i - 1][0], path[i - 1][1], path[i][0], path[i][1]
        if (x1 > x2):  # 上
            print("w", end='')
        elif (x1 < x2):  # 下
            print("s", end='')
        elif (y1 > y2):  # 左
            print("a", end='')
        elif (y1 < y2):  # 右
            print("d", end='')
    print('}')

按照路径输出字符串即可:

SHCTF{81f6ad65-9da6-41ae-bd61-88dea61332f1}

Misc

遮遮掩掩?CCRC!

CRC32爆破,3字节得到以下内容:

呋食性意怎嗄咯非捕呱達嗚呦噤更取訴覺覺山捕吖萌嘶嘍噤寶你類嘶噤啽很有住出捕既哮噗圖咬喜樣註性

根据题面,显然是与熊论道加密,加上熊曰:的前缀丢进网站解密

image-20241011144126771

SHCTF{F0ll0w_TaFFy_m1@0_ThanK5_M1@0}

拜师之旅

丢进TweakPNG查看,发现有一个IDAT块在中间而且没有满:

image-20241011144943800

把前三个IDAT块删掉,按F7预览,得到flag:

image-20241011145102937

SHCTF{1t_is_@_lucky_idat}

Schneider

有点幽默的的一道题,根据题面这个题想让我们找Schneider tool

Schneider是一家做工业软件的公司,旗下有产品EcoStruxure Operator Terminal Expert,这个软件导出的项目后缀恰好是.vxdz,那我们就下载这个软件打开附件即可

从官网找到安装包下载,体积有点庞大。安装后的打开附件

发现似乎是个空项目,仔细查找可以在UserGroup1找到Password,左上角ShowPassword即可

image-20241011232927441

不是很懂这个题想考什么= =意义似乎不是很大,还得花时间下个完全用不到的软件

SHCTF{ez_ICS_checkin_right?}