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

[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字符串,如下:

请添加图片描述

第二个就是了。

[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):#老是忘记加-1byte_403000[i-1] ^= byte_403000[i]print(''.join(chr(i) for i in byte_403000))

[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;}

[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加密。

在这里插入图片描述

[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>>

[BJDCTF2020]BJD hamburger competition

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

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

找到这个用dnspy打开,

在这里插入图片描述

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

这里取前20位。

flag{B8C37E33DEFDE51CF91E}

[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, unhexlifyqword_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)

[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; // raxif ( a1 ){type1(*((_QWORD *)a1 + 1));putchar(*a1);return type1(*((_QWORD *)a1 + 2));}return result;
}int __fastcall type2(char *a1)
{int result; // eaxif ( 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; // eaxchar Str[200]; // [esp+13h] [ebp-E5h] BYREFchar 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] BYREFunsigned 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; // eaxint 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_416050sub_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;//计算出v15while(v6 < 8){v11 += byte_856050[++v12];v13 = byte_856050[v12];v8 = byte_856050[v11];byte_856050[v11] = v13;byte_856050[v12] = v8;//sub_401710v17[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哪里分析错了?!

[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 base64enc3 = '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;  elseStr[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已经很明显了!

[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加密一下就行了。

[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,unhexlifyc = 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)

[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。

[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 unhexlifyv14, 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;}

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

在这里插入图片描述

[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 = 0x9E3779B9def _long2str(v, w):n = (len(v) - 1) << 2if w:m = v[-1]if (m < n - 3) or (m > n): return ''n = ms = struct.pack('<%iL' % len(v), *v)return s[0:n] if w else sdef _str2long(s, w):n = len(s)m = (4 - (n & 3) & 3) + ns = s.ljust(m, b"\0")v = list(struct.unpack('<%iL' % (m >> 2), s))if w: v.append(n)return vdef decrypt(str, key):if str == '': return strv = _str2long(str, False)k = _str2long(key.ljust(16, b"\0"), False)n = len(v) - 1z = v[n]y = v[0]q = 6 + 52 // (n + 1)sum = (q * _DELTA) & 0xffffffffwhile (sum != 0):e = sum >> 2 & 3for 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))) & 0xffffffffy = v[p]z = v[n]v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffffy = v[0]sum = (sum - _DELTA) & 0xffffffffreturn _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 = 0if v21 // 3 > 0:v24 = v20[v21]while v23 < v21 // 3:v24 ^= v20[v23]v23 += 1v20[v21] = v24print(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.rhkb.cn/news/29561.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端技术搭建俄罗斯方块(内含源码)

The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了扫雷游戏&#xff0c;今天还是继续按照我们原定的节奏来带领大家完成俄罗斯方块游戏&#xff0c;功能也比较简单简单&#xff0c;也…

订餐系统之微信点餐

经过几天的开发、测试微信点餐demo终于完成了&#xff0c;特在此分享下&#xff0c;不好的地方请大家多指正下哈&#xff01;一开始&#xff0c;就想这东西出来这么久了&#xff0c;网上应该有很多现成的东西&#xff0c;于是&#xff0c;baidu、google了半天&#xff0c;基本没…

点餐系统测试

一、设计测试用例 二、提交BUG a)BUG 一 标题:兼容性差&#xff0c;只能在Chrome浏览器和火狐浏览器上使用 1.版本号&#xff1a;V0009 2.测试环境&#xff1a;Chrome 浏览器 版本号 96.0.4664.45 火狐 浏览器 版本号 97.0.1 操作系统&#xff1a;win10 3.测试数据…

点餐系统详解

一、项目简介 “民以食为天”餐饮行业作为传统行业&#xff0c;传承着中国五千年来的饮食文化&#xff0c;现如今互联网发展迅速&#xff0c;随着餐厅的规模扩大&#xff0c;并伴随年轻消费者的崛起&#xff0c;网上点餐&#xff0c;逐渐成为一项必不可少的功能&#xff0c;传…

【Android】线上自助点餐系统

【Android】线上自助点餐系统 效果视频商品选购界面效果图商品数据商品标题文件商品详细文件信息商品图片 添加商品数据解析左侧标题数据解析右侧商品数据 左侧标题与右侧商品双向绑定加减按钮监听所选商品存入数据库 商品复选效果图取出商品数据商品清空商品增删 支付界面效果…

点餐系统的开发,php后台+微信小程序 实现完整的餐厅点餐系统。

对于我们的日常生活来说&#xff0c;微信点餐小程序使用十分广泛&#xff0c;使用微信小程序进行点餐已经成为如今的新餐饮消费方式之一&#xff0c;小程序的入口就隐藏在微信中&#xff0c;背靠微信大流量平台&#xff0c;坐拥数以万计的用户。对于商家来说&#xff0c;使用微…

chatgpt弄的 简单的点餐系统 不知道怎么样

点餐系统可以提高餐厅的效率和客户的体验&#xff0c;因此在许多情况下非常有用。 对于餐厅&#xff0c;点餐系统可以帮助管理顾客的订单&#xff0c;包括快速和准确地获取顾客的点餐信息&#xff0c;避免了人工点餐的错误&#xff0c;减少服务员的工作负担&#xff0c;提高服务…

17行python代码,openai帮你实现下班自由

chatgpt最近火到不行&#xff0c;AI受到了前所未有的关注&#xff0c;openai作为开发团队不仅仅开发了一个在线尝鲜的聊天机器人&#xff0c;也提供API并且提供了python语言的的pypi库。 火出圈的聊天机器人是chatgpt3,既然排行老三&#xff0c;就说明这个张飞的前面还有大哥刘…

安卓手机超强的悬浮窗工具

看到一个挺好用的安卓悬浮窗工具&#xff0c;仅作分享 该款工具是安卓上的一款应用&#xff0c;名字叫fooView 安装之后便会出现在手机主界面中的左下方或者是右下方&#xff01; 怎样操控球球&#xff1f; 按住它&#xff0c;向上滑动即可打开程序和文件管理器。也可以直接…

[日常折腾之码上归一]多种编程语言打印当前系统时间

之前做PIC粒子模拟时程序通常跑好几天&#xff0c;为了对比不同网格、粒子数等条件下的运行天数&#xff0c;我养成了一个在程序运行程序开始和程序运行结束分别打印当前时间戳的习惯&#xff0c;并坚持了3个多月。下面结合自己的实践经历&#xff0c;分别使用不同编程语言打印…

chatgpt赋能python:Python免费编程软件介绍

Python免费编程软件介绍 Python是一种功能强大且易于学习的编程语言&#xff0c;因此在全球范围内受到广泛使用。除了许多付费的编程软件外&#xff0c;Python还拥有许多优秀的免费编程软件&#xff0c;为用户带来了更多的选择和方便。以下将扼要介绍几个著名的Python免费编程…

线性插值和双线性插值

先讲一下线性插值: 已知数据 (x0, y0) 与 (x1, y1),要计算 [x0, x1] 区间内某一位置 x 在直线上的y值(反过来也是一样,略) 上面比较好理解吧,仔细看就是用x和x0,x1的距离作为一个权重,用于y0和y1的加权。离哪个点近,那个点对最后的值共享越多。双线性插值本质上就是…

MATLAB复习高等数学下册(8.0)

Matlab是一款科学计算软件&#xff0c;可用于数据分析、图形绘制、模型建立、算法设计和数值计算等方面。Matlab使用MATrix LABoratory的缩写&#xff0c;即矩阵实验室&#xff0c;最初是为数值计算和线性代数而设计的&#xff0c;但后来也扩展到了其他领域&#xff0c;如图像和…

C++调用matlab编译动态库方法及相关问题解决

目录 参考链接&#xff1a;1、C调用matlab代码的方法1.1、Library Compiler 方法1.1.1、功能1.1.2、参考链接1.1.3、matlab编译动态库方法1.1.4、C 使用matlab编译动态库的传参方法1.1.4.1、演示把一个cv::Mat单通道影像传入matlab编译的dll中,解析matlab返回的变量1.1.4.2、下…

【Matlab】画一只简单的小猫

这个其实是用ChatGPT生成的 % 定义猫的轮廓点坐标 x [-1 -1 -0.5 -0.5 0.5 0.5 1 1]; y [0.5 -0.5 -1 -0.5 -0.5 -1 -0.5 0.5];% 绘制猫的轮廓 plot(x, y, LineWidth, 2);% 添加猫的眼睛 hold on; plot(-0.4, 0.2, ko, MarkerFaceColor, k, MarkerSize, 6); plot(0.4, 0.2, …

MATLAB中CVX工具箱解决凸优化问题的基本知识——语法、变量声明、目标函数、约束条件、cvx编程错误及解决方法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、语法二、变量声明三、目标函数四、约束条件五、函数六、cvx特有的数学运算表达式七、常见错误八、进阶阅读参考资料 前言 本文是在最近学习MATLAB CVX工具…

记一次累累累的过往:在Linux环境下的项目实现

写在前面&#xff1a;当你看到这篇文章的时候&#xff0c;我有可能已经完成了这个实训&#xff0c;这是边做边写的一个叙事流&#xff0c;或者是什么私心分享流&#xff09;。 大书记官艾尔海森为你写诗:) 任务背景&#xff1a; 在任意环境下实现ARP欺骗&#xff08;主&#…

Prompt learning 教学[技巧篇]:通过增加示例、引导词、特殊符号指令等方式让chatgpt输出更好的答案

Prompt learning 教学[技巧篇]&#xff1a;通过增加示例、引导词、特殊符号指令等方式让chatgpt输出更好的答案 技巧1&#xff1a;To Do and Not To Do 在问答场景里&#xff0c;为了让 AI 回答更加准确&#xff0c;一般会在问题里加条件。比如让 AI 推荐一部电影给你 Recomme…

对含有中英文的文本去除停用词 结巴分词

对含有中英文的文本去除停用词 分词 这里的停用词表可以自己定义或者采用网上的 是文本分类 情感分析进行预处理的步骤 from collections import Counter import jieba**# jieba.load_userdict(userdict.txt) **# 创建停用词list**** def stopwordslist(filepath):stopwords …

一、(4) 结巴分词词性提取

一、&#xff08;4&#xff09; 结巴分词词性提取 代码如下&#xff1a; # -*- coding: utf-8 -*- """ Created on Tue May 14 14:45:01 2019author: sun """ import jieba.posseg as psg import codecs# 建立结果保存路径 result codecs.ope…