SHCTF | 山河CTF Re&&Misc个人题解 进度:Week2
SHCTF-2024
主做Re&& Misc
Week1
ezrc4
IDA反汇编
分析可知v4是密文,v5是密钥,数据均以小端序存储,因此在解密时需要调整端序。
进入RC4加密函数,发现最后魔改了一下,做了一个异或0x66的操作,因此要更改解密函数
脚本如下:
#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分析,异或
脚本如下:
#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进行调试。首先输入符号链接文件的路径:
使用命令lm显示所有模块
点击EzDbg->functions->M显示EzDBG模块下的M开头的函数,其中包括main函数
找到main函数的地址00007ff6d4f31a10,WinDbg不便分析函数,使用IDA打开.dmp文件进一步分析
找到main函数的加密逻辑:
密文:
解题脚本:
#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
部署好之后连接本机与模拟器
分析源代码:
只需要让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中可以看到
最后输入任意内容即可得到flag
SHCTF{Yu_@re_Very_N8_oF_@nDr01d}
比较有意思的是本题AES加密的密文以及密钥其实已经给出了
密钥就是取了一张Jpg图片的前16个字节,但是通过这种方式解密我没有解出来。后来的师傅可以尝试一下。
babytea
被魔改了一部分的XTEA算法
IDA字符串搜索找到主函数
密钥和密文都很明显。XTEA算法
轮数,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中复现这段代码打印出生成的随机字串:
在apk中检查一下正确性:
因此flag:
SHCTF{QdUOJ7V7Xruo}
注:如果你在做这道题的时候发现点击按钮闪退,原因是安卓版本小于10.大多数模拟器默认的版本是安卓9,因此要另外装一个高版本进行调试
花语
初次接触花指令,实在是搞不动,费半天劲去掉了一部分花。
不过这个地方加密的算法已经暴露出来了,猜测后面是cmp判断flag的正误。
这里补一下解花后得到的代码:
感觉自己纸张了,因为刚开始没解出来这部分,直接逆了前面的算法,解出来的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字节得到以下内容:
呋食性意怎嗄咯非捕呱達嗚呦噤更取訴覺覺山捕吖萌嘶嘍噤寶你類嘶噤啽很有住出捕既哮噗圖咬喜樣註性
根据题面,显然是与熊论道加密,加上熊曰:的前缀丢进网站解密
SHCTF{F0ll0w_TaFFy_m1@0_ThanK5_M1@0}
拜师之旅
丢进TweakPNG查看,发现有一个IDAT块在中间而且没有满:
把前三个IDAT块删掉,按F7预览,得到flag:
SHCTF{1t_is_@_lucky_idat}
Schneider
有点幽默的的一道题,根据题面这个题想让我们找Schneider tool
Schneider是一家做工业软件的公司,旗下有产品EcoStruxure Operator Terminal Expert,这个软件导出的项目后缀恰好是.vxdz,那我们就下载这个软件打开附件即可
从官网找到安装包下载,体积有点庞大。安装后的打开附件
发现似乎是个空项目,仔细查找可以在UserGroup1找到Password,左上角ShowPassword即可
不是很懂这个题想考什么= =意义似乎不是很大,还得花时间下个完全用不到的软件
SHCTF{ez_ICS_checkin_right?}