BUUCTF-Reverse —— 第二页的题目集合

news/2024/5/19 22:48:07 标签: CTF

CTF2020hello_world_go_0">[MRCTF2020]hello_world_go

用go语言编写的程序,go语言编写的程序用的是静态链接的方法所以程序都很大,而且反汇编后的伪代码也很麻烦。

因为是elf文件,动态调试elf文件的话,可以用IDA连linux虚拟机,也可以使用gdb动调。

动态调试elf文件的几种方法-腾讯云开发者社区-腾讯云 (tencent.com)


先简单来说一下这个题目,F5反汇编之后是一大堆代码,但比较明显的是runtime_cmpstring这个函数,而unk_4D3C58里面就存放的是flag。对,这就完事了。

接下来使用IDA和GDB来动态调试,在第61行(”if ( v9 != 24 )“)下断点,地址是0x49A400。运行程序,然后内存搜素flag。命令如下:

gdb hello_world_go //调试程序
b *0x49a400 //在0x49a400处下断点
r //运行程序
find "flag{" //内存搜索字符串

请添加图片描述

相册

发现了一个更好用的android反汇编工具-jadx,相比其他工具可以搜索整个apk中的字符。

根据提示,应该是要搜索mail。

请添加图片描述

发现可疑的函数sendMailByJavaMail,进入查看。

请添加图片描述

根据函数名,这应该是个发邮件的函数。主要分析一下这个参数,第一个应该是邮件要发往的邮箱,第二个是邮件标题,第三个是邮件内容。因为我们要找的是邮箱,所以主要是跟踪第一个参数。

右键函数名,查找用例。

请添加图片描述

跟进查看。

请添加图片描述

继续跟踪第一个参数,即C2.MAILSERVER

请添加图片描述

是个base64解密,继续跟踪参数,即NativeMethod.m()

坏了,是个类声明,而且是空的。

请添加图片描述

查阅他人博客才明白java中native关键字的含义。

简短介绍一下java中native方法。native方法就是一个java调用非java代码的接口,由非java代码实现功能。由于java的特性,有些层次的任务并不适合用java来实现,这时候就可以交给其他语言实现。而这类代码会生成静态连接库(.lib文件和.a文件)或动态链接库(.so文件和.dll文件)供java使用,不过一般用的都是动态链接库(因为静态链接库更大且编译时会加载到Java代码中)。

什么是Native方法 - 简书 (jianshu.com)

因此我们用IDA打开apk寻找这类文件,选择libcore.so,搜索base64字符串,如下:

请添加图片描述

第二个就是了。

CTF2020level3_75">[WUSTCTF2020]level3

请添加图片描述

看似是标准的base64加密,但实际上下面这个字符串并不能解密出来。而且也提示了不是标准的base64加密,那变表操作在哪呢?

方法一

我这里尝试的是用IDA和GDB来动态调试这个程序。对0x400bc0处下断点,然后运行程序,多输入几次肯定能进入到这个地方的(其实我想nop掉这个条件跳转指令,但是gdb似乎没有这个指令,其实也可以用其他软件nop)。

为什么在0x400bc0处下断点呢?因为此时已经完成了base64加密,那么base64对照表肯定发生了变化,这时候根据表的地址就可以在内存中找到变表。

请添加图片描述

请添加图片描述

请添加图片描述

方法二

查看base64_table的交叉引用,

请添加图片描述

发现还有其他函数使用了这个字符串,很可能就是变表操作。跟进查看,果真是。

请添加图片描述

[FlareOn4]IgniteMe

进入sub_401050函数,分析结果如下:

请添加图片描述

其中的sub_401000中的_ROL4_(x,n)是将x循环左移n位。

byte_403000 = '0D 26 49 45 2A 17 78 44 2B 6C 5D 5E 45 12 2F 17 2B 44 6F 6E 56 09 5F 45 47 73 26 0A 0D 13 17 48 42 01 40 4D 0C 02 69'.split(' ')
byte_403000 = [int(i, 16) for i in byte_403000]
v4 = 4
byte_403000[-1] ^= v4
print(byte_403000)
print(len(byte_403000))
for i in range(len(byte_403000)-1, 0, -1):#老是忘记加-1
    byte_403000[i-1] ^= byte_403000[i]

print(''.join(chr(i) for i in byte_403000))

CTF_2019xxor_131">[GWCTF 2019]xxor

关键代码。

请添加图片描述

输入

需要注意的是v6_int64类型,而每次输入是32bit的数据,那么实际上v6数组只有前3个赋了值,每个元素存储两次输入。

加密

接着看加密的for循环。dword_601078 = v6[j]实际上是将v6[i]的低32bit给dword_601078 。当然,伪代码中是看不出dword_601078是多少位的,但是可以从汇编代码中看。

请添加图片描述

可以看出是使用寄存器eaxdword_601078 赋值的,而且只赋值了一次,所以dword_601078是32bit。根据数据在内存中以小端模式存储,故dword_601078 存储的是低32bit,即截断。同理可知道dword_60107C也是32bit,存储高32bit。

然后是sub_400686函数,对前两个数据进行加密,并按相同位置赋给v7[j],同样也只有前3个有值。

最后是sub_400770,里面给出了v7的数据。同样也需要注意的是v7是_int64,而传入的参数是int*,里面的a1[i]是按4字节取的。

sub_400686函数分析

第一个参数虽然传入的是dword_601078的地址,但我们要注意,dword_601078dword_60107C在内存中是邻近的,这一点很重要!

请添加图片描述

第二参数dword_601060是一个数组,值为{2,2,3,4}。

请添加图片描述

跟进到函数里面进行分析。

请添加图片描述

因为a1存储的是dword_601078的地址,a1[1]就指向了dword_60107C。然后就是加密过程。

解密

通过sub_400770给出的数据求出加密后的数,然后每两个一组进行解密。输出的时候需要注意对每个数据从后往前转成字符。

#include <stdio.h>

int main(){
	
	unsigned int v7[6] = {3746099070, 550153460, 3774025685, 1548802262, 2652626477, 2230518816};
	
	unsigned int a2[] = {2,2,3,4};
	unsigned int v6[6] = {0};
	
	for(int i=0;i<6;i+=2){
		
		unsigned int v3 = v7[i];
		unsigned int v4 = v7[i+1];
		int v5 = 0x458BCD42*64;
		
		for (int j = 0; j < 64; ++j ){
			v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
		    v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + a2[0]) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
			v5 -= 0x458BCD42;
	  	}
	  	
	  	v6[i] = v3;
	  	v6[i+1] = v4;
	  	
	}
	
	//数据小端存储,每一个int:4字节,转成4个字符,顺序是反过来的 
	//然而实际上以十六进制打印出来只有3字节有效,最高字节为0 
//	for(int i=0;i<6;i++) {
//		printf("%x ",v6[i]); 
//	}
	
	for(int i=0;i<6;i++) {
		printf("%c%c%c",*((char*)&v6[i]+2),*((char*)&v6[i]+1),*((char*)&v6[i]+0)); 
	}
	
	return 0;
	
}

CTF2020Cr0ssfun_220">[WUSTCTF2020]Cr0ssfun

套娃判断,注意下标。

[FlareOn6]Overlong

请添加图片描述

unk_402008变量存储的是一个很长的字符串。

进入到sub_401160函数,分析如下

请添加图片描述

也就是说这个text文本最长也就是28了。

进入到sub_401000函数中。大致过程就是根据a2的值进行选择,生成相应的字符给text,然后返回的值v3a2相加,指向距离当前第v3个字符。

整个分析过程如下,我们先执行一下程序。

在这里插入图片描述

很显然,它并没有打印完,而且长度为27或28(有没有空格就不知道了)。

所以目标就很清晰了,就是要让它打印出完整的字符串。代码如下:

#include <stdio.h>

int sub_401000(char* a1,char* a2){
	
	int v3; 
  	char v4;
	
	if ( (int)(unsigned __int8)*a2 >> 3 == 30 ){
		v4 = a2[3] & 0x3F | ((a2[2] & 0x3F) << 6);
		v3 = 4;
	}
	else if ( (int)(unsigned __int8)*a2 >> 4 == 14 ){
		v4 = a2[2] & 0x3F | ((a2[1] & 0x3F) << 6);
		v3 = 3;
	}
	else if ( (int)(unsigned __int8)*a2 >> 5 == 6 ){
		v4 = a2[1] & 0x3F | ((*a2 & 0x1F) << 6);
		v3 = 2;
	}
	else{
		v4 = *a2;
		v3 = 1;
	}
	
	printf("%c",v4);
	
	*a1 = v4;
	return v3;
	
}


int main(){
	
	char text[250]={0};
	unsigned char unk_402008[] ={
		0xE0, 0x81, 0x89, 0xC0, 0xA0, 0xC1, 0xAE, 0xE0, 0x81, 0xA5, 
		0xC1, 0xB6, 0xF0, 0x80, 0x81, 0xA5, 0xE0, 0x81, 0xB2, 0xF0, 
		0x80, 0x80, 0xA0, 0xE0, 0x81, 0xA2, 0x72, 0x6F, 0xC1, 0xAB, 
		0x65, 0xE0, 0x80, 0xA0, 0xE0, 0x81, 0xB4, 0xE0, 0x81, 0xA8, 
		0xC1, 0xA5, 0x20, 0xC1, 0xA5, 0xE0, 0x81, 0xAE, 0x63, 0xC1, 
		0xAF, 0xE0, 0x81, 0xA4, 0xF0, 0x80, 0x81, 0xA9, 0x6E, 0xC1, 
		0xA7, 0xC0, 0xBA, 0x20, 0x49, 0xF0, 0x80, 0x81, 0x9F, 0xC1, 
		0xA1, 0xC1, 0x9F, 0xC1, 0x8D, 0xE0, 0x81, 0x9F, 0xC1, 0xB4, 
		0xF0, 0x80, 0x81, 0x9F, 0xF0, 0x80, 0x81, 0xA8, 0xC1, 0x9F, 
		0xF0, 0x80, 0x81, 0xA5, 0xE0, 0x81, 0x9F, 0xC1, 0xA5, 0xE0, 
		0x81, 0x9F, 0xF0, 0x80, 0x81, 0xAE, 0xC1, 0x9F, 0xF0, 0x80, 
		0x81, 0x83, 0xC1, 0x9F, 0xE0, 0x81, 0xAF, 0xE0, 0x81, 0x9F, 
		0xC1, 0x84, 0x5F, 0xE0, 0x81, 0xA9, 0xF0, 0x80, 0x81, 0x9F, 
		0x6E, 0xE0, 0x81, 0x9F, 0xE0, 0x81, 0xA7, 0xE0, 0x81, 0x80, 
		0xF0, 0x80, 0x81, 0xA6, 0xF0, 0x80, 0x81, 0xAC, 0xE0, 0x81, 
		0xA1, 0xC1, 0xB2, 0xC1, 0xA5, 0xF0, 0x80, 0x80, 0xAD, 0xF0, 
		0x80, 0x81, 0xAF, 0x6E, 0xC0, 0xAE, 0xF0, 0x80, 0x81, 0xA3, 
		0x6F, 0xF0, 0x80, 0x81, 0xAD
	};
	
	char* a1 = text;
	int a2 = (int)unk_402008;
	int a3 = 28;
	for(int i=0;i<sizeof(unk_402008);i++){
		a2 += sub_401000(a1, (char*)a2);
		a1 += 1;
	}
	
	return 0;
	
}

在这里插入图片描述

[FlareOn3]Challenge1

main函数中,代码流程是接收我们输入的字符串,进行加密,然后与已知字符串比较。

在这里插入图片描述

那直接进入到加密函数sub_401260中。从v8右侧表达式可以看出大致是base64加密(典型的除3乘4),以及byte_413000是个非标准的base64对照表。综合这两个特点,可以肯定是变表base64加密。

在这里插入图片描述

CTF2020Oruga_336">[ACTF新生赛2020]Oruga

算是个脑洞题吧。

直接看sub_78A函数,

在这里插入图片描述

分析不出来是个啥。翻看他人博客才知道是个迷宫。

’w’(-16, 向上)、‘E’(+1, 向右)、‘M’(+16, 向下)、‘J’(-1, 向左)控制方向,‘!’(0x21)始终点。下面那个循环就是沿某个方向一直走到底。

256个字符,根据+16,-16,估计是16×16的迷宫。打印出迷宫地图,绘制路径就得到flag。

在这里插入图片描述

MEWEMEWJMEWJM

特殊的 BASE64

查字符,base64编码和非标准base64对照表,简单看一下程序代码,可以肯定就是变表base64了。

值得注意的是伪代码变了一种风格,但容易看出是c++写的代码。

std::string::string
std::operator<<<std::char_traits<char>>

CTF2020BJD_hamburger_competition_369">[BJDCTF2020]BJD hamburger competition

头一次遇到Untiy3D和C#的题。使用dnspy工具进行分析。

C#编写的主逻辑模块代码静态编辑之后存储于Assembly-CSharp.dll文件中。

找到这个用dnspy打开,

在这里插入图片描述

一个sha1的摘要,拿到cmd5网站上解密出1001。然后进行md5加密,不过代码中的Md5函数并不是真正的md5,需要进入该函数查看,
在这里插入图片描述

这里取前20位。

flag{B8C37E33DEFDE51CF91E}

CTF2020Universe_final_answer_390">[ACTF新生赛2020]Universe_final_answer

sub_860函数,一堆等式,用z3-solver求解就行了。

注意将左移转成乘法运算。

from z3 import *

v1, v2, v3, v4, v5, v6, v7, v8, v9, v11 = Ints('v1 v2 v3 v4 v5 v6 v7 v8 v9 v11')
solver = Solver()
solver.add(-85 * v9 + 58 * v8 + 97 * v6 + v7 + -45 * v5 + 84 * v4 + 95 * v2 - 20 * v1 + 12 * v3 == 12613)
solver.add(30 * v11 + -70 * v9 + -122 * v6 + -81 * v7 + -66 * v5 + -115 * v4 + -41 * v3 + -86 * v1 - 15 * v2 - 30 * v8 == -54400)
solver.add(-103 * v11 + 120 * v8 + 108 * v7 + 48 * v4 + -89 * v3 + 78 * v1 - 41 * v2 + 31 * v5 - v6*2**6 - 120 * v9 == -10283)
solver.add(71 * v6 + v7*2**7 + 99 * v5 + -111 * v3 + 85 * v1 + 79 * v2 - 30 * v4 - 119 * v8 + 48 * v9 - 16 * v11 == 22855)
solver.add(5 * v11 + 23 * v9 + 122 * v8 + -19 * v6 + 99 * v7 + -117 * v5 + -69 * v3 + 22 * v1 - 98 * v2 + 10 * v4 == -2944)
solver.add(-54 * v11 + -23 * v8 + -82 * v3 + -85 * v2 + 124 * v1 - 11 * v4 - 8 * v5 - 60 * v7 + 95 * v6 + 100 * v9 == -2222)
solver.add(-83 * v11 + -111 * v7 + -57 * v2 + 41 * v1 + 73 * v3 - 18 * v4 + 26 * v5 + 16 * v6 + 77 * v8 - 63 * v9 == -13258)
solver.add(81 * v11 + -48 * v9 + 66 * v8 + -104 * v6 + -121 * v7 + 95 * v5 + 85 * v4 + 60 * v3 + -85 * v2 + 80 * v1 == -1559)
solver.add(101 * v11 + -85 * v9 + 7 * v6 + 117 * v7 + -83 * v5 + -101 * v4 + 90 * v3 + -28 * v1 + 18 * v2 - v8 == 6308)
solver.add(99 * v11 + -28 * v9 + 5 * v8 + 93 * v6 + -18 * v7 + -127 * v5 + 6 * v4 + -9 * v3 + -93 * v1 + 58 * v2 == -1697)
if solver.check() == sat:
    result = solver.model()
    print(result)
else:
    print(solver.check())

a1 = [result[v2], result[v1], result[v3], result[v4], result[v5], result[v7], result[v6], result[v8], result[v9], result[v11]]

print(''.join(chr(i.as_long()) for i in a1))
#F0uRTy_7w@

因为只要sub_860函数返回True,我们就可以进入到flag输出环节,是可以不分析sub_C50这个函数的。
在这里插入图片描述

[Zer0pts2020]easy strcmp

进入到main函数中,一个strcmp函数,发现没什么特别的,但flag并不是那个字符串。

返回到_start函数。
在这里插入图片描述

查阅了_libc_start_main函数的参数,如下所示

  • main: 应用程序的主函数。
  • argc: 命令行参数数量。
  • ubp_av: 指向命令行参数字符串的指针数组。
  • init: 构造函数会在 main() 函数之前被调用。
  • fini: 析构函数在 main() 函数退出之前被调用。
  • rtld_fini: 在卸载动态库时调用的析构函数。
  • stack_end: 当前线程堆栈的结束地址。

我们可以发现,在调用 main() 函数之前,会调用构造函数init。我们进入到这个函数,如下

在这里插入图片描述

这个函数的主要功能是调用0x200DE00x200DF0之间的函数。

在这里插入图片描述

跟进这些函数,发现sub_795函数比较可疑。他将strcmp函数的地址赋值给了qword_201090,然后将sub_6EA函数地址赋值给了off_201028,而off_201028之前存储的是strcmp函数的地址。

其实这就是篡改了strcmp函数的地址,这会使得执行call strcmp时,调用的函数并非strcmp函数,而是sub_6EA函数。

因为函数名只是一个符号,并不是函数的地址,函数地址会存储在一个变量中。当函数被调用时,会通过该变量找到函数地址。

在汇编代码中,如果直接使用 strcmp 函数的名称,汇编器会将其解析为一个标签(label),而不是函数地址。因此,在需要使用 strcmp 函数的地址时,需要通过 off_201028 数据对象来计算。

我们跟踪strcmp函数的调用,发现确实如此。

在这里插入图片描述

在这里插入图片描述

那么回到之前的main函数中,调用strcmp函数实际上是调用sub_6EA函数。我们跟进到sub_6EA函数进行查看,分析如下

在这里插入图片描述

那么解密过程就是将字符串每8字节分一组,加上qword_201060[j]。**需要注意的是数据是以小端模式存储在内存中,我们需要将其反转才行(或者将字符串先反转,解密后再反转回来)。**解密代码如下

from binascii import hexlify, unhexlify

qword_201060 = [0x410A4335494A0942, 0x0B0EF2F50BE619F0, 0x4F0A3A064A35282B]
encflag = b'********CENSORED********'
flag = b''
for i in range(3):
    eachpart = encflag[i*8:(i+1)*8][::-1]
    deceachpart = int(hexlify(eachpart), 16) + qword_201060[i]
    flag += unhexlify(hex(deceachpart)[2:])[::-1]

print(flag)

CTF2020level4_497">[WUSTCTF2020]level4

init分析

进入到init函数查看。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjMpCJYh-1688436961075)([WUSTCTF2020]level4/1.png)]

for循环中,每隔24字节赋值一个字符。然后将一系列unk的地址给qword值得注意的是,这些unk之间正好是隔24字节,而这些qword正好是在每24字节的最后面,一般都有两个,记录unk的地址,我们可以猜测,这是一个结构体,并且结构体中的变量qword存储了其他结构体的地址。

type1和type2分析

__int64 __fastcall type1(char *a1)
{
  __int64 result; // rax

  if ( a1 )
  {
    type1(*((_QWORD *)a1 + 1));
    putchar(*a1);
    return type1(*((_QWORD *)a1 + 2));
  }
  return result;
}

int __fastcall type2(char *a1)
{
  int result; // eax

  if ( a1 )
  {
    type2(*((_QWORD *)a1 + 1));
    type2(*((_QWORD *)a1 + 2));
    return putchar(*a1);
  }
  return result;
}

(_QWORD *)a1 + 1指向的是结构体存储的第一个qword(_QWORD *)a1 + 2指向的是结构体存储的第二个qword,这两个作为参数进行递归。在分析init函数中,我们分析到了qword存储的是其他结构体的地址,再结合代码的书写,很有可能这个结构体是二叉树,type1是中序遍历,type2是后序遍历。那么很有可能这个flag是前序存储的,所以我们需要根据中序、后序遍历还原前序遍历。

方法一

二叉树遍历:已知前序中序输出后序/已知后序中序输出前序_前序列表

#include <iostream>
 
using namespace std;  
char post[] = "20f0Th{2tsIS_icArE}e7__w";  
char mid[] = "2f0t02T{hcsiI_SwA__r7Ee}";  

//root是后序列表中代表根节点的点的下标,start,end是中序遍历中当前处理的树的开始与结尾
void pre(int root, int start, int end) {  
    if(start > end)   
        return;  
    int i = start;  
    while(i < end && mid[i] != post[root]) //定位根在中序的位置
		i++;  
    cout << mid[i];  //访问当前处理的树的根
    //中序(左根右)end-i 则是右子树的大小
	//后序(左右根)root-1-(end-i)则是左子树的大小 
	pre(root-1-(end-i), start, i - 1);  //递归处理左子树
    pre(root-1, i + 1, end);  //递归处理右子树  
}  

int main(){  
    pre(24, 0, 24); 
    return 0;  
}  

方法二

动态调试获取二叉树结构体,然后前序遍历。

[网鼎杯 2020 青龙组]singal

关键代码为vm_operad()函数。

在这里插入图片描述

该函数的while循环首先会进入到case 10 分支,然后将我们输入的flag进行加密,存储到Str + 100之后的空间,最后会进入到case 7分支,对加密后的字符串进行对比,只有全部符合才行。

解题的关键就是v5、v6、v7、v8、v9每轮的值,知道这个才能知道加密的过程。而这些值又由a1决定,故我们可以编造一个flag拿来测试,获得每一轮的值。

#include <stdio.h>
#include <string.h>
#include <windows.h> 

unsigned char a1[] =
{
  0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 
  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 
  0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 
  0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
  0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 
  0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
  0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 
  0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
  0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 
  0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 0x00, 
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 
  0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
  0x51, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 
  0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
  0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 
  0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 
  0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
  0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x08, 0x00, 
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 
  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 
  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 
  0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
  0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
  0x41, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00, 
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 
  0x22, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 
  0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 
  0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 
  0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 
  0x33, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 
  0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0xFF, 0xFF, 
  0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 
  0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 
  0x28, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x84, 0xFF, 
  0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF, 
  0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 
  0x00, 0x00, 0x7A, 0x00, 0x00, 0x00
};

int a2 = 114;

char str[200] = {0}; 

int vm_operad(int* a1,int a2) {
	int result; // eax
	char Str[200]; // [esp+13h] [ebp-E5h] BYREF
	char v4; // [esp+DBh] [ebp-1Dh]
	int v5; // [esp+DCh] [ebp-1Ch]
	int v6; // [esp+E0h] [ebp-18h]
	int v7; // [esp+E4h] [ebp-14h]
	int v8; // [esp+E8h] [ebp-10h]
	int v9; // [esp+ECh] [ebp-Ch]
	
	v9 = 0;
	v8 = 0;
	v7 = 0;
	v6 = 0;
	v5 = 0;
	int caselist[200] = {0},casepos=0;
	while ( 1 ){
	    result = v9;
	    if ( v9 >= a2 )
	      return result;
	      
	    caselist[casepos++] = a1[v9];
	    printf("v9 = %d, v8 = %d ,v7 = %d, v6 = %d, v5 = %d\n",v9,v8,v7,v6,v5);
	    switch ( a1[v9] )
	    {
	      case 1:
	        Str[v6 + 100] = v4;
	        ++v9;
	        ++v6;
	        ++v8;
			break;
	      case 2:
	        v4 = a1[v9 + 1] + Str[v8];
	        v9 += 2;
			break;
	      case 3:
	        v4 = Str[v8] - LOBYTE(a1[v9 + 1]);
	        v9 += 2;
	        break;
	      case 4:
	        v4 = a1[v9 + 1] ^ Str[v8];
	        v9 += 2;
	        break;
	      case 5:
	        v4 = a1[v9 + 1] * Str[v8];
	        v9 += 2;
	        break;
	      case 6:
	        ++v9;
	        break;
	      case 7:
	        for(int i=0;i<casepos;i++){
				printf("%d ",caselist[i]);
			}
	        printf("\n");
			if ( Str[v7 + 100] != a1[v9 + 1] )
	        {
	          printf("what a shame...");
	          exit(0);
	        }
	        ++v7;
	        v9 += 2;
			break;
	      case 8:
	        Str[v5] = v4;
	        ++v9;
	        ++v5;
	        break;
	      case 10:
	        strcpy(str,"flag{123456789}");
	        ++v9;
	        break;
	      case 11:
	        v4 = Str[v8] - 1;
	        ++v9;
	        break;
	      case 12:
	        v4 = Str[v8] + 1;
	        ++v9;
	        break;
	      default:
	      	continue;
	    }
	}
	
}
int main(){

	vm_operad((int*)a1,a2);

	return 0;
	
}

部分结果如下:

v9 = 79, v8 = 14 ,v7 = 0, v6 = 14, v5 = 14
v9 = 81, v8 = 14 ,v7 = 0, v6 = 14, v5 = 14
v9 = 82, v8 = 14 ,v7 = 0, v6 = 14, v5 = 15
v9 = 83, v8 = 14 ,v7 = 0, v6 = 14, v5 = 15
v9 = 84, v8 = 15 ,v7 = 0, v6 = 15, v5 = 15
10 4 8 3 1 4 8 5 1 3 8 11 1 12 8 4 1 5 8 3 1 11 8 11 1 4 8 3 1 2 8 4 1 12 8 11 1 5 8 2 1 2 8 4 1 2 8 5 1 5 8 2 1 4 8 3 1 2 8 12 1 7
what a shame...

知道v9v6之后,根据case 7我们可以推出加密后的结果。

unsigned char Str100[15] = {0};

int v9 = 84,v7 = 15;

for(int i=0;i<v7;i++){
    Str100[i] = *((int*)a1+v9+1);
    v9 += 2;
}

最后我们根据case的顺序,逆这个while循环就行了,需要注意的是我们需要使用unsigned int定义变量。

unsigned char a1[] =
{
  ...
};
int a2 = 114;
unsigned char Str100[15] = {0};
int order[] = {1, 12, 8, 2, 1, 3, 8, 4, 1, 2, 8, 5, 1, 5, 8, 2, 1, 4, 8, 2, 1, 2, 8, 5, 1, 11, 8, 12, 1, 4, 8, 2, 1, 3, 8, 4, 1, 11, 8, 11, 1, 3, 8, 5, 1, 4, 8, 12, 1, 11, 8, 3, 1, 5, 8, 4, 1, 3, 8, 4,10};
int dec_vm_operad(unsigned int* a1,int a2) {
	unsigned char Str[200]; // [esp+13h] [ebp-E5h] BYREF
	unsigned char v4; // [esp+DBh] [ebp-1Dh]
	int v5; // [esp+DCh] [ebp-1Ch]
	int v6; // [esp+E0h] [ebp-18h]
	int v7; // [esp+E4h] [ebp-14h]
	int v8; // [esp+E8h] [ebp-10h]
	int v9; // [esp+ECh] [ebp-Ch]
	
	v9 = 84;
	v8 = 15;
	v7 = 0;
	v6 = 15;
	v5 = 15;
	
	char sstr[20] = {0};
	int sstrpos = 15;
	int orderpos = 0;
	while ( 1 ){
	    switch ( order[orderpos++] )
	    {
	      case 1:
	      	--v9;
	        --v6;
	        --v8;
	        v4 = Str100[v6];
	        break;
	      case 2:
	      	v9 -= 2;
	        Str[v8] = v4 - a1[v9 + 1];
	        break;
	      case 3:
	      	v9 -= 2;
	        Str[v8] = v4 + LOBYTE(a1[v9 + 1]);
	        break;
	      case 4:
	      	v9 -= 2;
	        Str[v8] = a1[v9 + 1] ^ v4;
	        break;
	      case 5:
	      	v9 -= 2;
	        Str[v8] = v4 / a1[v9 + 1];
	        break;
	      case 6:
	        --v9;
	        break;
	      case 8:
	      	--v9;
	        --v5;
	        v4 = Str[v5];
	        break;
	      case 10:
	      	--v9;
//	      	printf("v9 = %d, v8 = %d ,v7 = %d, v6 = %d, v5 = %d\n",v9,v8,v7,v6,v5);
			for(int i=0;i<15;i++){
	      		printf("%c",Str[i]);
			}
			exit(0);
	      case 11:
	      	--v9;
	        Str[v8] = v4 + 1;
	        break;
	      case 12:
	      	--v9;
	        Str[v8] = v4 - 1;
	        break;
	      default:
	      	printf("error!\n"); 
	      	continue;
	    }
	}
		
	
		
}

int main(){

//	vm_operad((int*)a1,a2);

	int v9 = 84,v7 = 15;
	
	for(int i=0;i<v7;i++){
		Str100[i] = *((int*)a1+v9+1);
		v9 += 2;
	}
	
	dec_vm_operad((unsigned int*)a1,a2);
	
	return 0;
	
}
#757515121f3d478

crackMe

在这里插入图片描述

sub841090分析

sub841090函数主要是根据输入的v11(即账号)对byte_856050进行处理,这部分可以通过运行该块代码获动态调试获取。

loc_4011A0分析

sub8411A0函数原本是loc_4011A0。该函数点进去后,本身无法反编译,问题出在下面这个地方
在这里插入图片描述

jbe跳转指令的地址是有误的,所以将该指令nop掉。从loc_4011A0标签开始,选到retn指令,按p定义为函数,F5反汇编。

将数字转成字符,进行多次后,按ctrl+z,就会自动帮你弄好,如下所示

在这里插入图片描述

sub_401830分析

在这里插入图片描述

第一个while循环分析过程在图中,就是将输入的password每两个一组。

第二个while循环主要的就是第67行的异或和第73行的sub_841710函数。

进行完这个while循环后,有个sub_841470函数,主要功能是根据v17的值更改v14的值,然后再是v14的值跟0xAB94做判断。显然,v14必须为0xAB94,于是我们可以根据v14的值推出v17。而在sub_841470函数里,都是if……else……,且都是v17[i]与常量字符作比较,所以可以直接猜测v17就是那一串常量字符。

sub_841710分析

分析过程如图
在这里插入图片描述

解密

根据v14的值反推出v17,先调用sub841090函数得到处理后的byte_856050,然后逆第二个while循环(主要是第67和第73行),得到的v15直接转成十六进制数。

#include <stdio.h>
#include <windows.h>

unsigned char byte_856050[272] = {0};

void sub_401090(BYTE *a1)
{
	BYTE *result; // eax
	int v2; // [esp+Ch] [ebp-18h]
	int v3; // [esp+10h] [ebp-14h]
	BYTE *v4; // [esp+14h] [ebp-10h]
	int i; // [esp+18h] [ebp-Ch]
	char v7; // [esp+20h] [ebp-4h]
	char v8; // [esp+22h] [ebp-2h]
	unsigned __int8 v9; // [esp+23h] [ebp-1h]
	
	for ( i = 0; i < 256; ++i )
		byte_856050[i] = i;
	
	v2 = 0;
	v9 = 0;
	v3 = 0;
	result = a1;
	v4 = a1;
	
	do
		;
	while ( *v4++ );
	while ( v2 < 256 ){
		v8 = byte_856050[v2];
		v9 += v8 + a1[v3];
		v7 = byte_856050[v9];
		++v3;
		byte_856050[v9] = v8;
		byte_856050[v2] = v7;
		result = (BYTE *)v3;
		if ( v3 >= v4 - (a1 + 1) )
			v3 = 0;
		++v2;
	}
	
}

int main(){
	
	int v14 = 0xAB94;
	char v17[] = "dbappsec";
	char user[] = "welcomebeijing";
	char v15[8] = {0} ;
	//计算出byte_416050
	sub_401090((BYTE*)user);

	int v4; // [esp+18h] [ebp-22Ch]
	signed int v5; // [esp+1Ch] [ebp-228h]
	signed int v6; // [esp+28h] [ebp-21Ch]
	unsigned int v7; // [esp+30h] [ebp-214h]
	unsigned char v8; // [esp+36h] [ebp-20Eh]
	unsigned char v9; // [esp+37h] [ebp-20Dh]
	unsigned char v10; // [esp+38h] [ebp-20Ch]
	unsigned __int8 v11; // [esp+39h] [ebp-20Bh]
	unsigned __int8 v12; // [esp+3Ah] [ebp-20Ah]
	char v13; // [esp+3Bh] [ebp-209h]
	
	v5 = 0;
	v6 = 0;
	v12 = 0;
	v11 = 0;
	v10 = 0;
	v7 = 0;
	v4 = 0;
	
	//计算出v15
	while(v6 < 8){
		
		v11 += byte_856050[++v12];
	    v13 = byte_856050[v12];
	    v8 = byte_856050[v11];
	    byte_856050[v11] = v13;
	    byte_856050[v12] = v8;
		
		//sub_401710
		v17[v6] = user[v6] ^ v17[v6];
		
		v15[v6] = byte_856050[(256 + v8 + v13)%256] ^ v17[v6];
//		printf("v8 + v13=%d, %x\n",(v8 + v13+256)%256,byte_856050[(256 + v8 + v13)%256]);
		v6++;
		
	}
	//根据v15,反推出偏移量
	for(int i=0;i<8;i++){
//		BYTE pre_v8 = HIBYTE(v15[i]);
//		BYTE now_v8 = LOBYTE(v15[i]);
		printf("%02x",(BYTE)v15[i]);
	}
	
	return 0;
	
}

这里用代码生成byte_856050 比较麻烦,因为c语言的特性,会产生负数下标。

可以利用动调得到byte_856050[v8 + v13],方法参考:

(4条消息) BUUCTF crackMe 题解___lifanxin的博客-CSDN博客

将得到的结果进行md5加密,最终我们得到的结果并不是buuctf上的答案。因为我们多做了一步——第二个while循环中的sub_841710。那问题来了,sub_841710哪里分析错了?!

CTF2019number_game_1033">[GUET-CTF2019]number_game

main函数分析如下
在这里插入图片描述

sub_400758分析

v6是一个24字节的结构体,第一个字节存储字符。根据下面两个递归调用以及函数返回值是结构体地址,可以认为这是一个二叉树。再仔细分析参数2*a2+12*(a2+1),说明字符串是以前序方式存储的。那么整个二叉树就如下图所示

在这里插入图片描述

图中数字是二叉树存储的值在字符串中的下标。

sub_400807分析

将二叉树的中序遍历的结果赋值给v7,那么对应的结果(值对应的下标的中序遍历)为

7381940526

sub_400881分析

虽然是伪代码很简单,但是我们还是要仔细分析一下。对于每个byte_XXXX,它们存储的都是0x23(#),而对于byte_XXXX所在的那块内存中(大小为25字节),其他位置是有数据的,且值都在[0,4]

sub_400917分析

在这里插入图片描述

这一块初看是比较难懂的,查阅他人博客才知道是数独问题。那接下来就带着答案分析一下是不是这样的。

unk_601060正是那块25字节的空间。根据5*i等,猜测应该是将25个字节分成5×5的矩阵。两个if语句简化一下

unk_601060[i][j] != unk_601060[i][k]//同一行中,任意两个值不能相等
unk_601060[j][i] != unk_601060[k][i]//同一列中,任意两个值不能相等

看来果真是数独问题。

解密

根据数独问题,我们可以推出flag的中序遍历的结果。

1 4 # 2 3
3 0 # 1 #
0 # 2 3 #
# 3 # # 0
4 2 # # 1
=>
0421421430

在sub_400807的分析中,我们已经知道了值对应下标的中序遍历的结果

7381940526

那么只要将其恢复成正常顺序即可

字符排列:0421421430
下标排列:7381940526
最终结果:1134240024

[羊城杯 2020]easyre

main函数分析如图所示

在这里插入图片描述

大致就是将flag进行三次加密,然后与常量字符串比较。

第一个加密函数encode_one如下
在这里插入图片描述

这部分代码的意图是将字符串长度补成3的倍数,然后每6bit一组,再结合for循环中的i+=3(每3个字符一组)和alphabet是标准的base64表,可以肯定encode_one是进行base64加密。

第二个加密函数encode_two就是将v10按每13个字符分组,按照给定的顺序赋值给v9

第三个加密函数encode_three分析如图中所示

在这里插入图片描述

即字母和数字做偏移量为3的加密。

解密代码如下:

import base64

enc3 = 'EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG'
enc2 = ''
enc1 = ''
print(len(enc3))
for i in range(len(enc3)):
    if 'a' <= enc3[i] <= 'z':
        enc2 += chr((ord(enc3[i]) - 97 - 3) % 26 + 97)
    elif 'A' <= enc3[i] <= 'Z':
        enc2 += chr((ord(enc3[i]) - 65 - 3) % 26 + 65)
    elif '0' <= enc3[i] <= '9':
        enc2 += chr((ord(enc3[i]) - 48 - 3) % 10 + 48)
    else:
        enc2 += enc3[i]

print(f'enc2 = {enc2}')

enc1 += enc2[13:26]
enc1 += enc2[39:52]
enc1 += enc2[:13]
enc1 += enc2[26:39]

print(f'enc1 = {enc1}')

flag = base64.b64decode(enc1.encode())
print(flag)

findKey

在这里插入图片描述

注意0x40193D处的jmp指令,跳转的地址是指令的中间位置,涉及到花指令,而且有两个相同的push指令,把第二个nop掉。往上找到retn指令,选中指令下方的代码,一直到下一个retn,快捷键p创建函数,F5反汇编,关键代码如下

在这里插入图片描述

这个函数是窗口的消息处理函数。

整个代码的流程就是String进行md5哈希加密,Str中的每个字符与字符S异或,然后两者进行比较。最后是String循环异或v10字符串,得到的结果就是flag。

逆过程很简单,就不复述了,代码如下

Str = "0kk`d1a`55k222k2a776jbfgd`06cjjb"
md5 = ''
for i in range(len(Str)):
    md5 += chr(ord(Str[i]) ^ ord('S'))
print(md5)
#md5拿到网站上解密得到123321
v18 = '123321'
v10 = [
  0x57, 0x5E, 0x52, 0x54, 0x49, 0x5F, 0x01, 0x6D, 0x69, 0x46,
  0x02, 0x6E, 0x5F, 0x02, 0x6C, 0x57, 0x5B, 0x54, 0x4C
]

v5 = len(v18)
flag = ''
for i in range(len(v10)):
    flag += chr(v10[i] ^ ord(v18[i % v5]))
print(flag)

[网鼎杯 2020 青龙组]jocker

在这里插入图片描述

如果F5反编译失败,报sp-analysis failed,那就根据报错信息找到对应位置修正堆栈。

现在对main函数进行分析,VirtualProtect函数根据参数可知是将encrypt函数对应的内存的访问权限修改为读/写。

virtualProtect 函数 (memoryapi.h) - Win32 apps | Microsoft Learn

WinNT.h (内存保护常量) - Win32 apps | Microsoft Learn

第22~23行的两个函数比较简单,主要是将字符串根据奇偶下标进行不同加密,然后于常量字符串对比,解密代码如下

#include <stdio.h>

unsigned char v2[] =
{
  0x66, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x63, 0x00, 
  0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 
  0x61, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 
  0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 
  0x6B, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x7B, 0x00, 
  0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 
  0x50, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x5F, 0x00, 
  0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 
  0x71, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x37, 0x00, 
  0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


int main(){
	
	int Str[24] = {0};
	
	for(int i=0;i<=23;i++){
		if ( (i & 1) != 0 )
			Str[i] = *((int*)(v2)+i) + i;  
		else
			Str[i] = *((int*)(v2)+i) ^ i;
	}
	
	for(int i=0;i<=23;i++){
		printf("%c",Str[i]);
	}
	
	return 0;
	
}
//flag{fak3_alw35_sp_me!!}

然而并不是真正的flag。

继续往下看。第24行的for循环对encrypt所在的内存做异或,但我们却不能访问encrypt函数,会报40151D:cannot convert to microcode错误信息,但第26行却调用了encrypt函数。所以我们可以推测,encrypt函数所在的代码事先被加密了,然后程序运行的时候,for循环进行解密,然后就可以正常调用encrypt函数,相当于这块代码加了一层加密壳。

因此我们可以IDA动态调试,在for循环处下断点,找到encrypt处的汇编代码,恢复成伪代码。

IDA启动动态调试后,输入flag(24bytes,所以直接输入假的flag),断点起作用之后,在IDA View-EIP界面按快捷键Tab就可以得到伪代码,再按一次就可以将光标所在伪代码转成汇编代码

在这里插入图片描述

双击__Z7encryptPc进入到encrypt函数,
在这里插入图片描述

选中encrypt的所有代码(0x401500~0x401598),快捷键U将定义好的代码全部转成数据,然后快捷键C转成代码,快捷键P定义成函数,快捷键F5转成伪代码

在这里插入图片描述

对应逆过程代码如下

#include <stdio.h>

unsigned char v2[] =
{
  0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x09, 0x00, 
  0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 
  0x05, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x56, 0x00, 
  0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 
  0x0C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 
  0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 
  0x6B, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x59, 0x00, 
  0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

char Buffer[] = "hahahaha_do_you_find_me?";

int main(){
	
	int Destination[24] = {0};
	
	for(int i=0;i<=18;i++){
		Destination[i] = *((int*)(v2)+i) ^ Buffer[i];  
	}
	
	for(int i=0;i<=23;i++){
		printf("%c",Destination[i]);
	}
	
	return 0;
	
}
//flag{d07abccf8a410c

这里只算出了flag的前19位,还差5位。

接着继续finally函数,处理过程同encrypt一样,这里不再复述,得到伪代码如图

在这里插入图片描述

额。我重新设断点在图中的第12行,输入我们得到的部分flag,补充成24位,当断点起作用时,我查看了一下v3的内存,*(_DWORD *)&v3[5]的值为0,对应代码可以简化为

v3[0] != a1[0] == v4
=> 1 == v4

但是v4是随机的,所以输出完全依靠v4,而输出结果毫无作用,所以这一块代码毫无作用可言。

查阅他人博客,发现是个纯纯的脑洞题。v3作为缺失部分的flag密文,根据flag{}的格式,}:异或得到密钥,然后与密文异或得到缺失部分的flag。

int main(){
    char v3[] = "%tp&:";
	int key = v3[4] ^ '}';
	for(int i=0;i<5;i++){
		printf("%c",key ^ v3[i]);
	}
}
//b37a}

[FlareOn5]Minesweeper Championship Registration

是个压缩包,解压之后有classMF文件。

jadx工具打开class文件
在这里插入图片描述

flag已经很明显了!

CTF2020SoulLike_1357">[ACTF新生赛2020]SoulLike

在这里插入图片描述

main函数分析如上图所示。关键函数sub_83A无法进入,提示too big function,需要修改配置文件IDA\cfg\hexrays.cfg,将MAX_FUNCSIZE = 64修改成MAX_FUNCSIZE = 1024

IDA反编译失败总结ida无法反编译 寻梦&之璐的博客-CSDN博客

进入到sub_83A,发现有几千行代码,都是不断异或,最后比较。逆过程反过来就行(用excel来反转),除了代码量大以外,没啥技术可言。

firmware

头一次见固件分析的,参考了他人博客才知道怎么做。

我们要下载firmware-mod-kit工具,配置了很久,次次报错,不过终于找到了一篇好文章:2022年 firmware-mod-kit 配置方法 - Carykd - 博客园 (cnblogs.com)。

配置好之后,我们只要使用下列命令即可,可以不像网上那些博客写的那样需要那么多指令:

./extract-firmware.sh xxxx.bin

成功后,它会提示解包后的文件在firmware/fmk/*中。

在这里插入图片描述

就是这个rootfs文件了,根据题目,我们要找的是后门程序,进入文件查找,在tmp文件夹下就有个backdoor程序,拖出来分析。

使用Exeinfo PE工具,发现有个upx壳,脱壳后IDA分析。

根据题目提示是要找远程服务器和端口。

在这里插入图片描述

很明显initConnection是关键函数,进入到该函数查看。

在这里插入图片描述

明显v3是个端口,点击commServer进入查看会发现是个域名echo.byethost51.com。域名和端口都找到了,md5加密一下就行了。

CTF_2019re3_1403">[GWCTF 2019]re3

在这里插入图片描述

mportectdword_400000开始的0xF000大小的内存的权限改为可读可写可执行。

15行的for循环说明sub_402219函数一开始被加密,然后运行时解密。所以我们进行IDA动态调试。动态调试ELF文件时debugger的配置参考如下:

【reverse IDA使用技巧】IDA动态调试Linux_ELF配置+例题:SCUCTF新生赛2021-DebugMe - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

动调之后,操作同[网鼎杯 2020 青龙组]jocker相似。

查看sub_402219sub_40207B函数,有点复杂,函数嵌套。查阅了他人博客之后,才知道是AESmd5加密。

使用Findcrypt插件,可以识别出一些加密算法。这里识别出了AESmd5加密。

在这里插入图片描述

sub_40207B

点击MD5_XXX,进入到相应的函数(sub_401CF9)。函数中出现的变量和处理运算

v5 = 0x67452301;
v6 = 0xEFCDAB89;
v7 = 0x98BADCFE;
v8 = 0x10325476;
v15 = v11 ^ (v10 | ~v12)
v15 = v12 ^ v11 ^ v10
v15 = v10 & v12 | v11 & ~v12
v15 = v11 & v10 | v12 & ~v10

md5算法一致(MD5算法解析 - 知乎 (zhihu.com))。

调用该函数的sub_40207B则进行了4次md5加密,并赋值给了unk_603170。这里我们可以直接动态调式获得md5加密后的unk_603170

0xCB, 0x8D, 0x49, 0x35, 0x21, 0xB4, 0x7A, 0x4C, 0xC1, 0xAE, 0x7E, 0x62, 0x22, 0x92, 0x66, 0xCE

sub_402219

然后分析sub_402219函数。

在这里插入图片描述

参考AES加密算法的详细介绍与实现_TimeShatter的博客-CSDN博客,一个一个对比吧。

加密后的结果在byte_6030A0中。

我们知道AES中的keyCipher,因为没有IV,所以是ECB加密模式。

from Crypto.Cipher import AES
from binascii import hexlify,unhexlify

c = unhexlify('BC0AADC0147C5ECCE0B140BC9C51D52B46B2B9434DE5324BAD7FB4B39CDB4B5B')
key = unhexlify('CB8D493521B47A4CC1AE7E62229266CE')
print(c)
print(key)
# 从IDA中导出string literal如下,会出问题
# c =  '\xBC\x0A\xAD\x35BF4\x14|^\x35BF4\x35BF4\xB1@\xBC\x9CQ\x35BF4+F\xB2\xB9CM\x35BF42K\xAD\x7F\xB4\xB3\x9C\x35BF4K['
# key = '\x6EBF1\x8DI5!\xB4zL\x35BF4\xAE~b"\x92f\x35BF4'
aes = AES.new(key, AES.MODE_ECB)
m = aes.decrypt(c)
print(m)

CTF2020PixelShooter_1474">[MRCTF2020]PixelShooter

apk文件,拖到模拟器上,玩了一下,是个飞机打陨石的游戏。

因为apk文件,所以一开始我用jadx工具打开分析,并没有找到游戏中的文字提示。后来想到应该是个Unity3D游戏,查找了apk中的文件,发现确实有Assembly-CSharp.dll文件,所以尝试用dnSpy工具分析该文件。

查找了一番,发现GameContriller中有游戏结束的函数。

在这里插入图片描述

点击进入到GameOver函数中

在这里插入图片描述

答案显然易见了。

[FlareOn1]Bob Doge

下下来是个安装程序,得到challenge1.exe,拖到IDA中分析,

在这里插入图片描述

是个.net程序。打开之后发现函数很少,且IDA不能进行反汇编,于是使用Exeinfo PE工具查看一下,是用C#写的程序。

参考推荐.Net、C# 逆向反编译四大工具利器 - Shikyoh - 博客园 (cnblogs.com),使用dnSpy工具分析

在这里插入图片描述

btnDecode_Click函数顾名思义,显然是点击decode按钮触发的事件。整个函数的功能是将dat_sercet进行三次解密,得到的text3就是我们点击decode按钮后所看到的字符。此时,我们并不清楚哪个字符串才是我们想要的,所以这里我们直接动调。

在这里插入图片描述

可以看出text就是我们想要的flag。

CTF2019simple_CPP_1513">[GXYCTF2019]simple CPP

整个程序很大,只截取关键代码。

在这里插入图片描述

第一次,我们输入的字符串与v10异或,此时qword_xxx对应的内存全0,并没有赋初值,所以它肯定在运行这行代码之前,就已经通过某个函数得到了初值,所以我们查看该变量的交叉引用,发现确实有其他函数(sub_7FF6EFF81720)调用了它

在这里插入图片描述

所以参与异或的常量字符串就是"i_will_check_is_debug_or_not",长度为28。而在异或运算中,该字符串使用的索引值是v7%27,即不超过27,但该字符串长度又是28,所以肯定不是循环异或,只能猜测我们输入的字符串长度是27了(有点勉强)。

在这里插入图片描述

第二部分,将加密后的字符串每8字节一组(不足时仍为一组)。

在这里插入图片描述

这里用z3求解就行了。因此整个解密过程如下

from z3 import *
from binascii import unhexlify

v14, v13, v12, v11 = BitVecs('v14 v13 v12 v11', 64)
solver = Solver()
solver.add(v12 & ~v14 == 0x11204161012)
solver.add((v12 & ~v13) & v14 | v12 & ((v13 & v14) | v13 & ~v14 | ~(v13 | v14)) == 0x8020717153E3013)
solver.add((v12 & ~v14) | (v13 & v14) | (v12 & ~v13) | (v14 & ~v13) == 0x3E3A4717373E7F1F)
solver.add((((v12 & ~v14) | (v13 & v14) | (v12 & ~v13) | (v14 & ~v13)) ^ v11) == 0x3E3A4717050F791F)
solver.add(((v12 & ~v14) | (v13 & v14) | v13 & v12) == (~v14 & v12 | 0xC00020130082C0C) and 1)

if solver.check() == sat:
    result = solver.model()

_v14 = result[v14].as_long()
_v13 = result[v13].as_long()
_v12 = result[v12].as_long()
_v11 = result[v11].as_long()

v5 = hex(_v14)[2:].rjust(16, '0') + hex(_v13)[2:].rjust(16, '0') + hex(_v12)[2:].rjust(16, '0') + hex(_v11)[2:].rjust(3)
v5 = unhexlify(v5)[:27]

v10 = 'i_will_check_is_debug_or_not'

block = ''
for i in range(len(v5)):
    block += chr(v5[i] ^ ord(v10[i]))

print(block)
# We1l_D0ndeajoa_Slgebra_am_i

由于该方程组具有多解,所以答案不唯一。

[FlareOn5]Ultimate Minesweeper

一个扫雷游戏。用dnspy工具打开。
在这里插入图片描述

发现GetKey()比较可疑,是一个加密函数,很有可能这个就是加密flag的。

跟踪GetKey(),右键分析该函数,在上图的分析器中可以知道调用该函数的调用者,其实就在上面第104行。

很显然,我们得跟踪RevealedCells这个变量,查看它的值是如何产生的,同样也在上面第98行。

this.RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column);

跟进VALLOC_NODE_LIMIT,发现其值是30,也就是扫雷的行列值。

那么我们就得找到扫雷的初始化地图的函数。
在这里插入图片描述

AllocateMemory()函数就是对地图进行初始化。雷区的标志就是true,非雷区的标志就是false。而判断是否是非雷区的条件就是

this.VALLOC_TYPES.Contains(this.DeriveVallocType(r, c)

跟进VALLOC_TYPES,发现包含三个值:

在这里插入图片描述

DeriveVallocType()正好在图中的下面

~(r * MainForm.VALLOC_NODE_LIMIT + c)

那么一切就都理清了,我们只要求出三个非雷区的坐标即可。

代码如下:

#include <stdio.h>

int main(){
	
	int result[] = {4294966400,4294966657,4294967026};
	int row[3] = {0};
	int colum[3] = {0};
	
	for(int i=0;i<3;i++){
		int tmp = (~result[i]) - 31;
		row[i] = tmp / 30;
		colum[i] = tmp % 30; 
	}
	
	for(int i=0;i<3;i++){
		printf("%d %d\n",row[i],colum[i]);
	}

	return 0;
	
}

根据求出的坐标,玩个游戏就可以了。

在这里插入图片描述

CTF_2018IntroToPE_1646">[CFI-CTF 2018]IntroToPE

Exeinfo PE查看,发现是c#编写的程序,使用dnspy工具打开。

在这里插入图片描述

容易发现ValidatePasswd中的verifyPasswd是关键函数,逻辑很清晰且简单。

[2019红帽杯]xx

代码很长,关键代码如下:

在这里插入图片描述

先是输入Code,遍历Code获取字节数,且满足字节数为19。

然后是

在这里插入图片描述

注意::Code是全局变量Code,我们之前输入的Code是局部变量。整个do...while循环就是判断前4个字节是不是在全局变量Code中,并且将前四个字节存储到新开辟的空间v5中。

接下来是

对v5的第5个字节赋值0,然后第一个while循环获取长度,第二个while循环的结果就是v15=4。之后对v30进行补0操作,弄成16bytes

然后就是

在这里插入图片描述

进入到该函数内部查看,有明显的特征(当然我先借助了Findcrypt插件)

在这里插入图片描述

通过比对和询问chatgpt,基本上确定是xxtea加密。

继续往下分析

在这里插入图片描述

这里打乱了顺序,但打乱是有规律的,逆着写代码也容易。

然后就是一个异或操作

在这里插入图片描述

最后就是
在这里插入图片描述

v20v30进行比对,但需要注意数据在内存以小端模式存储,所以我们要将数据反过来。

整个过程分析完了,解密代码如下(下的xxtea库没有用,所以解密脚本是拿来的,有点小错误,根据报错改代码就行了)

from Crypto.Util.number import *
import struct
from binascii import unhexlify
_DELTA = 0x9E3779B9

def _long2str(v, w):
    n = (len(v) - 1) << 2
    if w:
        m = v[-1]
        if (m < n - 3) or (m > n): return ''
        n = m
    s = struct.pack('<%iL' % len(v), *v)
    return s[0:n] if w else s

def _str2long(s, w):
    n = len(s)
    m = (4 - (n & 3) & 3) + n
    s = s.ljust(m, b"\0")
    v = list(struct.unpack('<%iL' % (m >> 2), s))
    if w: v.append(n)
    return v

def decrypt(str, key):
    if str == '': return str
    v = _str2long(str, False)
    k = _str2long(key.ljust(16, b"\0"), False)
    n = len(v) - 1
    z = v[n]
    y = v[0]
    q = 6 + 52 // (n + 1)
    sum = (q * _DELTA) & 0xffffffff
    while (sum != 0):
        e = sum >> 2 & 3
        for p in range(n, 0, -1):
            z = v[p - 1]
            v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
            y = v[p]
        z = v[n]
        v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff
        y = v[0]
        sum = (sum - _DELTA) & 0xffffffff
    return _long2str(v, True)

v20 = 'CE BC 40 6B 7C 3A 95 C0 EF 9B 20 20 91 F7 02 35 23 18 02 C8 E7 56 56 FA'.split(' ')
v20 = [int(i, 16) for i in v20]
print(v20)
v18 = len(v20)

# 逆异或
for v21 in range(23, 0, -1):
    v23 = 0
    if v21 // 3 > 0:
        v24 = v20[v21]
        while v23 < v21 // 3:
            v24 ^= v20[v23]
            v23 += 1
            v20[v21] = v24

print(v20)

# 弄好顺序
v19 = [0 for i in range(24)]
for i in range(0, 24, 4):
    v19[i] = v20[i+1]
    v19[i+1] = v20[i+3]
    v19[i+2] = v20[i]
    v19[i+3] = v20[i+2]

print(v19)

# xxtea解密
enc = 0
for each in v19:
    enc = (enc << 8) + each
enc = long_to_bytes(enc)
print(enc)

key = 'flag'.encode() + b'\x00'*12
print(key)
Code = decrypt(enc, key)
print(Code)
#b'flag{CXX_and_++tea}'

equation

在这里插入图片描述

if语句中由!+[]()构成的就是jsfuck加密了。

题目也说了是js混淆,找到了个js反混淆的网站:JavaScript Deobfuscator (dev-coco.github.io)

在这里插入图片描述

不过有的数字变成字符,需要修改一下。整体上是多个方程组,用z3解方程组就行了。

from z3 import *

estring = "l['40'] + l['35'] + l['34'] - l[0] - l['15'] - l['37'] + l[7] + l[6] - l['26'] + l['20'] + l['19'] + l[8] - l['17'] - l['14'] - l['38'] + l[1] - l[9] + l['22'] + l['41'] + l[3] - l['29'] - l['36'] - l['25'] + l[5] + l['32'] - l['16'] + l['12'] - l['24'] + l['30'] + l['39'] + l['10'] + l[2] + l['27'] + l['28'] + l['21'] + l['33'] - l['18'] + l[4] == 861 && l['31'] + l['26'] + l['11'] - l['33'] + l['27'] - l[3] + l['12'] + l['30'] + l[1] + l['32'] - l['16'] + l[7] + l['10'] - l['25'] + l['38'] - l['41'] - l['14'] - l['19'] + l['29'] + l['36'] - l[9] - l['28'] - l[6] - l[0] - l['22'] - l['18'] + l['20'] - l['37'] + l[4] - l['24'] + l['34'] - l['21'] - l['39'] - l['23'] - l[8] - l['40'] + l['15'] - l['35'] == -448 && l['26'] + l['14'] + l['15'] + l[9] + l['13'] + l['30'] - l['11'] + l['18'] + l['23'] + l[7] + l[3] + l['12'] + l['25'] - l['24'] - l['39'] - l['35'] - l['20'] + l['40'] - l[8] + l['10'] - l[5] - l['33'] - l['31'] + l['32'] + l['19'] + l['21'] - l[6] + l[1] + l['16'] + l['17'] + l['29'] + l['22'] - l[4] - l['36'] + l['41'] + l['38'] + l[2] + l[0] == 1244 && l[5] + l['22'] + l['15'] + l[2] - l['28'] - l['10'] - l[3] - l['13'] - l['18'] + l['30'] - l[9] + l['32'] + l['19'] + l['34'] + l['23'] - l['17'] + l['16'] - l[7] + l['24'] - l['39'] + l[8] - l['12'] - l['40'] - l['25'] + l['37'] - l['35'] + l['11'] - l['14'] + l['20'] - l['27'] + l[4] - l['33'] - l['21'] + l['31'] - l[6] + l[1] + l['38'] - l['29'] == -39 && l['41'] - l['29'] + l['23'] - l[4] + l['20'] - l['33'] + l['35'] + l[3] - l['19'] - l['21'] + l['11'] + l['26'] - l['24'] - l['17'] + l['37'] + l[1] + l['16'] - l[0] - l['13'] + l[7] + l['10'] + l['14'] + l['22'] + l['39'] - l['40'] + l['34'] - l['38'] + l['32'] + l['25'] - l[2] + l['15'] + l[6] + l['28'] - l[8] - l[5] - l['31'] - l['30'] - l['27'] == 485 && l['13'] + l['19'] + l['21'] - l[2] - l['33'] - l[0] + l['39'] + l['31'] - l['23'] - l['41'] + l['38'] - l['29'] + l['36'] + l['24'] - l['20'] - l[9] - l['32'] + l['37'] - l['35'] + l['40'] + l[7] - l['26'] + l['15'] - l['10'] - l[6] - l['16'] - l[4] - l[5] - l['30'] - l['14'] - l['22'] - l['25'] - l['34'] - l['17'] - l['11'] - l['27'] + l[1] - l['28'] == -1068 && l['32'] + l[0] + l[9] + l['14'] + l['11'] + l['18'] - l['13'] + l['24'] - l[2] - l['15'] + l['19'] - l['21'] + l[1] + l['39'] - l[8] - l[3] + l['33'] + l[6] - l[5] - l['35'] - l['28'] + l['25'] - l['41'] + l['22'] - l['17'] + l['10'] + l['40'] + l['34'] + l['27'] - l['20'] + l['23'] + l['31'] - l['16'] + l[7] + l['12'] - l['30'] + l['29'] - l[4] == 939 && l['19'] + l['11'] + l['20'] - l['16'] + l['40'] + l['25'] + l[1] - l['31'] + l['28'] - l['23'] + l['14'] - l[9] - l['27'] + l['35'] + l['39'] - l['37'] - l[8] - l['22'] + l[5] - l[6] + l[0] - l['32'] + l['24'] + l['33'] + l['29'] + l['38'] + l['15'] - l[2] + l['30'] + l[7] + l['12'] - l[3] - l['17'] + l['34'] + l['41'] - l[4] - l['13'] - l['26'] == 413 && l['22'] + l[4] - l[9] + l['34'] + l['35'] + l['17'] + l[3] - l['24'] + l['38'] - l[5] - l['41'] - l['31'] - l[0] - l['25'] + l['33'] + l['15'] - l[1] - l['10'] + l['16'] - l['29'] - l['12'] + l['26'] - l['39'] - l['21'] - l['18'] - l[6] - l['40'] - l['13'] + l[8] + l['37'] + l['19'] + l['14'] + l['32'] + l['28'] - l['11'] + l['23'] + l['36'] + l[7] == 117 && l['32'] + l['16'] + l[3] + l['11'] + l['34'] - l['31'] + l['14'] + l['25'] + l[1] - l['30'] - l['33'] - l['40'] - l[4] - l['29'] + l['18'] - l['27'] + l['13'] - l['19'] - l['12'] + l['23'] - l['39'] - l['41'] - l[8] + l['22'] - l[5] - l['38'] - l[9] - l['37'] + l['17'] - l['36'] + l['24'] - l['21'] + l[2] - l['26'] + l['20'] - l[7] + l['35'] - l[0] == -313 && l['40'] - l[1] + l[5] + l[7] + l['33'] + l['29'] + l['12'] + l['38'] - l['31'] + l[2] + l['14'] - l['35'] - l[8] - l['24'] - l['39'] - l[9] - l['28'] + l['23'] - l['17'] - l['22'] - l['26'] + l['32'] - l['11'] + l[4] - l['36'] + l['10'] + l['20'] - l['18'] - l['16'] + l[6] - l[0] + l[3] - l['30'] + l['37'] - l['19'] + l['21'] + l['25'] - l['15'] == -42 && l['21'] + l['26'] - l['17'] - l['25'] + l['27'] - l['22'] - l['39'] - l['23'] - l['15'] - l['20'] - l['32'] + l['12'] + l[3] - l[6] + l['28'] + l['31'] + l['13'] - l['16'] - l['37'] - l['30'] - l[5] + l['41'] + l['29'] + l['36'] + l[1] + l['11'] + l['24'] + l['18'] - l['40'] + l['19'] - l['35'] + l[2] - l['38'] + l['14'] - l[9] + l[4] + l[0] - l['33'] == 289 && l['29'] + l['31'] + l['32'] - l['17'] - l[7] + l['34'] + l[2] + l['14'] + l['23'] - l[4] + l[3] + l['35'] - l['33'] - l[9] - l['20'] - l['37'] + l['24'] - l['27'] + l['36'] + l['15'] - l['18'] - l[0] + l['12'] + l['11'] - l['38'] + l[6] + l['22'] + l['39'] - l['25'] - l['10'] - l['19'] - l[1] + l['13'] - l['41'] + l['30'] - l['16'] + l['28'] - l['26'] == -117 && l[5] + l['37'] - l['39'] + l[0] - l['27'] + l['12'] + l['41'] - l['22'] + l[8] - l['16'] - l['38'] + l[9] + l['15'] - l['35'] - l['29'] + l['18'] + l[6] - l['25'] - l['28'] + l['36'] + l['34'] + l['32'] - l['14'] - l[1] + l['20'] + l['40'] - l['19'] - l[4] - l[7] + l['26'] + l['30'] - l['10'] + l['13'] - l['21'] + l[2] - l['23'] - l[3] - l['33'] == -252 && l['29'] + l['10'] - l['41'] - l[9] + l['12'] - l['28'] + l['11'] + l['40'] - l['27'] - l[8] + l['32'] - l['25'] - l['23'] + l['39'] - l[1] - l['36'] - l['15'] + l['33'] - l['20'] + l['18'] + l['22'] - l[3] + l[6] - l['34'] - l['21'] + l['19'] + l['26'] + l['13'] - l[4] + l[7] - l['37'] + l['38'] - l[2] - l['30'] - l[0] - l['35'] + l[5] + l['17'] == -183 && l[6] - l[8] - l['20'] + l['34'] - l['33'] - l['25'] - l[4] + l[3] + l['17'] - l['13'] - l['15'] - l['40'] + l[1] - l['30'] - l['14'] - l['28'] - l['35'] + l['38'] - l['22'] + l[2] + l['24'] - l['29'] + l[5] + l[9] + l['37'] + l['23'] - l['18'] + l['19'] - l['21'] + l['11'] + l['36'] + l['41'] - l[7] - l['32'] + l['10'] + l['26'] - l[0] + l['31'] == 188 && l[3] + l[6] - l['41'] + l['10'] + l['39'] + l['37'] + l[1] + l[8] + l['21'] + l['24'] + l['29'] + l['12'] + l['27'] - l['38'] + l['11'] + l['23'] + l['28'] + l['33'] - l['31'] + l['14'] - l[5] + l['32'] - l['17'] + l['40'] - l['34'] + l['20'] - l['22'] - l['16'] + l['19'] + l[2] - l['36'] - l[7] + l['18'] + l['15'] + l['26'] - l[0] - l[4] + l['35'] == 1036 && l['28'] - l['33'] + l[2] + l['37'] - l['12'] - l[9] - l['39'] + l['16'] - l['32'] + l[8] - l['36'] + l['31'] + l['10'] - l[4] + l['21'] - l['25'] + l['18'] + l['24'] - l[0] + l['29'] - l['26'] + l['35'] - l['22'] - l['41'] - l[6] + l['15'] + l['19'] + l['40'] + l[7] + l['34'] + l['17'] - l[3] - l['13'] + l[5] + l['23'] + l['11'] - l['27'] + l[1] == 328 && l['22'] - l['32'] + l['17'] - l[9] + l['20'] - l['18'] - l['34'] + l['23'] + l['36'] - l['35'] - l['38'] + l['27'] + l[4] - l[5] - l['41'] + l['29'] + l['33'] + l[0] - l['37'] + l['28'] - l['40'] - l['11'] - l['12'] + l[7] + l[1] + l[2] - l['26'] - l['16'] - l[8] + l['24'] - l['25'] + l[3] - l[6] - l['19'] - l['39'] - l['14'] - l['31'] + l['10'] == -196 && l['11'] + l['13'] + l['14'] - l['15'] - l['29'] - l[2] + l[7] + l['20'] + l['30'] - l['36'] - l['33'] - l['19'] + l['31'] + l[0] - l['39'] - l[4] - l[6] + l['38'] + l['35'] - l['28'] + l['34'] - l[9] - l['23'] - l['26'] + l['37'] - l[8] - l['27'] + l[5] - l['41'] + l[3] + l['17'] + l['40'] - l['10'] + l['25'] + l['12'] - l['24'] + l['18'] + l['32'] == 7 && l['34'] - l['37'] - l['40'] + l[4] - l['22'] - l['31'] - l[6] + l['38'] + l['13'] - l['28'] + l[8] + l['30'] - l['20'] - l[7] - l['32'] + l['26'] + l[1] - l['18'] + l[5] + l['35'] - l['24'] - l['41'] + l[9] - l[0] - l[2] - l['15'] - l['10'] + l['12'] - l['36'] + l['33'] - l['16'] - l['14'] - l['25'] - l['29'] - l['21'] + l['27'] + l[3] - l['17'] == -945 && l['12'] - l['30'] - l[8] + l['20'] - l[2] - l['36'] - l['25'] - l[0] - l['19'] - l['28'] - l[7] - l['11'] - l['33'] + l[4] - l['23'] + l['10'] - l['41'] + l['39'] - l['32'] + l['27'] + l['18'] + l['15'] + l['34'] + l['13'] - l['40'] + l['29'] - l[6] + l['37'] - l['14'] - l['16'] + l['38'] - l['26'] + l['17'] + l['31'] - l['22'] - l['35'] + l[5] - l[1] == -480 && l['36'] - l['11'] - l['34'] + l[8] + l[0] + l['15'] + l['28'] - l['39'] - l['32'] - l[2] - l['27'] + l['22'] + l['16'] - l['30'] - l[3] + l['31'] - l['26'] + l['20'] + l['17'] - l['29'] - l['18'] + l['19'] - l['10'] + l[6] - l[5] - l['38'] - l['25'] - l['24'] + l[4] + l['23'] + l[9] + l['14'] + l['21'] - l['37'] + l['13'] - l['41'] - l['12'] + l['35'] == -213 && l['19'] - l['36'] - l['12'] + l['33'] - l['27'] - l['37'] - l['25'] + l['38'] + l['16'] - l['18'] + l['22'] - l['39'] + l['13'] - l[7] - l['31'] - l['26'] + l['15'] - l['10'] - l[9] - l[2] - l['30'] - l['11'] + l['41'] - l[4] + l['24'] + l['34'] + l[5] + l['17'] + l['14'] + l[6] + l[8] - l['21'] - l['23'] + l['32'] - l[1] - l['29'] - l[0] + l[3] == -386 && l[0] + l[7] - l['28'] - l['38'] + l['19'] + l['31'] - l[5] + l['24'] - l[3] + l['33'] - l['12'] - l['29'] + l['32'] + l[1] - l['34'] - l[9] - l['25'] + l['26'] - l[8] + l[4] - l['10'] + l['40'] - l['15'] - l['11'] - l['27'] + l['36'] + l['14'] + l['41'] - l['35'] - l['13'] - l['17'] - l['21'] - l['18'] + l['39'] - l[2] + l['20'] - l['23'] - l['22'] == -349 && l['10'] + l['22'] + l['21'] - l[0] + l['15'] - l[6] + l['20'] - l['29'] - l['30'] - l['33'] + l['19'] + l['23'] - l['28'] + l['41'] - l['27'] - l['12'] - l['37'] - l['32'] + l['34'] - l['36'] + l[3] + l[1] - l['13'] + l['18'] + l['14'] + l[9] + l[7] - l['39'] + l[8] + l[2] - l['31'] - l[5] - l['40'] + l['38'] - l['26'] - l[4] + l['16'] - l['25'] == 98 && l['28'] + l['38'] + l['20'] + l[0] - l[5] - l['34'] - l['41'] + l['22'] - l['26'] + l['11'] + l['29'] + l['31'] - l[3] - l['16'] + l['23'] + l['17'] - l['18'] + l[9] - l[4] - l['12'] - l['19'] - l['40'] - l['27'] + l['33'] + l[8] - l['37'] + l[2] + l['15'] - l['24'] - l['39'] + l['10'] + l['35'] - l[1] + l['30'] - l['36'] - l['25'] - l['14'] - l['32'] == -412 && l[1] - l['24'] - l['29'] + l['39'] + l['41'] + l[0] + l[9] - l['19'] + l[6] - l['37'] - l['22'] + l['32'] + l['21'] + l['28'] + l['36'] + l[4] - l['17'] + l['20'] - l['13'] - l['35'] - l[5] + l['33'] - l['27'] - l['30'] + l['40'] + l['25'] - l['18'] + l['34'] - l[3] - l['10'] - l['16'] - l['23'] - l['38'] + l[8] - l['14'] - l['11'] - l[7] + l['12'] == -95 && l[2] - l['24'] + l['31'] + l[0] + l[9] - l[6] + l[7] - l[1] - l['22'] + l[8] - l['23'] + l['40'] + l['20'] - l['38'] - l['11'] - l['14'] + l['18'] - l['36'] + l['15'] - l[4] - l['41'] - l['12'] - l['34'] + l['32'] - l['35'] + l['17'] - l['21'] - l['10'] - l['29'] + l['39'] - l['16'] + l['27'] + l['26'] - l[3] - l[5] + l['13'] + l['25'] - l['28'] == -379 && l['19'] - l['17'] + l['31'] + l['14'] + l[6] - l['12'] + l['16'] - l[8] + l['27'] - l['13'] + l['41'] + l[2] - l[7] + l['32'] + l[1] + l['25'] - l[9] + l['37'] + l['34'] - l['18'] - l['40'] - l['11'] - l['10'] + l['38'] + l['21'] + l[3] - l[0] + l['24'] + l['15'] + l['23'] - l['20'] + l['26'] + l['22'] - l[4] - l['28'] - l[5] + l['39'] + l['35'] == 861 && l['35'] + l['36'] - l['16'] - l['26'] - l['31'] + l[0] + l['21'] - l['13'] + l['14'] + l['39'] + l[7] + l[4] + l['34'] + l['38'] + l['17'] + l['22'] + l['32'] + l[5] + l['15'] + l[8] - l['29'] + l['40'] + l['24'] + l[6] + l['30'] - l[2] + l['25'] + l['23'] + l[1] + l['12'] + l[9] - l['10'] - l[3] - l['19'] + l['20'] - l['37'] - l['33'] - l['18'] == 1169 && l['13'] + l[0] - l['25'] - l['32'] - l['21'] - l['34'] - l['14'] - l[9] - l[8] - l['15'] - l['16'] + l['38'] - l['35'] - l['30'] - l['40'] - l['12'] + l[3] - l['19'] + l[4] - l['41'] + l[2] - l['36'] + l['37'] + l['17'] - l[1] + l['26'] - l['39'] - l['10'] - l['33'] + l[5] - l['27'] - l['23'] - l['24'] - l[7] + l['31'] - l['28'] - l['18'] + l[6] == -1236 && l['20'] + l['27'] - l['29'] - l['25'] - l[3] + l['28'] - l['32'] - l['11'] + l['10'] + l['31'] + l['16'] + l['21'] - l[7] + l[4] - l['24'] - l['35'] + l['26'] + l['12'] - l['37'] + l[6] + l['23'] + l['41'] - l['39'] - l['38'] + l['40'] - l['36'] + l[8] - l[9] - l[5] - l[1] - l['13'] - l['14'] + l['19'] + l[0] - l['34'] - l['15'] + l['17'] + l['22'] == -114 && l['12'] - l['28'] - l['13'] - l['23'] - l['33'] + l['18'] + l['10'] + l['11'] + l[2] - l['36'] + l['41'] - l['16'] + l['39'] + l['34'] + l['32'] + l['37'] - l['38'] + l['20'] + l[6] + l[7] + l['31'] + l[5] + l['22'] - l[4] - l['15'] - l['24'] + l['17'] - l[3] + l[1] - l['35'] - l[9] + l['30'] + l['25'] - l[0] - l[8] - l['14'] + l['26'] + l['21'] == 659 && l['21'] - l[3] + l[7] - l['27'] + l[0] - l['32'] - l['24'] - l['37'] + l[4] - l['22'] + l['20'] - l[5] - l['30'] - l['31'] - l[1] + l['15'] + l['41'] + l['12'] + l['40'] + l['38'] - l['17'] - l['39'] + l['19'] - l['13'] + l['23'] + l['18'] - l[2] + l[6] - l['33'] - l[9] + l['28'] + l[8] - l['16'] - l['10'] - l['14'] + l['34'] + l['35'] - l['11'] == -430 && l['11'] - l['23'] - l[9] - l['19'] + l['17'] + l['38'] - l['36'] - l['22'] - l['10'] + l['27'] - l['14'] - l[4] + l[5] + l['31'] + l[2] + l[0] - l['16'] - l[8] - l['28'] + l[3] + l['40'] + l['25'] - l['33'] + l['13'] - l['32'] - l['35'] + l['26'] - l['20'] - l['41'] - l['30'] - l['12'] - l[7] + l['37'] - l['39'] + l['15'] + l['18'] - l['29'] - l['21'] == -513 && l['32'] + l['19'] + l[4] - l['13'] - l['17'] - l['30'] + l[5] - l['33'] - l['37'] - l['15'] - l['18'] + l[7] + l['25'] - l['14'] + l['35'] + l['40'] + l['16'] + l[1] + l[2] + l['26'] - l[3] - l['39'] - l['22'] + l['23'] - l['36'] - l['27'] - l[9] + l[6] - l['41'] - l[0] - l['31'] - l['20'] + l['12'] - l[8] + l['29'] - l['11'] - l['34'] + l['21'] == -502 && l['30'] - l['31'] - l['36'] + l[3] + l[9] - l['40'] - l['33'] + l['25'] + l['39'] - l['26'] + l['23'] - l[0] - l['29'] - l['32'] - l[4] + l['37'] + l['28'] + l['21'] + l['17'] + l[2] + l['24'] + l[6] + l[5] + l[8] + l['16'] + l['27'] + l['19'] + l['12'] + l['20'] + l['41'] - l['22'] + l['15'] - l['11'] + l['34'] - l['18'] - l['38'] + l[1] - l['14'] == 853 && l['38'] - l['10'] + l['16'] + l[8] + l['21'] - l['25'] + l['36'] - l['30'] + l['31'] - l[3] + l[5] - l['15'] + l['23'] - l['28'] + l[7] + l['12'] - l['29'] + l['22'] - l[0] - l['37'] - l['14'] - l['11'] + l['32'] + l['33'] - l[9] + l['39'] + l['41'] - l['19'] - l[1] + l['18'] - l[4] - l[6] + l['13'] + l['20'] - l[2] - l['35'] - l['26'] + l['27'] == -28 && l['11'] + l['18'] - l['26'] + l['15'] - l['14'] - l['33'] + l[7] - l['23'] - l['25'] + l[0] - l[6] - l['21'] - l['16'] + l['17'] - l['19'] - l['28'] - l['38'] - l['37'] + l[9] + l['20'] - l[8] - l[3] + l['22'] - l['35'] - l['10'] - l['31'] - l[2] + l['41'] - l[1] - l[4] + l['24'] - l['34'] + l['39'] + l['40'] + l['32'] - l[5] + l['36'] - l['27'] == -529 && l['38'] + l[8] + l['36'] + l['35'] - l['23'] - l['34'] + l['13'] - l[4] - l['27'] - l['24'] + l['26'] + l['31'] - l['30'] - l[5] - l['40'] + l['28'] - l['11'] - l[2] - l['39'] + l['15'] + l['10'] - l['17'] + l[3] + l['19'] + l['22'] + l['33'] + l[0] + l['37'] + l['16'] - l[9] - l['32'] + l['25'] - l['21'] - l['12'] + l[6] - l['41'] + l['20'] - l['18'] == -12 && l[6] - l['30'] - l['20'] - l['27'] - l['14'] - l['39'] + l['41'] - l['33'] - l[0] + l['25'] - l['32'] - l[3] + l['26'] - l['12'] + l[8] - l['35'] - l['24'] + l['15'] + l[9] - l[4] + l['13'] + l['36'] + l['34'] + l[1] - l['28'] - l['21'] + l['18'] + l['23'] + l['29'] - l['10'] - l['38'] + l['22'] + l['37'] + l[5] + l['19'] + l[7] + l['16'] - l['31'] == 81".replace("'",'')
elist = estring.split('&&')
print(elist)

s = Solver()
flen = 0x2a
l = [0 for i in range(flen)]

for i in range(flen):
    l[i] = Int(f'l[{i}]')

for e in elist:
    s.add(eval(e))

if s.check() == sat:
    result = s.model()

print(result)

flag = ''
for i in range(flen):
    flag += chr(result[eval(f'l[i]')].as_long())

print(flag)

http://www.niftyadmin.cn/n/1485279.html

相关文章

采药 ,简单背包 ,代码写得比较差

采药 &#xff0c;简单背包 &#xff0c;代码写得比较差 #include <stdio.h> #include <algorithm> #include <memory.h> using namespace std; int result[1001][101]; int w[101],v[101]; int main() {int totalTime , count;memset(result , 0 ,sizeof(re…

ajax put请求_放下Axios,手写纯原生Ajax通信接口

前言axios作为vue框架中最常用的ajax工具包之一&#xff0c;可以与后台API接口进行通信&#xff0c;传送或接收数据&#xff0c;在前后端分离开发中&#xff0c;发挥了通信桥梁的重要作用。虽然像axios这样的第三方工具包很好用&#xff0c;但是这类工具包并不能很好地帮助前段…

bat怎么获取前一天 的日期_【转】BAT取当前日期的前一天

取前一天的日期 OKecho off::前一天的日期&#xff0c;格式化输出echo ----代码开始----------echo Wscript.echo dateadd("d",-1,date)>vbs.vbsfor /f %%a in (cscript //nologo vbs.vbs) do del vbs.vbs&&set yyyymmdd%%afor /f "tokens1,2,…

python scket 通讯_python3.5实现socket通讯示例(TCP)

TCP连接&#xff1a; tcp是面向连接的一个协议&#xff0c;意味着&#xff0c;客户端和服务器开发发送数据之前&#xff0c;需要先握手创建一个TCP连接。TCP连接的一端与客户端套接字相互联系&#xff0c;另一端与服务器套接字相联系。当创建该TCP连接的时&#xff0c;我们需要…

SafeNet公司高层出席2012年安全信息大会

全球领先的信息安全解决方案供应商SafeNet公司近日宣布&#xff0c;公司负责企业发展战略的副总裁大卫艾图&#xff08;David Etue&#xff09;将主持在旧金山举行的2012年安全信息大会&#xff08; RSA Conference &#xff09;。 此外&#xff0c;艾图还将与阿卡迈科技公司&a…

axios不发起请求_详解Axios 如何取消已发送的请求

前言最近在项目中遇到一个问题&#xff0c;在连续发送同一请求时&#xff0c;如果第二次请求比第一次请求快&#xff0c;那么实际显示的是第一次请求的数据&#xff0c;这就会造成数据和我选择的内容不一致的问题。解决的方案&#xff1a;在后续发送请求时&#xff0c;判断之前…

C#根据字符串内容读取Object属性的值

首先随便哪一个类举例子 public class User{....public string name {get;set;}public string phone {get;set;}public string adress {get;set;}}然后在实例化之后&#xff0c;使用matchcolumn指定需要读取的属性 User user new User()... string matchcolumn name; stri…

Jaxb annotation初步使用

一.Jaxb处理java对象和xml之间转换常用的annotation有&#xff1a; XmlTypeXmlElementXmlRootElementXmlAttributeXmlAccessorTypeXmlAccessorOrderXmlTransientXmlJavaTypeAdapter二.常用annotation使用说明 XmlType XmlType用在class类的注解&#xff0c;常与XmlRootElement&…