BUUCTF逆向工程-3
BUUCTF逆向工程第三页部分题目
没写完,但是被push了,所以发出来吧~
[2019红帽杯]childRE
很神奇的一道题,不过为啥红帽杯的题都多多少少有点阴间成分?
首先看到它的验证部分,这个很好逆。
运行如下代码:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char ans[99] = "1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'ASDFGHJKL:\"ZXCVBNM<>?zxcvbnm,./";
char num[99] = "55565653255552225565565555243466334653663544426565555525555222";
char str[99] = "(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&";
int i, pos;
pos = 0;
do
{
for(i = 32; i < 125; i++)
{
if(ans[i % 23] == str[pos] && ans[i / 23] == num[pos])
{
cout << char(i);
break;
}
}
pos++;
}
while(pos < 62);
return 0;
}
就可以得到
private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
而前文中存在UnDecorateSymbolName()函数。因此我们得到的字符串是某个函数去修饰后的样子。
根据这篇文章可以知道函数去修饰前应该是?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
继续跟踪它与输入的关系可以找到找到这个函数:
不用怀疑,这个b绝对是二叉树。为什么呢?被恶心多了就记住了。看规则是后序遍历,手动算一下遍历顺序,写出代码:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char raw[99] = "?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z";
char ans[99];
int order[99] = {15,16,7,17,18,8,3,19,20,9,21,22,10,4,1,23,24,11,25,26,12,5,27,28,13,29,30,14,6,2,0};
int i;
for(i = 0; i < 31; i++)
{
ans[order[i]] = raw[i];
}
for(i = 0; i < 31; i++)
{
cout << ans[i];
}
return 0;
}
运行得到Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
将其32位小写md5加密后得到flag:63b148e750fed3a33419168ac58083f5
[SWPU2019]ReverseMe
这道题代码写得很神奇,反正我没看得太懂,所以嗯逆非常滴恶心。
下面这一段是本题唯一可以看得懂的东西。其中v29很奇怪,会莫名其妙地变掉从而执行if。
接着是sub_6525C0()函数中加密的部分。
然后是主函数中的对比部分。
它的代码写得非常莫名其妙,所以采用动调回事一种很好的方法。动调的时候一定要对着汇编码,随便输入点啥,然后把跟Str异或之后的东西前几位记一下。接着就不停地找哪个寄存器是这一串东西,它的值什么时候变了,就能找到加密的地方。写出代码:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char str[20] = "SWPU_2019_CTF";
int Xor[99] = {134, 12, 62, 202, 152, 215, 174, 25, 226, 119,
107, 166, 106, 161, 119, 176, 105, 145, 55, 5,
122, 249, 123, 48, 67, 90, 75, 16, 134, 125,
212, 40, 171, 171, 171, 171, 171, 171, 171, 171,
0, 0, 0, 0, 0, 0, 0, 0, 20, 246,
245, 17, 13, 244, 0, 24};
int input[99] = {179, 55, 15, 248, 188, 188, 174, 93, 186, 90,
77, 134, 68, 151, 98, 211, 79, 186, 36, 22,
11, 159, 114, 26, 101, 104, 109, 38, 186, 107,
200, 103};
int i;
for(i = 0; i < 32; i++)
{
input[i] ^= str[i % strlen(str)];
input[i] ^= Xor[i];
cout << char(input[i]);
}
return 0;
}
得到flag:Y0uaretheB3st!#@_VirtualCC
[羊城杯 2020]login
是一个简单的pyinstaller逆向,下载这个,在命令行里输入
python pyinstxtractor.py attachment.exe
会得到一个文件夹,在文件夹里找到login.pyc,把它传到这里就可以了。
是个简单的异或和z3,得到flag:58964088b637e50d3a22b9510c1d1ef8
[QCTF2018]Xman-babymips
简单的异或加密,注意要取后两位,即x & 0xFF。
得到flag:ReA11y_4_B@89_mlp5_4_XmAn_
[安洵杯 2019]crackMe
很牛逼的一道题。
根据findcrypt插件或者运行后搜索弹出的提示词可以找到:
上面一大段是对base表进行更改,下面一段看不懂。跟进得到:
其中sub_411172()是生成SM4加密所需轮秘钥(RK)。查看RK的交叉引用可以得到SM4的加密过程:
跟进TopLevelExceptionFilter可以找到校验部分:
在base64加密过程中有魔改的地方:
即:base表需要向前移动24个,同时最后的等号由叹号代替。
解密得到flag:SM4foRExcepioN?!
官方文档介绍说这个程序在main函数之前hook了MessageBoxW()函数,之后又调用了这个函数。因此在执行MessageBoxW()前先修改base表并创建VEH向量。执行函数后注册SEH并触发异常。在VEH中构造轮秘钥并注册UnhandledExceptionFilter。在SEH中进行SM4加密。在UnhandledExceptionFilter中修改对比结果并进行变种base64。最后执行比较函数。挺好的,都是中国字==我看懂了。
[UTCTF2020]babymips
没啥技术含量。
得到flag:mips_cpp_gang_5VDm:~`N]ze;\)5%vZ=C'C(r#$q=*efD"ZNY_GX>6&sn.wF8$v*mvA@'
[GKCTF 2021]QQQQT
根据字符串找到base58加密
得到flag:12t4tww3r5e77
[NPUCTF2020]你好sao啊
这题有意思,需要对给定的数据进行base64加密,然后才可以得到结果
得到flag:w0w+y0U+cAn+r3lllY+dAnc3
[WUSTCTF2020]funnyre
五个花指令和1000+行的屎山,没什么技术含量
得到flag:1dc20f6e3d497d15cef47d9a66d6f1af
[GUET-CTF2019]encrypt
先魔改RC4,再魔改base64:
魔改RC4部分的Sbox可以通过动调get_key()函数获得,sub_4007DB()函数如下:
这个部分比较简单,逆的时候抄下来跑一边就行。而魔改base64如下:
可以发现它采用的base表是以=开头的,依照ascii码递增的64位字符串,即
=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
可以写出代码:
#include <iostream>
using namespace std;
int main()
{
int box[999] = {
176, 49, 117, 112, 248, 223, 7, 60, 120, 113, 80, 41, 44, 22, 105, 18, 200, 43, 59, 127, 178,
231, 75, 104, 140, 197, 166, 21, 3, 88, 71, 4, 19, 141, 135, 38, 9, 237, 23, 138, 194, 242, 67, 192,
172, 89, 151, 245, 63, 103, 94, 57, 134, 213, 114, 97, 218, 247, 1, 5, 139, 195, 177, 119, 175, 29,
48, 198, 69, 14, 95, 238, 174, 240, 40, 206, 205, 167, 155, 42, 25, 72, 8, 68, 32, 254, 109, 181,
46, 106, 241, 52, 188, 30, 62, 204, 65, 146, 216, 189, 165, 232, 77, 10, 73, 13, 162, 250, 98, 116,
212, 131, 150, 148, 61, 203, 24, 99, 153, 70, 202, 183, 142, 207, 251, 163, 108, 126, 81, 39, 96,
154, 17, 243, 92, 110, 186, 66, 118, 47, 239, 191, 33, 170, 228, 214, 27, 85, 125, 190, 234, 211,
16, 244, 199, 74, 35, 121, 132, 164, 28, 171, 20, 219, 76, 58, 184, 82, 236, 55, 56, 182, 210, 160,
90, 91, 152, 102, 84, 158, 78, 79, 180, 196, 201, 208, 37, 156, 128, 222, 45, 6, 34, 11, 145, 107,
159, 246, 230, 226, 193, 15, 147, 144, 123, 157, 143, 221, 229, 101, 53, 173, 169, 220, 130, 187,
0, 83, 209, 168, 51, 233, 64, 26, 255, 161, 149, 54, 217, 235, 137, 227, 124, 115, 133, 136, 122,
224, 253, 100, 12, 87, 50, 179, 185, 31, 215, 252, 129, 225, 2, 249, 93, 86, 111, 36
};
int ans[99] = {118, 53, 253, 245, 125, 71, 254, 149, 19, 122, 38, 89, 63, 255, 49, 161, 133, 124, 99, 2, 110, 189, 147, 106, 62, 77, 141, 215, 39, 115, 45, 94, 204, 98, 242, 223, 229, 210, 0};
int i, j = 0, k = 0;
int v7, v8;
for(i = 0; i < 38; i++)
{
j = (j + 1) & 0xff;
v7 = box[j];
k = (k + v7) & 0xff;
v8 = box[k];
box[j] = v8;
box[k] = v7;
ans[i] ^= (box[(v7 + v8) & 0xff] & 0xff);
printf("%c", ans[i]);
}
return 0;
}
得到flag:e10adc3949ba59abbe56e057f20f883e
[WMCTF2020]easy_re
本题采用没听过的perl语言编写,在通过perlapp编译。此类文件会在运行前先将代码解压,然后再执行。
再解压前会有script关键字,同时解压后的代码会存在rax中。通过x64dbg看到:
复制出来是一段perl的代码:
$flag = "WMCTF{I_WAnt_dynam1c_F1ag}";
print "please input the flag:";
$line = <STDIN>;
chomp($line);
if($line eq $flag){
print "congratulation!"
}else{
print "no,wrong"
}
得到flag:I_WAnt_dynam1c_F1ag
[CISCN2018]2ex
魔改base64,但是题目给的输出多了一个"|",不是很理解。
得到flag:change53233
[RoarCTF2019]polyre
很牛逼的一道题。打开看见ollvm混淆,夹杂了控制流平坦化和虚假控制流。采用脚本去除后看见:
P.S.官方题解给出的debcf脚本在IDA7.7中已无法运行,需要自己更新一下,或者用我的。
可以看见它又一些莫名其妙地语句。整体算法发现是可逆的,如果数据大于0,执行后必定为偶数,否则必定为奇数。可以写出代码:
#include <iostream>
using namespace std;
int main()
{
unsigned long long ans[10] = {0xbc8ff26d43536296, 0x520100780530ee16, 0x4dc0b5ea935f08ec, 0x342b90afd853f450, 0x8b250ebcaa2c3681, 0x55759f81a2c68ae4};
int i, j;
for(i = 0; i < 6; i++)
{
for(j = 0; j < 64; j++)
{
if(ans[i] & 1)
{
ans[i] = (ans[i] ^ 0xB0004B7679FA26B3) / 2;
ans[i] |= 0x8000000000000000; //强制给它置为负数
}
else
{
ans[i] /= 2;
}
}
for(j = 0; j < 8; j++)
{
printf("%c", ans[i] & 0xff);
ans[i] >>= 8;
}
}
return 0;
}
运行得到flag:6ff29390-6c20-4c56-ba70-a95758e3d1f8
[watevrCTF 2019]Timeout
通过010 editor或者exeinfoPE可以知道这家伙是一个elf文件。把.com后缀删掉拖到IDA里,然后翻一翻各种函数,可以找到flag。
得到flag:3ncrytion_is_overrated_youtube.com/watch?v=OPf0YbXqDm0
[SCTF2019]babyre
非常傻逼的题目。
首先干掉4个花指令。
然后处理三组加密。第一个走迷宫,第二个硬爆破,第三个嗯逆。
//爆破
#include <iostream>
using namespace std;
int main()
{
int str[700] = {
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 62, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 63, 0,
0, 0, 52, 0, 0, 0, 53, 0, 0, 0,
54, 0, 0, 0, 55, 0, 0, 0, 56, 0,
0, 0, 57, 0, 0, 0, 58, 0, 0, 0,
59, 0, 0, 0, 60, 0, 0, 0, 61, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 64, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 2, 0,
0, 0, 3, 0, 0, 0, 4, 0, 0, 0,
5, 0, 0, 0, 6, 0, 0, 0, 7, 0,
0, 0, 8, 0, 0, 0, 9, 0, 0, 0,
10, 0, 0, 0, 11, 0, 0, 0, 12, 0,
0, 0, 13, 0, 0, 0, 14, 0, 0, 0,
15, 0, 0, 0, 16, 0, 0, 0, 17, 0,
0, 0, 18, 0, 0, 0, 19, 0, 0, 0,
20, 0, 0, 0, 21, 0, 0, 0, 22, 0,
0, 0, 23, 0, 0, 0, 24, 0, 0, 0,
25, 0, 0, 0, 127, 0, 0, 0, 127, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 26, 0,
0, 0, 27, 0, 0, 0, 28, 0, 0, 0,
29, 0, 0, 0, 30, 0, 0, 0, 31, 0,
0, 0, 32, 0, 0, 0, 33, 0, 0, 0,
34, 0, 0, 0, 35, 0, 0, 0, 36, 0,
0, 0, 37, 0, 0, 0, 38, 0, 0, 0,
39, 0, 0, 0, 40, 0, 0, 0, 41, 0,
0, 0, 42, 0, 0, 0, 43, 0, 0, 0,
44, 0, 0, 0, 45, 0, 0, 0, 46, 0,
0, 0, 47, 0, 0, 0, 48, 0, 0, 0,
49, 0, 0, 0, 50, 0, 0, 0, 51, 0,
0, 0, 127, 0, 0, 0, 127, 0, 0, 0,
127, 0, 0, 0, 127, 0, 0, 0, 127, 0
};
int i, j, k, p, q, r;
int ans[5] = {0x736374, 0x665f39, 0x313032};
for(i = 0; i < 3; i++)
{
for(j = 32; j < 126; j++)
{
for(k = 32; k < 126; k++)
{
for(p = 32; p < 126; p++)
{
for(q = 32; q < 126; q++)
{
if((((((str[j * 4] & 0x3f) << 6) | (str[k * 4] & 0x3f)) << 6 | (str[p * 4] & 0x3f)) << 6 | (str[q * 4] & 0x3f)) == ans[i])
{
if(str[j * 4] != 64 && str[k * 4] != 64 && str[p * 4] != 64 && str[q * 4] != 64)
{
printf("%c%c%c%c", j, k, p, q);
}
}
}
}
}
}
}
return 0;
}
//嗯逆
#include <iostream>
#include <cstdlib>
using namespace std;
int __ROL4__(unsigned int x, int k)
{
return (x << k) | (x >> (32 - k));
}
int __ROR4__(unsigned int x, int k)
{
return (x >> k) | (x << (32 - k));
}
int func(int x)
{
int data[2000] = {
214, 0, 0, 0, 144, 0, 0, 0, 233, 0,
0, 0, 254, 0, 0, 0, 204, 0, 0, 0,
225, 0, 0, 0, 61, 0, 0, 0, 183, 0,
0, 0, 22, 0, 0, 0, 182, 0, 0, 0,
20, 0, 0, 0, 194, 0, 0, 0, 40, 0,
0, 0, 251, 0, 0, 0, 44, 0, 0, 0,
5, 0, 0, 0, 43, 0, 0, 0, 103, 0,
0, 0, 154, 0, 0, 0, 118, 0, 0, 0,
42, 0, 0, 0, 190, 0, 0, 0, 4, 0,
0, 0, 195, 0, 0, 0, 170, 0, 0, 0,
68, 0, 0, 0, 19, 0, 0, 0, 38, 0,
0, 0, 73, 0, 0, 0, 134, 0, 0, 0,
6, 0, 0, 0, 153, 0, 0, 0, 156, 0,
0, 0, 66, 0, 0, 0, 80, 0, 0, 0,
244, 0, 0, 0, 145, 0, 0, 0, 239, 0,
0, 0, 152, 0, 0, 0, 122, 0, 0, 0,
51, 0, 0, 0, 84, 0, 0, 0, 11, 0,
0, 0, 67, 0, 0, 0, 237, 0, 0, 0,
207, 0, 0, 0, 172, 0, 0, 0, 98, 0,
0, 0, 228, 0, 0, 0, 179, 0, 0, 0,
28, 0, 0, 0, 169, 0, 0, 0, 201, 0,
0, 0, 8, 0, 0, 0, 232, 0, 0, 0,
149, 0, 0, 0, 128, 0, 0, 0, 223, 0,
0, 0, 148, 0, 0, 0, 250, 0, 0, 0,
117, 0, 0, 0, 143, 0, 0, 0, 63, 0,
0, 0, 166, 0, 0, 0, 71, 0, 0, 0,
7, 0, 0, 0, 167, 0, 0, 0, 252, 0,
0, 0, 243, 0, 0, 0, 115, 0, 0, 0,
23, 0, 0, 0, 186, 0, 0, 0, 131, 0,
0, 0, 89, 0, 0, 0, 60, 0, 0, 0,
25, 0, 0, 0, 230, 0, 0, 0, 133, 0,
0, 0, 79, 0, 0, 0, 168, 0, 0, 0,
104, 0, 0, 0, 107, 0, 0, 0, 129, 0,
0, 0, 178, 0, 0, 0, 113, 0, 0, 0,
100, 0, 0, 0, 218, 0, 0, 0, 139, 0,
0, 0, 248, 0, 0, 0, 235, 0, 0, 0,
15, 0, 0, 0, 75, 0, 0, 0, 112, 0,
0, 0, 86, 0, 0, 0, 157, 0, 0, 0,
53, 0, 0, 0, 30, 0, 0, 0, 36, 0,
0, 0, 14, 0, 0, 0, 94, 0, 0, 0,
99, 0, 0, 0, 88, 0, 0, 0, 209, 0,
0, 0, 162, 0, 0, 0, 37, 0, 0, 0,
34, 0, 0, 0, 124, 0, 0, 0, 59, 0,
0, 0, 1, 0, 0, 0, 33, 0, 0, 0,
120, 0, 0, 0, 135, 0, 0, 0, 212, 0,
0, 0, 0, 0, 0, 0, 70, 0, 0, 0,
87, 0, 0, 0, 159, 0, 0, 0, 211, 0,
0, 0, 39, 0, 0, 0, 82, 0, 0, 0,
76, 0, 0, 0, 54, 0, 0, 0, 2, 0,
0, 0, 231, 0, 0, 0, 160, 0, 0, 0,
196, 0, 0, 0, 200, 0, 0, 0, 158, 0,
0, 0, 234, 0, 0, 0, 191, 0, 0, 0,
138, 0, 0, 0, 210, 0, 0, 0, 64, 0,
0, 0, 199, 0, 0, 0, 56, 0, 0, 0,
181, 0, 0, 0, 163, 0, 0, 0, 247, 0,
0, 0, 242, 0, 0, 0, 206, 0, 0, 0,
249, 0, 0, 0, 97, 0, 0, 0, 21, 0,
0, 0, 161, 0, 0, 0, 224, 0, 0, 0,
174, 0, 0, 0, 93, 0, 0, 0, 164, 0,
0, 0, 155, 0, 0, 0, 52, 0, 0, 0,
26, 0, 0, 0, 85, 0, 0, 0, 173, 0,
0, 0, 147, 0, 0, 0, 50, 0, 0, 0,
48, 0, 0, 0, 245, 0, 0, 0, 140, 0,
0, 0, 177, 0, 0, 0, 227, 0, 0, 0,
29, 0, 0, 0, 246, 0, 0, 0, 226, 0,
0, 0, 46, 0, 0, 0, 130, 0, 0, 0,
102, 0, 0, 0, 202, 0, 0, 0, 96, 0,
0, 0, 192, 0, 0, 0, 41, 0, 0, 0,
35, 0, 0, 0, 171, 0, 0, 0, 13, 0,
0, 0, 83, 0, 0, 0, 78, 0, 0, 0,
111, 0, 0, 0, 213, 0, 0, 0, 219, 0,
0, 0, 55, 0, 0, 0, 69, 0, 0, 0,
222, 0, 0, 0, 253, 0, 0, 0, 142, 0,
0, 0, 47, 0, 0, 0, 3, 0, 0, 0,
255, 0, 0, 0, 106, 0, 0, 0, 114, 0,
0, 0, 109, 0, 0, 0, 108, 0, 0, 0,
91, 0, 0, 0, 81, 0, 0, 0, 141, 0,
0, 0, 27, 0, 0, 0, 175, 0, 0, 0,
146, 0, 0, 0, 187, 0, 0, 0, 221, 0,
0, 0, 188, 0, 0, 0, 127, 0, 0, 0,
17, 0, 0, 0, 217, 0, 0, 0, 92, 0,
0, 0, 65, 0, 0, 0, 31, 0, 0, 0,
16, 0, 0, 0, 90, 0, 0, 0, 216, 0,
0, 0, 10, 0, 0, 0, 193, 0, 0, 0,
49, 0, 0, 0, 136, 0, 0, 0, 165, 0,
0, 0, 205, 0, 0, 0, 123, 0, 0, 0,
189, 0, 0, 0, 45, 0, 0, 0, 116, 0,
0, 0, 208, 0, 0, 0, 18, 0, 0, 0,
184, 0, 0, 0, 229, 0, 0, 0, 180, 0,
0, 0, 176, 0, 0, 0, 137, 0, 0, 0,
105, 0, 0, 0, 151, 0, 0, 0, 74, 0,
0, 0, 12, 0, 0, 0, 150, 0, 0, 0,
119, 0, 0, 0, 126, 0, 0, 0, 101, 0,
0, 0, 185, 0, 0, 0, 241, 0, 0, 0,
9, 0, 0, 0, 197, 0, 0, 0, 110, 0,
0, 0, 198, 0, 0, 0, 132, 0, 0, 0,
24, 0, 0, 0, 240, 0, 0, 0, 125, 0,
0, 0, 236, 0, 0, 0, 58, 0, 0, 0,
220, 0, 0, 0, 77, 0, 0, 0, 32, 0,
0, 0, 121, 0, 0, 0, 238, 0, 0, 0,
95, 0, 0, 0, 62, 0, 0, 0, 215, 0,
0, 0, 203, 0, 0, 0, 57, 0, 0, 0,
72, 0, 0, 0, 198, 0, 0, 0, 186, 0,
0, 0, 177, 0, 0, 0, 163, 0, 0, 0,
80, 0, 0, 0, 51, 0, 0, 0, 170, 0,
0, 0, 86, 0, 0, 0, 151, 0, 0, 0,
145, 0, 0, 0, 125, 0, 0, 0, 103, 0,
0, 0, 220, 0, 0, 0, 34, 0, 0, 0,
112, 0, 0, 0, 178, 0, 0, 0, 0, 0
};
int v2;
v2 = data[(x & 0xff) * 4];
x >>= 8;
v2 |= data[(x & 0xff) * 4] << 8;
x >>= 8;
v2 |= data[(x & 0xff) * 4] << 16;
x >>= 8;
v2 |= data[(x & 0xff) * 4] << 24;
return __ROL4__(v2, 12) ^ (__ROL4__(v2, 8) ^ __ROR4__(v2, 2)) ^ __ROR4__(v2, 6);
}
int main()
{
int ans[20];
ans[0] = 190;
ans[1] = 4;
ans[2] = 6;
ans[3] = 128;
ans[4] = 197;
ans[5] = 175;
ans[6] = 118;
ans[7] = 71;
ans[8] = 159;
ans[9] = 204;
ans[10] = 64;
ans[11] = 31;
ans[12] = 216;
ans[13] = 191;
ans[14] = 146;
ans[15] = 239;
int loop[50];
int i;
for(i = 0; i < 4; i++)
{
loop[i] = (ans[12 - i * 4] << 24) + (ans[12 - 4 * i + 1] << 16) + (ans[12 - i * 4 + 2] << 8) + ans[12 - i * 4 + 3];
}
for(i = 4; i < 30; i++)
{
loop[i] = loop[i - 4] ^ func(loop[i - 3] ^ loop[i - 2] ^ loop[i - 1]);
}
for (int i = 29; i >= 26; i--)
{
printf("%c%c%c%c", ((char*)&loop[i])[0], ((char*)&loop[i])[1], ((char*)&loop[i])[2], ((char*)&loop[i])[3]);
}
return 0;
}
血的教训:写循环位移要定义成unsigned类型。不unsigned的话,右移时整数补0,负数补1,然后就错啦!
得到flag:ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)
[ACTF新生赛2020]fungame
题出的很好,下次别出了。
见面看到一个加密,这个很简单。
解出来交上去就可以发现错啦!
看看下一个函数:
这个函数很神奇,它把16字节的东西赋值给了12字节的空间,这会造成栈溢出。对,这其实是一个pwn题。
跟踪x可以发现
于是解得flag:Re_1s_So0_funny!=#@a1s0_pWn
[羊城杯 2020]Bytecode
傻逼python字节码,有种硬读汇编的美。抄一个脚本。
from z3 import *
en = [3, 37, 72, 9, 6, 132]
output = [101, 96, 23, 68, 112, 42, 107, 62, 96, 53, 176, 179, 98, 53, 67, 29, 41, 120, 60, 106, 51, 101, 178, 189, 101, 48]
flag = ''
s = Solver()
a1 = Int('a1')
a2 = Int('a2')
a3 = Int('a3')
a4 = Int('a4')
a5 = Int('a5')
a6 = Int('a6')
s.add(a1 * 3 + a2 * 2 + a3 * 5 == 1003)
s.add(a1 * 4 + a2 * 7 + a3 * 9 == 2013)
s.add(a1 + a2 * 8 + a3 * 2 == 1109)
s.add(a4 * 3 + a5 * 2 + a6 * 5 == 671)
s.add(a4 * 4 + a5 * 7 + a6 * 9 == 1252)
s.add(a4 + a5 * 8 + a6 * 2 == 644)
if s.check():
print(s.model())
k=0
for i in range(13):
flag += chr(output[k+1] ^ en[i%6])
flag += chr(output[k] ^ en[i%6])
k = k + 2
s = [97, 101, 102, 102, 55, 51]
for i in range(6):
flag += chr(s[i])
print(flag)
得到flag:cfa2b87b3f746a8f0ac5c5963faeff73
[FlareOn2]very_success
打开一看函数这么少还以为它加壳了呢,没想到它就是这么少。
逻辑很简单,通过动调可以发现v11始终为1。写出脚本:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int Xor[100] = {
170, 236, 164, 186, 175, 174, 170, 138, 192, 167,
176, 188, 154, 186, 165, 165, 186, 175, 184, 157,
184, 249, 174, 157, 171, 180, 188, 182, 179, 144,
154, 168
};
int i, t = 0, last = 0;
for(i = 0; i < 32; i++)
{
printf("%c", (Xor[32 - i - 1] - (1 << (last & 3)) - 1) ^ 0xC7);
last += Xor[32 - i - 1];
}
return 0;
}
得到flag:a_Little_b1t_harder_plez@flare-on.com
[2019红帽杯]Snake
Unity3D的游戏,去找Assembly-CSharp.dll。可以看见他调用了一个外部的Interface.dll。
在Snake_Data/Plugins文件夹下可以找到对应dll。用IDA打开,找到flag关键字的位置可以看见一坨非常抽象的东西,nnd看不懂一点。
去搜的大佬的博客知道这个东西可以爆破。python的ctypes库允许爆破dll文件中的特定函数。
import ctypes
for i in range(100):
dll = ctypes.cdll.LoadLibrary("./Interface.dll")
print(i)
dll.GameObject(i)
在地19次是可以得到flag:Ch4rp_W1th_R$@
从结果来看,这个b用C++写了一个RSA......只能说不愧是红帽杯......
[SCTF2019]Strange apk
安卓的SMC。打开看见程序的入口点应该是sctf.demo.myapplication.t,但是找不到。
看见这个里面存在代码西修改的部分。它将data文件经过__()函数和_()函数解密(都tm什么函数名字)。
有用的解密代码部分:
写脚本运行
def get_byte(v):
return v.to_bytes(1, byteorder='little')
with open('data', 'rb') as f:
a = f.read()
with open('sctf.apk', 'wb') as g:
for i in range(len(a)):
g.write(get_byte(a[i] ^ ord("syclover"[i % 8])))
打开解密后的文件,这回找得到t部分了,但是看不懂。不过他总共就t、s、f三个有用的,找一找可以才出来加密的流程。下面这一部分在s里。前12个字符由f.sctf()函数加密。
后18个字符由f.encode()函数加密。
后半部分加密后的东西在t中
挺离谱的,我不理解为什么会是这么个执行流程。看大佬的博客说跟安卓的父子组件通信机制有关。
得到flag:W3lc0me~t0_An4r0id-w0rld
[安洵杯 2019]game
OLLVM代码混淆。用脚本除了trace()函数之外都可以反混淆。但是只有三个check函数是有意义的,其余的不用管它。
check1()函数对输入进行了一波操作:
check3()函数就是将输入复制到Dog3[]里再与sudoku[]对比。
因此,我们可以让程序运行起来,下断点之后将sudoku的内容提取出来。
接下来就是写一个脚本的过程:
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
int sudoku[81] = {1,4,5,3,2,7,6,9,8,8,3,9,6,5,4,1,2,7,6,7,2,8,1,9,5,4,3,4,9,6,1,8,5,3,7,2,2,1,8,4,7,3,9,5,6,7,5,3,2,9,6,4,8,1,3,6,7,5,4,2,8,1,9,9,8,4,7,6,1,2,3,5,5,2,1,9,3,8,7,6,4};
int Dog3[81] = {1,0,5,3,2,7,0,0,8,8,0,9,0,5,0,0,2,0,0,7,0,0,1,0,5,0,3,4,9,0,1,0,0,3,0,0,0,1,0,0,7,0,9,0,6,7,0,3,2,9,0,4,8,0,0,6,0,5,4,0,8,0,9,0,0,4,0,0,1,0,3,0,0,2,1,0,3,0,7,0,4};
int input[81] = {};
int i, cnt = 0, tmp;
for(i = 0; i < 81; i++)
{
if(Dog3[i] == 0)
{
input[cnt] = sudoku[i] + 48;
cnt++;
}
}
for(i = 0; i < cnt; i++)
{
input[i] += 20;
input[i] = (input[i] & 0xF3 | ~input[i] & 0xC);
}
for(i = 0; i < cnt; i += 2)
{
tmp = input[i];
input[i] = input[i+1];
input[i+1] = tmp;
}
cnt = 20;
for(i = 0; i < 20; i++)
{
tmp = input[i];
input[i] = input[cnt];
input[cnt] = tmp;
cnt++;
}
for(i = 0; i < 40; i++)
{
printf("%c", input[i]);
}
return 0;
}
运行脚本得到flag:KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J
[GWCTF 2019]babyvm
最瞧不起这种真flag在本地打不通,但假flag能打通的题目。
传统虚拟机逆向。尽管它在执行VMrun函数的时候会跳转到VMrun_another函数很神奇,但并不影响我瞧不起它。我觉得很奇怪,你明明能做到这个,为什么不将VMrun_another函数改成真正需要选手破解的那一段。然后再把VMcheck执行的时候用同样的技术跳转到VMcheck_another函数。这样不就是一个在本地能打通的题目了???
没什么可说的,解题脚本如下:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int enc[30] = {105, 69, 42, 55, 9, 23, 197, 11, 92, 114, 51, 118, 51, 33, 116, 49, 95, 51, 115, 114};
int i, j, tmp;
for(i = 13; i <= 15; i++)
{
tmp = enc[i];
enc[i] = enc[32-i];
enc[32-i] = tmp;
}
for(i = 8; i >= 6; i--)
{
for(j = 32; j < 127; j++)
{
if((((j*3 + enc[i+1]*2 + enc[i+2]) * enc[12]) & 0xff) == enc[i])
{
enc[i] = j;
break;
}
}
}
for(i = 5; i >= 0; i--)
{
enc[i] ^= enc[i+1];
}
for(i = 0; i < 20; i++)
{
printf("%c", enc[i]);
}
return 0;
}
运行得到flag:Y0u_hav3_r3v3rs3_1t!
[网鼎杯 2020 青龙组]bang
安卓脱壳。
使用这个脚本查壳,然后使用模拟器和frida脱壳。
脱壳后会生成两个dex文件,使用dex2jar工具将其转为jar文件。再使用jadx-jui打开jar文件。
可以找到flag:borring_things