LOADING

加载过慢请开启缓存 浏览器默认开启

一些题目记录整理

2024/11/7

主站题目

easy_py.cpython-38

注意线程1执行的时候对flag的修改不会似乎同步到线程2,线程2异或的下一个值仍为修改前的值。

Happy = [44, 100, 3, 50, 106, 90, 5, 102, 10, 112]
result = [0]*10
for i in range(9,-1,-2):
    result[i]=Happy[i] ^ i

for i in range(8,-2,-2):
    result[i]=Happy[i] ^ Happy[i+1]

for i in range(0,10):
    print(chr(result[i]),end="")

输入flag以获取flag

观察发现是Electron,搜一下Electron逆向

npm install asar -g
cd apps
asar extract app.asar app //解压拿到源码
asar pack app app.asar //重新打包

就可以拿到源码。然后分析。

但是文件有main.jsc,无法直接查看。发现program.js中含有信息。使用bytenode进行编译,网上搜索不断尝试,还是没能复原

后来继续搜索看见了两位学长的wpimage-20241204152325826

image-20241204162510212

两次题目似乎有些许区别,SSSCTF2024应该加了一个联网获取ip的判断

看来之前尝试逆向main.jsc思路可能不太行,跟着学长博客复现了一遍

先hook一下加密方法,获取一下原文

const { Hash } = require('crypto');
const _update = Hash.prototype.update;
Hash.prototype.update = function (...args) {
    console.log('Hash update', args);
    return _update.apply(this, args);
}

改一下host,再写一个小程序返回数据

from flask import Flask, request, jsonify
json = {"status": "success",
    "country": "China",
    "countryCode": "CN",
    "region": "TT",
    "regionName": "Tianting",
    "city": "NanTianMen",
    "zip": "",
    "lat": 38.8804,
    "lon": 121.529,
    "timezone": "Asia/Shanghai",
    "as": "AS4538 China Education and Research Network Center",
    "query": "210.30.97.133"}

app = Flask(__name__)
#/json
@app.route('/json/', methods=['GET'])
def get_json():
    return jsonify(json)
#run
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=80)

myFor

输入一个矩阵,程序会返回自己输入数据加密后对应值跟理想数据,直接爆破

1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6

ezex

异常处理。image-20241206125454569

我是修改了ida跳转逻辑,让ida可以分析异常处理的代码

image-20241206125528393

大意是是奇偶数分别异或。写出解密脚本

Buf2 =[0]*28
Buf2[0] = 341250117
Buf2[1] = 142350161
Buf2[2] = 542393405
Buf2[3] = 926947173
Buf2[4] = 962408270
Buf2[5] = 592914762
Buf2[6] = 456790650
Buf2[7] = 0x413c
str = bytearray()
for i in Buf2:
    str.extend(i.to_bytes(4, 'little'))
for a in range(30, -1, -1):
    if a%2==0:
        str[a]^=a+1
    else:
        str[a]^=str[(a-1)%len(str)]
print(str)
'''DUTCTF{t4y_thrOw_aNd_c@tch!!!}'''

ezre1

main函数那个解密后是假的flag

后来继续看代码发现一个疑似加密的地方

image-20241206144749830

疑似这里才是真正的加密部分,尝试写一个解密脚本。

其中

  if ( rand() % 2 )
      s[i] = ~(s[i] & s[(i + 1) % v3]) & (s[(i + 1) % v3] | s[i]);
    else
      s[i] = (s[i] ^ s[(i - 1) % v3]) + 2 * (s[i] & s[(i - 1) % v3]);

这部分逆向操作需要注意一下。

当rand为奇数时,

Game!

shift+f12查看字符串,然后定位到疑似解密部分

image-20241206230850359

然后就直接SetIp到这里,随后继续执行,获得flag

image-20241206231009310

NSSCTF题目

[GXYCTF 2019]luck_guy

动态调试就可以,把流程走一遍

GXY{do_not_hate_me}

[HZNUCTF 2023 final]虽然他送了我玫瑰花

花指令

text:0040114D                 xor     ebx, ebx
.text:0040114F                 test    ebx, ebx
.text:00401151                 jnz     short near ptr byte_401155
.text:00401153                 jz      short loc_401156

ebx肯定为0,所以nop掉jnz,然后把jz换成jmp

image-20241223170127109

输入数据进不同函数异或。

解密脚本如下:

arr = [ 127, 126,  81, 206, 251,  78, 122,  36, 232, 223,
   89, 113,  38, 202, 225, 108, 134,  33, 204, 245,
   40, 113,  20, 216, 239, 110, 119,  98, 250]
for i in range(len(arr)):
    aa = i%5
    if aa==0:
        arr[i] = arr[i]^0x19
    elif aa==1:
        arr[i] = arr[i]-18
    elif aa==2:
        arr[i] = arr[i]+16
    elif aa==3:
        for cc in range(3):
            arr[i] = arr[i]/2+0x7f*cc
            if 32<=arr[i]<=126:
                break
    elif aa==4:
        for cc in range(3):
            arr[i] = ~(arr[i]+0x80*cc)
            if 32<=arr[i]<=126:
                break
print(len(arr))
for i in range(len(arr)):
    print(chr(int(arr[i])&0xff), end='')

[SWPUCTF 2021 新生赛]老鼠走迷宫(主站BabyMaze)

Pyc反编译

用pyinstxtractor对程序处理,得到5.pyc,随后用www.pylingual.io进行反编译,但是得到代码不完整,随后使用uncompyle6,却得到了完整的代码。(奇奇怪怪

分析代码,发现1为墙壁0为路,要找到最短路径。(搜一下就能解决但是还是假装研究一下吧)

[MoeCTF 2022]chicken_soup

花指令,将两个地方修改一下就可以继续用ida了。

随后分析对数据加密的两个函数

unsigned int __cdecl sub_D51080(const char *a1)
{
  unsigned int result; // eax
  unsigned int i; // [esp+18h] [ebp-8h]

  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= strlen(a1) )
      break;
    a1[i] = (16 * a1[i]) | ((int)(unsigned __int8)a1[i] >> 4);
  }
  return result;
}
void __cdecl sub_D51000(const char *a1)
{
  unsigned int i; // [esp+18h] [ebp-8h]

  for ( i = 0; i < strlen(a1) - 1; ++i )
    a1[i] += a1[i + 1];
}

先逆向第一个过程,第一个过程将数据左移四位后和右移四位进行或运算。(实际上似乎是将十六进制的数字两位进行了交换,例如0xCD变成了0xDC),再写一次就可以交换回来。

第二个过程倒着减一下。

解密代码:(获取data时使用shift+E可以一键提取)

#include <stdio.h>
int main(){
    unsigned char data[] = {
        0xCD, 0x4D, 0x8C, 0x7D, 0xAD, 0x1E, 0xBE, 0x4A,
        0x8A, 0x7D, 0xBC, 0x7C, 0xFC, 0x2E, 0x2A, 0x79,
        0x9D, 0x6A, 0x1A, 0xCC, 0x3D, 0x4A, 0xF8, 0x3C,
        0x79, 0x69, 0x39, 0xD9, 0xDD, 0x9D, 0xA9, 0x69,
        0x4C, 0x8C, 0xDD, 0x59, 0xE9, 0xD7
    };
    for(int i=0;i<38;i++){
        unsigned char b = data[i];
        data[i] = b<< 4 | (b >> 4);
    }
    for(int i=36;i>=0;i--){
        data[i] -=data[i+1];
    }
    printf("%s",data);
}

[NISACTF 2022]string

分析代码,前面好多都是干扰,实际上真正起作用的只有几行

srand(seed);
  printf("NSSCTF{");
  for ( m = 0; m < v10; ++m )
  {
    v4 = rand();
    printf("%d", (unsigned int)(v4 % 8 + 1));
  }
  putchar(125);

有一个小坑是必须要linux写出来的程序得到的才是正确的答案,win的写出来是错的。因为这个rand依赖于libc版本

[HGAME 2023 week1]easyenc

main函数里就有核心代码

v4 = -1i64;
  do
    ++v4;
  while ( *((_BYTE *)v10 + v4) );
  if ( v4 == 41 )
  {
    while ( 1 )
    {
      v5 = (*((_BYTE *)v10 + v3) ^ 0x32) - 86;
      *((_BYTE *)v10 + v3) = v5;
      if ( *((_BYTE *)v8 + v3) != v5 )
        break;
      if ( ++v3 >= 41 )
      {
        v6 = "you are right!";
        goto LABEL_8;
      }
    }
    v6 = "wrong!";

有一个地方需要注意一下就是IDA给的伪代码不完全准确,导致v8显示的数据不全,

image-20241111220925083

image-20241111220952998

可以看到F9这个数据没有被IDA伪代码状态下显示出来。

[NSSRound#3 Team]jump_by_jump

image-20241113201117900

shift+f12居然直接看到了flag。不过假装自己没看见,分析一下。

[CISCN 2022 东北]easycpp

main函数里写了加密逻辑,enc[0]^enc[1],1异或2,2异或3,然后下一轮1异或2。只需要反向异或就行。附上解密代码。

#include <stdio.h>
int main(){
    unsigned char enc[] =
    {
        0x0A, 0x0B, 0x7D, 0x2F, 0x7F, 0x67, 0x65, 0x30, 0x63, 0x60, 
        0x37, 0x3F, 0x3C, 0x3F, 0x33, 0x3A, 0x3C, 0x3B, 0x35, 0x3C, 
        0x3E, 0x6C, 0x64, 0x31, 0x64, 0x6C, 0x3B, 0x68, 0x61, 0x62, 
        0x65, 0x36, 0x33, 0x60, 0x62, 0x36, 0x1C, 0x7D
    };
    for(int i=34;i>=0;i--){
        enc[i+2]^=enc[i+3];
        enc[i+1]^=enc[i+2];
        enc[i]^=enc[i+1];
    }
    printf("%s",enc);
}

[CISCN 2023 初赛]babyRE

xml给了一个网站,访问网站导入xml。我写了一个py脚本导出加密数据。

import xml.dom.minidom as minidom
dom=minidom.parse('[CISCN 2023 初赛]babyRE.xml')
root=dom.documentElement
block = root.getElementsByTagName('l')
last = block[3].firstChild.data
enc = []
print(len(block))
for i in range(0, len(block),2):
    print(block[i+1].firstChild.data)
    if block[i+1].firstChild.data!=last:
        enc.insert(int(block[i+1].firstChild.data)-1, block[i].firstChild.data)
    else:
        enc.append(block[i].firstChild.data)
print(enc)

随后分析一下加密过程。

image-20241119121800750

这里注意一下当i=1的时候第0个字符似乎会默认为0处理,也就是test第一个元素没有做处理。

解密脚本:

enc=['102', '10', '13', '6', '28', '74', '3', '1', '3', '7', '85', '0', '4', '75', '20', '92', '92', '8', '28', '25', '81', '83', '7', '28', '76', '88', '9', '0', '29', '73', '0', '86', '4', '87', '87', '82', '84', '85', '4', '85', '87', '30']
for i in range(0,len(enc)):
    enc[i] = int(enc[i])
for i in range(0,len(enc)-1):
    enc[i+1]^=enc[i]
for i in range(0,len(enc)):
    print(chr(enc[i]),end='')

[LitCTF 2023]程序和人有一个能跑就行了

[HGAME 2023 week2]math

v11[5 * i + j] += *((char *)&savedregs + 5 * i + k - 368) * v10[5 * k + j];

关键代码,可以理解成

v11[5 * i + j] += input1[5 * i + k] * v10[5 * k + j];

其中i,j,k均属于0到4,且原始v11为0

得出结果后跟v12比较,即v12为最终结果

可知v12[0]的值是当i和j都为0时的解,k=1,2,3,4以此类推

随后用z3解方程。

[LitCTF 2023]snake

pyc文件,与以往不同的是需要改头文件的Magic Number。

enum PycMagic {
    MAGIC_1_0 = 0x00999902,
    MAGIC_1_1 = 0x00999903, /* Also covers 1.2 */
    MAGIC_1_3 = 0x0A0D2E89,
    MAGIC_1_4 = 0x0A0D1704,
    MAGIC_1_5 = 0x0A0D4E99,
    MAGIC_1_6 = 0x0A0DC4FC,

    MAGIC_2_0 = 0x0A0DC687,
    MAGIC_2_1 = 0x0A0DEB2A,
    MAGIC_2_2 = 0x0A0DED2D,
    MAGIC_2_3 = 0x0A0DF23B,
    MAGIC_2_4 = 0x0A0DF26D,
    MAGIC_2_5 = 0x0A0DF2B3,
    MAGIC_2_6 = 0x0A0DF2D1,
    MAGIC_2_7 = 0x0A0DF303,

    MAGIC_3_0 = 0x0A0D0C3A,
    MAGIC_3_1 = 0x0A0D0C4E,
    MAGIC_3_2 = 0x0A0D0C6C,
    MAGIC_3_3 = 0x0A0D0C9E,
    MAGIC_3_4 = 0x0A0D0CEE,
    MAGIC_3_5 = 0x0A0D0D16,
    MAGIC_3_5_3 = 0x0A0D0D17,
    MAGIC_3_6 = 0x0A0D0D33,
    MAGIC_3_7 = 0x0A0D0D42,
    MAGIC_3_8 = 0x0A0D0D55,
    MAGIC_3_9 = 0x0A0D0D61,
};

注意一下用010改的时候要反着写,例如0x0A0D0D42改成image-20241125181911770

[WUSTCTF 2020]Cr0ssfun

直接明文给了flag,image-20241125182448740

[CISCN 2021初赛]babybc

查了一下bc文件,应该先用

clang output.bc -o program

生成可执行文件

v27 = 0LL;
  do
  {
    v24 = v27;
    memset(v29, 0, sizeof(v29));
    v25 = &v29[(unsigned __int8)map[5 * v27]];
    if ( *v25
      || (*v25 = 1, v23 = &v29[(unsigned __int8)map[5 * v27 + 1]], *v23)
      || (*v23 = 1, v2 = &v29[(unsigned __int8)map[5 * v27 + 2]], *v2)
      || (*v2 = 1, v1 = &v29[(unsigned __int8)map[5 * v27 + 3]], *v1)
      || (*v1 = 1, v29[(unsigned __int8)map[5 * v27 + 4]]) )
    {
      v22 = 0;
      return v22 & 1;
    }
    ++v27;
  }

这是在检查同一行元素是否相同

总体规则是:输入25个数字(1-5),填充到5*5map中。每行每列元素不同。

  • row[4 * v16] 对应于 map[5 * v16]map[5 * v16 + 1] 的比较。

  • row[4 * v16 + 1] 对应于 map[5 * v16 + 1]map[5 * v16 + 2] 的比较,以此类推。

  • 如果

    row[x] == 1
    
    • 当前元素必须小于下一个元素(递增)。
  • 如果

    row[x] == 2
    
    • 当前元素必须大于下一个元素(递减)。

col是相反的。

import z3
row = [0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0]
col = [0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
r=5
c=5
s = z3.Solver()
m = [[z3.Int(f"m_{i}_{j}") for j in range(c)] for i in range(r)]
print(m)
s.add(m[2][2]==4,m[3][3]==3)
for i in range(r):
    for j in range(c):
        s.add(1 <= m[i][j], m[i][j] <= 5)
for i in range(r):
    for j in range(c):
        for k in range(j+1, c):
            s.add(m[i][j] != m[i][k])
for i in range(c):
    for j in range(r):
        for k in range(j+1, r):
            s.add(m[j][i] != m[k][i])
for i in range(20):
    if row[i] == 1:
        s.add(m[i//4][i%4] > m[i//4][i%4+1])
    if row[i] == 2:
        s.add(m[i//4][i%4] < m[i//4][i%4+1])
for i in range(20):
    if col[i] == 1:
        s.add(m[i//5][i%5] < m[i//5+1][i%5])
    if col[i] == 2:
        s.add(m[i//5][i%5] > m[i//5+1][i%5])
if s.check() == z3.sat:
    model = s.model()
    for i in range(r):
        for j in range(c):
            print(model[m[i][j]], end='')
else:
    print("No solution found.")
'''
把给的数字换成0
14253
53142
35021
21504
42315
'''

map给了两个数字要换成0因为fill_number里

v11 = v14;
    v12 = 5 * v14;
    v13 = *(_BYTE *)(a1 + 5 * v14);
    if ( map[5 * v14] )
    {
      v10 = 0;
      if ( v13 != 48 )
        return v10 & 1;
    }
    else
    {
      map[5 * v14] = v13 - 48;
    }

若map数据非0,则必须输入为0,否则程序退出。

[MoeCTF 2021]baby_bc

先生成可执行文件。

image-20241126094420931

分析加密逻辑。本题采用RC4加密。先输入长度40的v9。在进行RC4加密。然后进入HSencode。解密则需要先逆向HSencode的过程,再想办法得到密钥流然后直接异或就得到原文了。

[HNCTF 2022 WEEK2]来解个方程?

考察z3使用。

from z3 import *
s = z3.Solver()
v=[Int('v%d' % i) for i in range(0,50)] #批量添加变量
s.add() #添加约束条件
s.check() #判断是否添加的约束条件是否有解,有解返回sat,无解返回unsat
a = s.model() #求解
for i in v:
    if a[i] is not None:
        print(chr(a[i].as_long()), end='') #打印字符

[CISCN 2022 东北]happymath

输入长度为32的字符串写入byte_7FF73623AC30中,

ISCTF2024新生赛题目

MIPS

需要先把文件编译一下。

Linux安装一下

sudo apt install gcc-mips-linux-gnu

然后

 mips-linux-gnu-gcc -o MIPS MIPS.s

就可以使用ida进行分析了(ida9才可以反编译)

v3 = [104,74,120]
arr = [214,  50,  39,  78, 119, 202, 213, 118, 163,  33,
    2,   9, 104,   9,  69, 137,  54, 154, 186, 144,
  236,  71, 152, 248,  254, 240,0x4b]
v5 = [17,43,26, 1,32,  17,  7, 36,  18,   1, 37,  22]
print(len(v5))
for i in range(len(v5)):
    v5[i] = chr(v5[i] ^ v3[i%3])
print(v5)
key = v5
def gen(key):
    s = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s[i] + ord(key[i % len(key)])) % 256
        tmp = s[i]
        s[i] = s[j]
        s[j] = tmp
    i = j = 0
    data = []
    for _ in range(50):
        i = (i + 1) % 256
        j = (j + s[i]) % 256
        tmp = s[i]
        s[i] = s[j]
        s[j] = tmp
        data.append(s[(s[i] + s[j]) % 256])
    return data
key = gen(key)
enc = arr
c=0
for i in range(len(enc)):
    enc[i]^=key[c]^82
    c+=1
print(bytes(enc))

魔改RC4,多异或了一个82,然后key被异或修改了。

《回忆安魂曲》–第二章:当记忆被割裂

这是一个ELF文件,我采用IDA远程wsl进行动态调试

IDA配置远程调试

在wsl下使用

./linux_server64

参考文档:IDA远程动态调试(linux & Windows)_ida远程调试-CSDN博客

配置好动调之后,在String找到了加密的key:

i_can_reverse_but_i_can_not_have_you

image-20241111222544679

找到函数但是发现IDA无法生成伪代码

image-20241111222638795

需要把这个改成jmp rax之后可以看到伪代码

image-20241111222712637

总共好几个加密函数,通过动态调试可以知道执行顺序,随后写出解密脚本如下:

# 解密函数
def decrypt_data(encrypted_data, key):
    decrypted_data = []

    for i, enc_byte in enumerate(encrypted_data):
        key_byte = key[i]
        enc_byte ^= key_byte+i
        enc_byte -= 6
        enc_byte ^= (i + 102) ^ 0x52
        decrypted_data.append(enc_byte)

    return decrypted_data


# 示例加密后的数据
encrypted_data = [
    0xEA, 0x0C, 0x1A, 0x11, 0xF6, 0x2C, 0x1D, 0x3E, 0x17, 0x35,
    0x31, 0x29, 0xF4, 0x39, 0x39, 0xD3, 0xC3, 0x2D, 0x00, 0x10,
    0x30, 0x3D, 0xCC, 0x00, 0xD3, 0xC0, 0x4B, 0xC6, 0x11, 0xC7,
    0x29, 0x3E, 0xBA, 0x60, 0x90, 0x34
]  # 假设这是加密后的字节

# 密钥
key = [0x69, 0x5F, 0x63, 0x61, 0x6E, 0x5F, 0x72, 0x65, 0x76, 0x65,
       0x72, 0x73, 0x65, 0x5F, 0x62, 0x75, 0x74, 0x5F, 0x69, 0x5F,
       0x63, 0x61, 0x6E, 0x5F, 0x6E, 0x6F, 0x74, 0x5F, 0x68, 0x61,
       0x76, 0x65, 0x5F, 0x79, 0x6F, 0x75]

# 恢复加密前的数据
decrypted_data = decrypt_data(encrypted_data, key)

# 打印恢复后的数据(以十六进制表示)
print([hex(byte) for byte in decrypted_data])
for i in decrypted_data:
    print(chr(i), end='')

《回忆安魂曲》–第三章:逃不出的黑墙

很经典,就是上面的走迷宫问题

找啊找

问了大佬才知道这个是有壳的。用DIE查壳发现有壳image-20241114232843873

但是直接用upx -d解不出来。网上搜了一下,这个文件好像区段名被修改。(贴一个参考网页UPX防脱壳机脱壳、去除特征码、添加花指令小探 - 吾爱破解 - 52pojie.cn

image-20241114233409112

这里把APK改成UPX,随后就可以upx -d解出来了。

随后有一个小坑就是main里边的unk_40040在程序执行时被悄悄更改了值,动调可以看出来。拿到最后flag

def reverse(s):
    res = ""
    for i in s:
        if i.islower():
            res += i.upper()
        elif i.isupper():
            res += i.lower()
        else:
            res += i
    return res
s = [0x50, 0x4A, 0x5A, 0x4D, 0x5F, 0x42, 0x7E, 0x76, 0x76, 0x5D,
        0x18, 0x18, 0x08, 0x4A, 0x56, 0x66, 0x60, 0x56, 0x4C, 0x66,
        0x5F, 0x08, 0x57, 0x5D, 0x66, 0x08, 0x6D, 0x66, 0x54, 0x79,
        0x50, 0x57, 0x18, 0x18, 0x79, 0x44]
for i in s:
    i^=0x39
    print(reverse(chr(i)),end="")

桀桀桀

比赛时没有做出来,赛后看看网上的wp复现一遍。

先使用z3解方程,答案是ISCTF。

第二步魔改Tea(注意key被TLS改了)。

存在jz/jnz花指令,nop掉

int __cdecl tea_0(unsigned int *a1, int *a2)
{
  int result; // eax
  int v3; // [esp+D0h] [ebp-68h]
  int v4; // [esp+DCh] [ebp-5Ch]
  int v5; // [esp+E8h] [ebp-50h]
  int v6; // [esp+F4h] [ebp-44h]
  int i; // [esp+100h] [ebp-38h]
  int v8; // [esp+10Ch] [ebp-2Ch]
  int v9; // [esp+118h] [ebp-20h]
  unsigned int v10; // [esp+124h] [ebp-14h]
  unsigned int v11; // [esp+130h] [ebp-8h]

  v11 = *a1;
  v10 = a1[1];
  v9 = 0;
  v8 = -1640531527;
  v6 = *a2;
  v5 = a2[1];
  v4 = a2[2];
  v3 = a2[3];
  for ( i = 0; i < 32; ++i )
  {
    v9 += v8;
    v11 += (v5 + (v10 >> 5)) ^ (v9 + v10) ^ (v6 + 16 * v10);
    v10 += (v3 + (v11 >> 5)) ^ (v9 + v11) ^ (v4 + 16 * v11);
    if ( i == 15 )
    {
      v6 = a2[2];
      v5 = a2[3];
      v4 = *a2;
      v3 = a2[1];
    }
    else if ( i == 23 )
    {
      v8 = 1131796;
    }
  }
  *a1 = v11;
  result = 4;
  a1[1] = v10;
  return result;
}

然后第三步:

rand();
v9 = sub_41126C();
enc2(Str, v9);

注意这个代码,对应汇编如下。

.text:004121D6                 call    ds:rand
.text:004121DC                 cmp     esi, esp
.text:004121DE                 call    sub_41126C
.text:004121E3                 mov     [ebp+var_74], eax
.text:004121E6                 mov     eax, [ebp+var_74]
.text:004121E9                 push    eax             ; int
.text:004121EA                 lea     ecx, [ebp+Str]
.text:004121ED                 push    ecx             ; Str
.text:004121EE                 call    enc2

如果sub_41126C未返回值的话那么v9就应该是rand的返回值,ida分析有误。

再来看enc2的内容

void __cdecl sub_411B90(char *Str, int a2)
{
  char v2; // cl
  signed int v3; // [esp+D0h] [ebp-2Ch]
  signed int v4; // [esp+DCh] [ebp-20h]

  v3 = 0;
  v4 = j_strlen(Str);
  while ( v3 < v4 )
  {
    if ( v3 % 2 )
      v2 = (a2 >> 6) ^ Str[v3];
    else
      v2 = (a2 - ((unsigned __int8)(a2 >> 6) << 6)) ^ Str[v3];
    Str[v3++] = v2;
  }
}

对str进行了奇偶分组异或操作。

解密操作:先奇偶分组异或,然后Tea解密。

解密脚本

#include <iostream>
#include <string.h>
#include <stdlib.h>
void DeTea(unsigned char* enc, uint32_t* key, uint32_t XorNum) {
    uint32_t k1, k2, k3, k4;
    uint32_t sum, delta;
    uint32_t v1, v2;
    uint32_t* flag = NULL;
    int index = 0;
    int c = 0;
    flag = (uint32_t*)enc;
    int key1, key2, len;
    int i = 0;
    key1 = XorNum >> 6;
    key2 = XorNum - (key1 << 6);
    len = 32;
    for (i=0; i < len; i++) {
        if (i % 2) {
            enc[i] ^= key1;
        }
        else {
            enc[i] ^= key2;
        }
    }
    k1 = key[2], k2 = key[3], k3 = key[0], k4 = key[1];
    for (c = 0; c < 4; c++) {
        delta = 0x114514;
        k1 = key[2], k2 = key[3], k3 = key[0], k4 = key[1];
        sum = delta * 8 + 24 * 0x9e3779b9;
        v1 = flag[c * 2];
        v2 = flag[c * 2 + 1];
        for (index=0; index < 32; index++) {
            v2 -= ((v1 << 4) + k3) ^ (v1 + sum) ^ ((v1 >> 5) + k4);
            v1 -= ((v2 << 4) + k1) ^ (v2 + sum) ^ ((v2 >> 5) + k2);
            sum -= delta;
            if (index == 7) {
                delta = 0x9e3779b9;
            }
            else if (index == 15) {
                k1 = key[0];
                k2 = key[1];
                k3 = key[2];
                k4 = key[3];
            }
        }
        flag[c * 2] = v1;
        flag[c * 2 + 1] = v2;
    }
    printf("%s\n",flag);
}

int main(){
    unsigned char enc[60]={
        177, 210, 249, 122, 131,  76,  81,  35, 183, 173, 
        169, 190, 232, 250,  36,  22, 147, 254,  66, 215, 
        176,  31,  82, 247,  90, 125, 128, 232,  40, 252, 
        65, 111
    }; 
    int Seed=0;
    char v10[6] = "ISCTF";

    for (int i = 0; i < 5; ++i )
        Seed += v10[i] ^ 0xA1;
    uint32_t key[4] = {28519, 26994, 26740, 29549};
    for(int i=0;i<4;i++){
        key[i]^=0xA1u;
    }
    srand(Seed);
    int xornum = rand();
    DeTea(enc,key,xornum);
    printf("%s",enc);
}

注意一下int Seed=0。一开始没让它=0,导致一直不行。

ISCTF2023新生赛题目

FlowerRSA

首先有一个花指令

  xor     rbx, rbx
.text:0000000000001268                 test    rbx, rbx
.text:000000000000126B                 jnz     short labela
.text:000000000000126D                 jz      short labelb

rbx值肯定为0,所以肯定会跳转到lableb。把无效部分nop掉。

image-20241127161133968

输入52位的v7,然后分成13组传给s2n函数。比较每一组的465次方mod3162244531与c是否相等。

进行了RSA加密运算。使用yafu进行分解质因数。

factor(3162244531)

代码如下

c =[1966878405,2375075638,  2166893744,  2129446000,  2488145363,  746243878,  1904115824,  818668601,  2983811740,  1840670651,  306202172,  2009857636,  299417177]
e = 465
q = 56369
p = 56099
n = p*q
phi = (p-1)*(q-1)
d = pow(e,-1,phi)
flag = ''
for i in c:
    flag+=(pow(i,d,n).to_bytes(4,'big').decode('utf-8'))
print(flag)

注意to_bytes可以把数字转成字符,然后decode把bytes转成str格式。方便拼接flag。

ezrust

拖入IDA分析,一开始有很多变量无法识别,需要F5多刷新几下。

HBCTF 部分题目

ezre

sm4加密算法。找一个解密脚本

Newstar 2024

PangBai 泰拉记(1)

反调试,关键在于result

image-20241117160022419

当检测到调试时,result的值变成1,执行if语句中的内容。把key的值改变。从而下一步产生错误flag

应该手动让它执行else语句,从而获得正确key值。拿到正确flag。

011vm

反编译之后很复杂,直接看题解了。

取啥名好呢?

根据题目提示,推测应该是程序转移到异常处理程序image-20241119212147860

发现疑似数据的东西

SMc_math

关键在于encrypt函数被加密了,运行时才会被解密。

可以打一个断点image-20241120111900633

在解密完成时打断点然后恢复函数内容。

image-20241120112014955

重新分析代码

image-20241120112133121

_BOOL8 __fastcall encrypt(unsigned int *a1)
{
  __int64 v2; // [rsp+10h] [rbp-38h]
  __int64 v3; // [rsp+18h] [rbp-30h]
  __int64 v4; // [rsp+20h] [rbp-28h]
  __int64 v5; // [rsp+28h] [rbp-20h]
  __int64 v6; // [rsp+30h] [rbp-18h]
  __int64 v7; // [rsp+38h] [rbp-10h]
  __int64 v8; // [rsp+40h] [rbp-8h]

  v2 = *a1;
  v3 = a1[1];
  v4 = a1[2];
  v5 = a1[3];
  v6 = a1[4];
  v7 = a1[5];
  v8 = a1[6];
  return 5 * (v3 + v2) + 4 * v4 + 6 * v5 + v6 + 9 * v8 + 2 * v7 == 0xD5CC7D4FFLL
      && 4 * v8 + 3 * v5 + 6 * v4 + 10 * v3 + 9 * v2 + 9 * v7 + 3 * v6 == 0x102335844BLL
      && 9 * v6 + 4 * (v5 + v4) + 5 * v3 + 4 * v2 + 3 * v8 + 10 * v7 == 0xD55AEABB9LL
      && 9 * v3 + 5 * v2 + 9 * v8 + 2 * (v4 + 2 * v5 + 5 * v6 + v7) == 0xF89F6B7FALL
      && 5 * v6 + 9 * v5 + 7 * v2 + 2 * v3 + v4 + 3 * v8 + 9 * v7 == 0xD5230B80BLL
      && 8 * v8 + 6 * v5 + 10 * v4 + 5 * v3 + 6 * v2 + 3 * v7 + 9 * v6 == 0x11E28ED873LL
      && v2 + 4 * (v4 + v3 + 2 * v5) + 9 * v6 + v7 + 3 * v8 == 0xB353C03E1LL;
}

得到这个,需要解方程。用

ezre1