内容纲要

2103校赛

equation

脚本必须学学!tql

import re 
with open("c.py", "w") as f2:
    f2.write("from z3 import *\n\n")
for i in range(26):
    f2.write("x{} = Int(\"x{}\")\n".format(i, i))

f2.write("\n")
f2.write("s = Solver()\n\n")
with open("./my.c", "r") as f:
    s = f.readlines()
    for i in range(26):
        ss = s[33+i*24:57+i*24]
        li = []
        for k in ss:
            li += re.findall(r"\b[xabcdef\d]+", k)
            st = "s.add("
            for j in range(25):
                st += li[j]
                st += "*"
                st += "x{}".format(j)
                st += "+"
            st += li[25]
            st += "*x25 == "
            st += li[26]
            st += ")\n"
            f2.write(st)

    f2.write("\ns.check()\nm = s.model()\nresult = []\n")
    for i in range(26):
        f2.write("result.append(m[x{}].as_long())\n".format(i))
    f2.write("\nprint(bytes(result[::-1]))")

dante——rand、srand随机种子生成

这题输在随机种子生成的知识上,当时做的时候疯狂nop和jmp,就差跳到天外去了

ida分析出来的:

  v12[0] = 0xF2B86C642D330F33LL;
  v12[1] = 0x641CE77341A745CBLL;
  v12[2] = 0xA7158B7691886FFDLL;
  v12[3] = 0xA1D37ED6B253767CLL;
  v13 = 0xDEAA;
  v14 = 0xC4;
fflush(stdout);
  v5 = 0x3111A;
  v9 = time(0LL);
  do
  {
    for ( j = 0; j <= 34; ++j )
    {
      v10 = time(0LL);
      srand(v10 + 0xDEADBEE - v9);
      v11[j] ^= rand();
      puts((&off_4080)[j]);
      sleep(1u);
    }
    --v5;
  }
  while ( v5 );

rand种子相同则结果相同
srand初始由于sleep了1s所以会不断加1,根此可以模拟出rand()生成的数

#include <stdio.h> 
#include <stdlib.h>
#define RAND_INIT 0xdeadbee 
#define POEM_LEN 35 
#define LOOP_TIME 0x3111a 
int main() {
    unsigned char keys[POEM_LEN] = {0xd8, 0x87, 0x99, 0xe0, 0xee, 0x93, 0xb9, 0x7b, 0xef, 0x12, 0x8d, 0xee, 0x5b, 0xb2, 0xa4, 0xd6, 0x5b, 0x79, 0x04, 0x67, 0xbe, 0xf4, 0xf5, 0xf5, 0xbd, 0xc3, 0xdd, 0x88, 0x6d, 0x55, 0x3a, 0xd3, 0x14, 0xb1, 0xfe};
    unsigned char v12[POEM_LEN]={0x33,0x0f,0x33,0x2d,0x64,0x6c,0xb8,0xf2,0xcb,0x45,0xa7,0x41,0x73,0xe7,0x1c,0x64,0xfd,0x6f,0x88,0x91,0x76,0x8b,0x15,0xa7,0x7c,0x76,0x53,0xb2,0xd6,0x7e,0xd3,0xa1,0xaa,0xde,0xc4};
    long counter = 0; 
    int n = LOOP_TIME; 
    do {
        for (int i = 0; i < POEM_LEN; i++) {
            srand(counter + RAND_INIT); 
            counter++; 
            keys[i] ^= rand();
        }
        n--; 
    }while(n);
    for (int i = 0; i < POEM_LEN; i++) {
        printf("%02x", keys[i]);
    }
    printf("\n");
    for (int i = 0; i< POEM_LEN; i++) {
         printf("0x%02x, ", keys[i] ^ v12[i]); 
    }
    printf("\n"); 
}
//这里要注意的是v12值的顺序……这里x86是小端序,实际上是v12[k]从右往左

骏哥还提供了一种方法,是通过
LD_PRELOAD 环境变量,把原来的sleep和time函数给hook掉。

static int t = 0;
void sleep(int sec) {
    t += sec;
}

int time() {
    return t;
}
gcc --shared solver.c -o solver.so
LD_PRELOAD=./solver.so ./dante

//这方法只能说骏哥tql orz

LD_PRELOAD

让我来看看这个牛逼兮兮的玩意是怎么用的

LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。
https://blog.csdn.net/chen_jianjian/article/details/80627693

真是厉害可恶

simd

simd知识

_mm_xor_si128(a,b):将a和b进行按位异或,即r=a^b。
_mm_adds_epi8(a,b):a和b中对应的8bits数相
_mm_shuffle_epi8(a,b):
https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/bb531427(v=vs.90)

debugme

反调我永远的痛,是时候深入学习一下了

插件 scylla hide
在x64dbg上调的,调的我莫名其妙,突然就断点在了正确位置,然后dump出了完整的main函数,晕了
file

CnHongKe_WP

Crackme——Android

拖入jd

image-20210425123603144

image-20210425123626316

用户名是test

ascii前五位为11610和0x6b016异或

simNumber卡了半天,查了联通移动电信都不对

image-20210425123832465

最后问了群主……89012和916753异或

第三部分就是916753

得到CnHongKe{433484-26655-916753}

to0——Android

image-20210425124026359

拖入ida

image-20210425124133785

image-20210425124819876

最后一位没动还是0……好吧

oj

oj_软件破解1——OD找api

ida打开,函数太多根本找不到

软件打开是个窗口

image-20210423175849387

说明有调用API createwindowW啥啥的

od打开,搜索api找到

image-20210423175950847

image-20210423180031060

断点往下找

这里很可疑

屏幕截图(16)

查看数据

image-20210423180149190

异或:[0x28,0x57,0x64,0x6b,0x93,0x8f,0x65,0x51,0xe3,0x53,0xe4,0x4e,0x1a,0xff]

往下走,发现各种cmp和jnz,得到运算后的数组[0x1b,0x1c,0x17,0x46,0xf4,0xfd,0x20,0x30,0xb7,0x0c,0x8e,0x7e,0x78,0xde]

image-20210423180457330

image-20210423180916524

image-20210423181006867

总体来说还算简单,主要还是od的使用

软件破解2——未完成

动调找到未被解释的指令,然后去花

正好出在了一直卡着的进程改写,准备等看完书来解决

https://blog.csdn.net/whklhhhh/article/details/78687357

https://blog.csdn.net/getsum/article/details/87616656

这里直接通过汇编看出了异或和自增,我明明看到这里却没发现……

FindPass——未完成

CrackMe2——Csharp

image-20210426180553856

C#写的,拖入dnspy32,乱七八糟的,只能调试看看

image-20210426181309514

从Wm@@点进去看看

image-20210426181106135

看到了一个加密的地方,一个是cryptoTransform(=RijndaelManaged),还有一个base64

image-20210426181036980

image-20210426181130481

对A_0进行AES加密(key是bytes)得到array,再对array进行base64

搜索\u202B\u202E\u200F\u200B\u202D\u202D\u200B\u206B\u200C\u202E\u206A\u206C\u206C\u200F\u200B\u200B\u206C\u200C\u202E\u202B\u206F\u202C\u202A\u206C\u202B\u200C\u200E\u202D\u206F\u206E\u200B\u202B\u202C\u202B\u206A\u206D\u200D\u206E\u200D\u206E\u202E

image-20210426183234909

点进去text的200E

image-20210426183317437

text是我们的输入

image-20210426185037996

bytes是pctf2016……

image-20210426185210282

image-20210426185309626

text2是运算结果

image-20210426185600675

image-20210426185657688

result=array2

"x/nzolo0TTIyrEISd4AP1spCzlhSWJXeNbY81SjPgmk="

image-20210426190229933

#python脚本解密
from base64 import b64decode
from Crypto.Cipher import AES

key = b'pctf2016pctf2016pctf2016pctf2016'
cipher = 'x/nzolo0TTIyrEISd4AP1spCzlhSWJXeNbY81SjPgmk='
ciphDec = b64decode(cipher)
aes = AES.new(key, AES.MODE_ECB)
print(aes.decrypt(ciphDec))

Evil.exe——手动脱壳

动调,找到pushad,然后在栈上下硬件访问断点,f9,到popad,接着走,如果发生跳转,一般就是oep

image-20210427175841429

已经快到oep了,经过0x43dc3b发生了跳转(这里为什么是这样我也不清楚……)

找到了oep,然后dump

image-20210427184830313

拖入ida有东西了

image-20210427184926758

查看winmain函数

image-20210429205853825

sub_4018F0函数

image-20210429210740049

这里主要是要知道api

下载一张图片,得到图片的字节(放到(block))。v2是接收的字节数赋给函数参数也就是size,同时返回存储图片数据的缓冲区的首地址block。

即:

image-20210503003821228

Block是接收图片字节的首地址,Size改变为接收的字节数

FILE *fp,*dump;
    fp =fopen("x.jpg","rb");
    dump = fopen("shell.txt","wb");

    fseek(fp,0,SEEK_END);
    int file_len = ftell(fp);//文件的长度
    fseek(fp,0,SEEK_SET);
    cout<

sub_401740

image-20210429212945316

初始化一个byte数组 v6[256+8]

这里的汇编

image-20210502235850544

得到a2是0x4A8754F5745174

//v4类型是char只能存低8位,所以v4[8]={0x74,0x51,0x74,0xf5,0x54,0x87,0x4a,0x00}
int v4[8]={0x74,0x51,0x74,0xf5,0x54,0x87,0x4a,0x00};
//(256+8)个空间,同时前8位为0,所以要赋0
unsigned char v6[264]={0};
for (int i = 0;i < 256; ++i )
{
    v6[i + 8] = i + v4[i % 8];
}

sub_401800

image-20210429213505765

一个算法,得到与文件等长一段数据存在缓冲区buffer

sub_401220

image-20210429213608042

对上述得到的缓冲区的数据进行一个简单的异或(^i)

逆向代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    FILE *fp,*dump;
    fp =fopen("x.jpg","rb");
    dump = fopen("shell.txt","wb");

    fseek(fp,0,SEEK_END);
    int file_len = ftell(fp);//文件的长度
    fseek(fp,0,SEEK_SET);
    cout<<file_len<<endl;
    char file[file_len];
    fread(file,file_len,1,fp);
   fclose(fp);
    unsigned char key[264];
    long long first = 0x4A8754F5745174;

    for(int i=0;i<8;i++)
    {
        key[i] = 0;
    }
    for(int i=0;i<256;i++)
        key[i+8] = ((i + (first >>(i % 8 * 8)))&0xff);//运算符的优先级
    int len = 264;
    int dkey[len/4];

    int temp;
    unsigned char temp4,temp5;

    for(int i=0;i<len/4;i++)
    {
        temp = 0;
        for(int j=3;j>-1;j--)
        {
            temp = (temp<<8) + key[4 * i + j];
        }
        dkey[i] = temp;
    }
    char flag[file_len];

    for(int i=0;i<file_len;i++)
    {
        dkey[0] = (dkey[0]+ 1)%256;
        dkey[1] = (key[dkey[0] + 8] + dkey[1])%256;
        temp4 = key[dkey[0] + 8];
        temp5 = key[dkey[1] + 8];
        key[dkey[0] + 8] = temp5;
        key[dkey[1] + 8] = temp4;
       flag[i] = (key[(temp4 + temp5 )%256 + 8] ^ file[i] ^ i)%256;
       fputc(flag[i],dump);
    }
    fclose(dump);
    return 0;
}

得到image-20210502232049547

再看看十六进制文件

image-20210502232127787

68在汇编里是push

复制到od

image-20210502232247339

image-20210502232334065

image-20210502232353474

image-20210502233527803

每四个提取,栈是后入先出,所以要从后面开始

得到DDCTF-cc49badacb1c4d69bd4ae7173d9e9ade@didichuxing.com

或者

    for(int i=file_len-1;i>=4;i --)
    {
        if(flag[i]==0x68)
        {
            printf("%c%c%c%c",flag[i-4],flag[i-3],flag[i-2],flag[i-1]);
            i -= 4;
        }
    }
    cout<<endl;

image-20210503000604304

bulehat

Calc——wasm

i32_load形式的函数为取值函数,第一个参数为取值对象,第二个参数为偏移
i32_store形式的函数为操作存储函数,第一个参数为存储对象,第二个参数为存储位置,第三个参数为操作方式

image-20210505011601493

在calc.html的check_input下断点,动调后发现,会调用calc.js的ccall函数

image-20210505012152283

image-20210505012232018

接着往下走

image-20210505012542330

……醉了 真是要了解各个语言……这里竟然是调用了wasm里的h函数,js竟然可以这么写吗……(还有ident的不含”_“前缀也是蛮骚的……)

一路动调

image-20210505020618975

image-20210505021040880

image-20210505021205511

image-20210505021530005

image-20210505021732494

第二次输出

image-20210505020629448

image-20210505020700588

image-20210505023115957

发现两次输出都调用了同一个函数,也就是func29是用来输出字符的

鉴于我之前在wasm转c里看到了一堆十六进制数组,所以去搜了一下

image-20210505023553406

image-20210505023824155

看一下这都是啥

image-20210505024726992

这个开头总感觉很重要

再来找找调用的地方

image-20210505032159619

ida里找initmemory

image-20210505032147776

image-20210505032307841

这样看得更清楚

由于29前的45有点可疑,去ida看,这根本看不出来啥(wp说动调看出是判断结果……我直接???orz)

45动调下来发现var2的值在过程中会变(唯一会变的)

(然后就是wp的骚操作了……用cheat engine来看数值……orz)

经过多个数值的比较得到是0x80进制……

image-20210505035627747

image-20210505035638861

————————————————————————————————————————————

附上牛逼的wp……

2a089fc8962b6670

697f92f3cd91abe9

buu

[BJDCTF2020]easy1

这题还是蛮简单的

有两种方法

静调

image-20210505144951876

image-20210505145036069

image-20210505145052332

这里的数组汇编

image-20210505145130222

所以v3和v4根本就是一家= =,把数组[0x224fc7ffa7e31,0x22a84884a4239 , 0x3ff87084ff235 , 0x2318588424233 , 0x231fc7e4243f1] 里的每个元素先转换为二进制,转换的过程中遇到1就输出*,遇到0就输出一个空格。

动调

在main下个断点,然后动调,修改eip值到ques函数的首地址

image-20210505144827459

然后就可以得到flag

image-20210505144700003

攻防世界

easyre153——手动脱壳upx

拖入ida

image-20210509025122993

sub_8048294函数;

image-20210509025147285

看到了int 80,get pid

image-20210509025811048

汇编

image-20210509032815608

image-20210509032414686

ebx:

从第一个int 80得到eax=[pid]

将eax赋给ebp+var78

ebp+var4c=/proc/

将eax=[pid]入栈(var46),同时赋给ebp+var68,得到/proc/[pid]

加/,得到/proc/[pid]/

ecx->68->eax->46,exe赋给ecx,得到/proc/[pid]/exe

ebx=/proc/[pid]/exe的地址

这里和我之前学栈溢出的知识点串起来了

image-20210509032531249

系统调用号

而proc/[pid]/exe是程序进程的绝对地址

开始动调

image-20210513080505792

去查找字符串

image-20210513075322296

去目录下看看

image-20210513075421352

image-20210513081343991

然后这个时候的fd文件里只有012

接着F8

image-20210513081457319

image-20210513081532247

当执行完两个循环后,发现

image-20210513081606739

多了一个AAAAAA的文件,而且权限为写+执行

一直动调会发现经过一次判断跳到

image-20210513083134494

查看上一步

image-20210513083215662

esi+4

image-20210513083230951

猜测是运行upx壳

所以修改eip进入该判断内,判断的是v38==! (一个!和三个空格)且off==0

image-20210513083520196

执行if(sys_close*2)先关闭两个文件

image-20210513084021170

执行sys_open(file)打开file也就是之前创建的AAAA

image-20210513084141152

可以看到这个时候AAAA的权限变成r+x,而这时会发现我们的动调文件里有,复制出来

image-20210513085507985

接着执行到unlink

image-20210513084850750

同时AAAA也不在动调文件了

运行之前复制的文件

image-20210513085705339

用ida打开

image-20210513085813396

int __cdecl lol(_BYTE *a1)
{
  char v2; // [esp+15h] [ebp-13h]
  char v3; // [esp+16h] [ebp-12h]
  char v4; // [esp+17h] [ebp-11h]
  char v5; // [esp+18h] [ebp-10h]
  char v6; // [esp+19h] [ebp-Fh]
  char v7; // [esp+1Ah] [ebp-Eh]
  char v8; // [esp+1Bh] [ebp-Dh]

  v2 = 2 * a1[1];
  v3 = a1[4] + a1[5];
  v4 = a1[8] + a1[9];
  v5 = 2 * a1[12];
  v6 = a1[18] + a1[17];
  v7 = a1[10] + a1[21];
  v8 = a1[9] + a1[25];
  return printf("flag_is_not_here");
}
//RCTF{rhelheg}

写脚本,得到flag

附上大佬的wp

anxun2020

debugging——调试

之前一直不知道怎么做,因为这几天做的题一直用到修改rip,所以突发奇想,看看能不能这道题也修改rip来动调到主函数(因为之前其实我是找到了可以断下来的点以及无法动调却调用的地方)

image-20210513142437697

先是输入34个字符赋给v4,之后v4进行相邻两位的xor,然后进入sub14001142e

一顿下来,发现如此简单是必不可能的

这是个假main函数,根本没地方调用它……

image-20210513144451147

问题就是这里将rip改成了sub1400113a7

image-20210513144732934

加上这里根本无法f7

修改rip虽然无法使它正常调试,但是还能看看函数作用的

image-20210513145209368

image-20210513145222638

进入neww函数

image-20210513145453699

v7,v8,v9和后面sub1400110fa的第一个参数都是地址,g转到地址,找到内容,发现v7是base64转换表,v8一段无规则码,v9是flag{i_am……}

动调出来发现sub1400110fa将flag{}变成了一段字符,很明显是将它按字母顺序重排序

下面部分,没看出来也感觉不太重要,先放放

image-20210513150230508

分析下个函数

image-20210513150318923

image-20210513150357315

这里v8运算下来竟然是input:

image-20210513150729998

image-20210513150749548

image-20210513150803791

image-20210513150821482

image-20210513150834985

image-20210513150845959

image-20210513150900677

多截几幅图来表达我内心的卧槽……菜鸡第一次见到这样的隐藏函数和假函数,原来是这样实现的,学会了!

所以这个函数是print(input)

image-20210513151201076

分析这个函数

image-20210513151236831

有一个cin,cin下面经分析是blowfish

image-20210513151308138

image-20210513151334019

image-20210513151346295

可以看到有点像blowfish的sbox,pbox,但是每个的最后两位又不一样

image-20210513152036287

key=who_am_i

接着分析

image-20210513151444088

image-20210513151500299

看看byte140021150

image-20210513151627225

这特征,太像vm了

所以总体流程是

AE345B3B45CA8CEED7DD35F7D11C0704

从头分析各个函数

startAddress:

image-20210517193434973

v12通过地址的运算变成了blowfish不标准s_box的首地址,动调发现s_box变成了正宗的s_box

image-20210517193323311

image-20210517193750478

p_box同理

经此,完成了blowfish的初始盒

然后经过了输出input→输入flag→blowfish(key=who_am_i )→vm

动调vm部分

重复的部分很多,直接断点f9看rax,直到变化再步入看函数

image-20210517205606755

这里我因为是修改rip,所以我的cin_str的数据都为0,为了方便调试,我把它的数据也改了

image-20210517212229128

case1:

image-20210517212628613

image-20210517212614282

这样看得可能不是很明白,r一下,

image-20210517212726795

将输入的字符进行hexlify

case5:

将v8=3f值置零

然后会发现不停的44-3和33-3,一直调到switch(v5)

image-20210517213448681

会发现在57前都逆转了一下,逆转了32个字符串,与v8最大到3f对应

接下来会有几个循环

30,3,30,3,30,3,30,3,5f,9,d,6循环

当rax=d时,记录RDX数据

49,102,49,53,53,53,101,49,50,99,56,98,48,51,54,57

调试下来,发现经过case d发生改变

image-20210518013315931

当v8=0x350时

image-20210518013730331

步过后

image-20210518013755256

35+50=85

42+3=45

记录v8的数值

0x00c4, 0x0330, 0x024c, 0x0350, 0x0424, 0x04f8, 0x0b0c, 0x0620,0x0708, 0x0f78, 0x09a0, 0x1260, 0x09c0, 0x0b28, 0x0ca8, 0x0e40

8,6循环,edx不停数据更新

image-20210517220309262

dump出栈数据

unsigned char ida_chars[] ={

246, 50, 99, 53, 148, 55, 101, 54, 175, 55,

53, 56, 135, 104, 49, 57, 84, 57, 48, 101,

89, 59, 101, 97, 64, 111, 57, 51, 130, 108,

56, 57, 64, 104, 56, 49, 218, 63, 51, 55,

214, 57, 49, 54, 153, 66, 101, 102, 248, 64,

100, 50, 97, 60, 55, 55, 224, 61, 51, 100,

113, 113, 54, 48};

3,63循环

case63:

image-20210517221417187

image-20210517221113660

a2=rdx,与变化后的cin作比较,相等a3+1

记录出rdx数据

0x356332F6,0x36653794,0x383537AF,0x39316887,0x65303954,0x61653B59,0x33396F40,0x39386C82,0x31386840,0x37333FDA,0x363139D6,0x66654299,0x326440F8,0x37373C61,0x64333DE0,0x30367171

就是前面8—6循环的分组

走到34,发现输出了error

image-20210517221631394

case34:判断之前的a3是否等于16

流程:输入→blowfish→hexlify→逆转→加数值→比较

写脚本

from binascii import unhexlify
from Crypto.Cipher import Blowfish
import codecs

compare=[0x356332F6,0x36653794,0x383537AF,0x39316887,0x65303954,0x61653B59,0x33396F40,0x39386C82,0x31386840,0x37333FDA,0x363139D6,0x66654299,0x326440F8,0x37373C61,0x64333DE0,0x30367171]

plus=[0x00c4, 0x0330, 0x024c, 0x0350, 0x0424, 0x04f8, 0x0b0c, 0x0620,0x0708, 0x0f78, 0x09a0, 0x1260, 0x09c0, 0x0b28, 0x0ca8, 0x0e40]

for i in range(16):
    compare[i]-=plus[i]
    #print(hex(compare[i]))

result = []
for a in range(16):
    for i in range(4):
        result.append(compare[a]>>(i*8)&0xff)

result=result[::-1]

result = bytearray(result)
#print(result)
key="who_am_i"
def Decrypt(enc,key):
        key=key.encode("utf-8")
        cl = Blowfish.new(key, Blowfish.MODE_ECB)
        ciphertext = codecs.decode(enc, 'hex_codec')
        code = cl.decrypt(ciphertext)
        return code
flag = Decrypt(result, key)
print(flag)

CISCN2021

glass——Android

image-20210519014709796

调用了native-lib

libnative.so拖入ida

image-20210515220609878

image-20210515220632511

第二次变化

写脚本

from Crypto.Cipher import ARC4

data = [0xA3, 0x1A, 0xE3, 0x69, 0x2F, 0xBB, 0x1A, 0x84, 0x65, 0xC2, 
  0xAD, 0xAD, 0x9E, 0x96, 0x05, 0x02, 0x1F, 0x8E, 0x36, 0x4F, 
  0xE1, 0xEB, 0xAF, 0xF0, 0xEA, 0xC4, 0xA8, 0x2D, 0x42, 0xC7, 
  0x6E, 0x3F, 0xB0, 0xD3, 0xCC, 0x78, 0xF9, 0x98, 0x3F, 0x00]

arr = []

key = b'12345678'*32
cipher = ARC4.new(key)

for i, v in enumerate(data):
    arr.append(v ^ (0x31 + (i % 8)))

print(arr)

arr2 = [arr[i*3:i*3+3] for i in range(len(arr)//3)]

myarrrr = []

for i in arr2:
    myarr = i
    myarr[1] = myarr[1] ^ myarr[0]
    myarr[2] = myarr[2] ^ myarr[1]
    myarr[0] = myarr[0] ^ myarr[2]
    print(myarr)
    myarrrr = myarrrr + myarr

print(myarrrr)

bb = bytes(myarrrr)

print(cipher.decrypt(bb))

baby.bc——clang xx.bc→xx.out

clang baby.bc

得到的文件拖入ida

image-20210515220913481

动调下来发现fillnumber是要有两位为0

docheck

image-20210515221011056

image-20210515221024931

行列不重复,数独游戏

image-20210515221100847

image-20210515221109419

得到的数独图

IMG_0784

数独求解

14253

53142

35421

21534

42315

4,3改0

md5得到flag

gift——go nm的go

大半夜和骏哥在无wp的情况下分析出来了,过于兴奋,于是六点要起床的我在两点写起了wp

做题要从5.20的中午说起,骏哥突然想做做看gift,问我要题,然后下午他突然发现ida7.6说支持go语言,于是他下载了free7.6,打开发现,好一个清晰的玩意,然后我就被告知了

∴ ida7.6和骏哥yyds

520做题真是太有意思了:)

出来了这玩意

image-20210521015415737

出题人也是真nm皮,wtf函数确实挺wtf的,谢谢有被恶心到

main_main函数

image-20210521015729353

off_228230的值

image-20210521015758741

v16的值

image-20210521020354535

main_wtf函数

image-20210521015935144

动调下来,猜测是指数增长,至于函数是什么,不知道

先分析wtf

a1==a4-1是是否进入wtf循环的判断

这个nnn的算术式经动调发现只是不断+1,且0x10+1=0,即nnn在0-0x10之间取值,一共17个数

goooo函数是判断是否nnn变量要+1

把call wtf函数patch掉发现只会输出2222222222

main_main函数

v14经过函数传参发现肯定和wtf指数增长有关,同时数值的不断变大,也很好验证这点

byte_2239c0里面是ascii码表

动调下来发现参数为1 3 6 9 0A 0B 0C 0D 0E时对应的nnn值为2 3 2 2 6 3 4 0 2

猜测规律:(4^(i-1)+2^(i-1))%17

写脚本

data = [0x54, 0x5E, 0x52, 0x4, 0x55, 5, 0x53, 0x5F, 0x50, 7, 0x54, 0x56, 0x51, 2, 3, 0]
d = [1,3,6,9,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x14,0x19,0x1E,0x28,0x42,0x66,0x0A0, 0x936,0x3D21,0x149A7,0x243AC,
0x0CB5BE,0x47DC61,0x16C0F46,0x262C432,0x4ACE299,0x10FBC92A,0x329ECDFD,0x370D7470]
flag=''
for j in d:
    flag+=chr(data[(4**(j-1)+2**(j-1))%17]^0x66)

print(flag)
#4b445b3247c45344c54c44734445452c

津门

GOODRE——tea算法复杂代码

下断点

image-20210518125633958

image-20210518125642814

通过字符长度64的比较后,用findcrypt找到函数

image-20210518125713763

image-20210518125731727

image-20210518125751888

tea的特征值

经分析,最有可能的是这个地方进行加密

image-20210518151244323

输入64个字符,分16个一组,进行四组加密

分析函数

image-20210518151924643

第一步得到了tea的delta

image-20210518152030331

tea的关键代码

 input0+=((input1<<4)+key0)^(input1+sum)^((input1>>5)+key1);
 input1+=((input0<<4)+key2)^(input0+sum)^((input0>>5)+key3);

4和5表现得很明显

接下来就是缺加密后的数据和key[4]了

动调找值

image-20210518154218881

v11和v12应该是key

image-20210518154554995

都是0x11

v15=v15^v16

v15=v15^v17

image-20210518155519496

image-20210518155621643

从+4每隔36位比较数

0x79AE1A3B,0x596080D3,

0x80E03E80,0x846C8D73,

0x21A01CF7,0x0C7CACA32,

0x45F9AC14,0x0C5F5F22F

写脚本解密

#include<stdio.h>
#include<stdint.h>

void TEAEncrypt(uint32_t *input,uint32_t* key){
    uint32_t input0=input[0],input1=input[1];
    uint32_t sum=0;
    uint32_t delta=0x9e3779b9;//特征值
    uint32_t key0=key[0],key1=key[1],key2=key[2],key3=key[3];
    for(int i=0;i<32;i++){
        sum+=delta;
        input0+=((input1<<4)+key0)^(input1+sum)^((input1>>5)+key1);
        input1+=((input0<<4)+key2)^(input0+sum)^((input0>>5)+key3);
    }
    input[0]=input0;
    input[1]=input1;
    printf("%u %u\n",input[0],input[1]);
}

void TEADecrypt(uint32_t *input,uint32_t* key){
    uint32_t input0=input[0],input1=input[1];
    uint32_t sum=0xc6ef3720;
    uint32_t delta=0x9e3779b9;
    uint32_t key0=key[0],key1=key[1],key2=key[2],key3=key[3];
    for(int i=0;i<32;i++){
        input1-=((input0<<4)+key2)^(input0+sum)^((input0>>5)+key3);
        input0-=((input1<<4)+key0)^(input1+sum)^((input1>>5)+key1);
        sum-=delta;
    }
    input[0]=input0;
    input[1]=input1;
    printf("%x %x\n",input[0],input[1]);
}

int main(){
    uint32_t v0[2]={0x79ae1a3b,0x596080d3};
    uint32_t v1[2]={0x80e03e80,0x846c8d73};
    uint32_t v2[2]={0x21a01cf7,0xc7caca32};
    uint32_t v3[2]={0x45f9ac14,0xc5f5f22f};
    uint32_t k[4]={0x11,0x11,0x11,0x11};

    TEADecrypt(v0,k);
    TEADecrypt(v1,k);
    TEADecrypt(v2,k);
    TEADecrypt(v3,k);
}

红明谷

g0——go语言

go语言写的,go程序中需要包含main包,main包中还必须包含main函数

package main
import  "fmt"  
func main() {
  fmt.Println("Hello World!")
}

image-20210519151709198

函数不多

image-20210519164145028

输入abcdefghijklmnopqrst经过change_queue函数后变成了fghijpqrstklmnoabcde

change_queue调用了两次

image-20210519162033681

image-20210519162044131

12Nrst6CDquvG7BefghJKLMEFHPQZabRSTUVmyzno89ApwxWXYcdkij345

猜测乱序的base58索引表

image-20210519161836338

取前28位,2GVdudkYo2CBXoQii7gfpkjTc4gT,猜测为比较的字符串

写脚本

import base58
b58_table='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
table='12Nrst6CDquvG7BefghJKLMEFHPQZabRSTUVmyzno89ApwxWXYcdkij345'
compare='2GVdudkYo2CBXoQii7gfpkjTc4gT'
list=''
cin='abcdefghijklmnopqrst'
cin_new='fghijpqrstklmnoabcde'
flag=''
flag1=''
for i in range(len(compare)):
    list+=b58_table[table.index(compare[i])]

flag_old=base58.b58decode(list.encode())
flag_old=flag_old.decode("utf-8")
print(flag_old)
for i in range(len(flag_old)):
    flag1+=flag_old[cin.index(cin_new[i])]

for i in range(len(flag_old)):
    flag+=flag1[cin.index(cin_new[i])]

print(flag)

BJDCTF2020——Unity

如果存在/Data/Managed,基本可以确定是unity。

程序C#编写的侏罗纪模块代码存储于Assembly-CSarp.dll文件中。

文件夹

image-20210524021852388

拖入ILSpy

image-20210524022845024

image-20210524022852981

解密得到1001,md5加密

image-20210524022915537

大写32位加密,取前二十位