十月份安恒杯两道逆向题
本帖最后由 yechen123 于 2018-11-21 20:59 编辑这两题目不怎么难主要看耐心
篇幅可能有点长 可能会有错误 望见谅
先上传题目
没有cb的看这里
链接: https://pan.baidu.com/s/1vptBYyCO4Woyjk4mEvVu8A 提取码: eqmt
1.easytree
看题目就知道应该是考察树的知识
下载下来没exe后缀名以为是linux文件用winhex打开才发现是pe文件
加个exe后缀名能正常打开
查壳发现有upx壳
用吾爱脱壳机脱壳
脱壳后用ida打开
两个函数里面的代码略乱
此时打开 软件看看
ida 搜索字符串
定位关键点
附上主要代码
int Good_job()
{
const char *v0; // eax@4
int v2; // @1
char v3; // @4
char Str; // @1
int v5; // @4
int *v6; // @1
sub_402400();
v2 = 0;
v6 = &v2;
printHello();
gets(&Str);
if ( strlen(&Str) != 15 )
{
printf("Nah... ur a fake reverser!");
exit(0);
}
v5 = sub_401500((int)&Str, 15); // 创建结构
sub_40166E(v5, v6, (int)&v3); // v5是根节点的首地址
v0 = sub_401794(&v3); // icnerrseaetrvee
if ( !strncmp(v0, "aWNuZXJyc2VhZXRydmVl", 0x14u) )
puts("well done bro!");
printf("ur really know something about tree!");
}
else
{
printf("Nah.. ur an idiot!");
}
return 0;
}
sub_401500 sub_40166e sub_401794这几个函数比较可疑
先看看最后一个
显得有点复杂在结合下面的代码
if ( !strncmp(v0, "aWNuZXJyc2VhZXRydmVl", 0x14u) )
大胆预测这是base64加密
aWNuZXJyc2VhZXRydmVl 这是密文解密得到icnerrseaetrvee
再看看sub_401500这个函数
看起来像是创建树结构的代码
还好这几天学完树和二叉树这一章了
大概看了下这个代码会给每个节点创建12个字节的空间前面四个字节储存自己的数据中间四个字节储存左孩子的地址最后四个字节是储存右孩子的地址
前面有限制我们只能输入15个字符比如输入abcdefghijklmno
那么他的储存结构是这样子的(输入时候不小心换成大写了望见谅)
再看看 sub_40166E这个函数
int __cdecl sub_40166E(int input, _DWORD *v6, int a3)
{
int v3; // eax@2
int result; // eax@2
if ( input )
{
v3 = (*v6)++;
*(_BYTE *)(a3 + v3) = *(_BYTE *)input;
sub_40166E(*(_DWORD *)(input + 4), v6, a3);
result = sub_40166E(*(_DWORD *)(input + 8), v6, a3);
}
return result;
}
这个大概是遍历二叉树的代码遍历分为前序遍历 中序遍历 后序遍历 这个应该是前序遍历
好了 上面我们得到的base64解密代码是icnerrseaetrvee
那么可以根据他创建二叉树的结构 以及根据前序遍历的规则得出树结构
那么可以得到flag为 icanreversetree
第二题 BASE++
这道题跟九月份的有点像有兴趣的可以看一下我前面发的九月份安恒杯月赛的帖子
说明跟base加密有关
可能改变了一下
文件给了exe和一个ida文件 直接打开ida文件
直接上代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
int v4; //
int v5; //
sub_401790(std::cout, "please input your flag:");
sub_4019D0(std::cin, &input_flag);
if ( (signed int)strlen((const char *)&input_flag) < 32 )// 小于32位
goto LABEL_14;
transform_1((const char *)&input_flag);
v5 = strcmp((const char *)&input_flag, "guvf_vf_n_snxr_synt_____________");
if ( v5 )
v5 = -(v5 < 0) | 1;
if ( v5 )
{
LABEL_14:
transform_2();
transform_3(strlen((const char *)&input_flag), (int)&input_flag, (int)a0);
v4 = strcmp(a0, "TRLT5amLBoLT5Z6Fa5LqN6mkTomqR66Da4LqX5mgBwkkP5wmTZ6D====");
if ( v4 )
v4 = -(v4 < 0) | 1;
if ( v4 )
sub_401790(std::cout, "soooooooooorry\n");
else
sub_401790(std::cout, "Congratulations!!!\n");
system("pause");
result = 0;
}
else
{
sub_401790(std::cout, "try harder!\n");
result = 0;
}
return result;
}
要是输入的字符串小于32直接跳到下边的if里边
先看看if里边的东西
主要涉及函数
transform_2
transform_3
然后加密过后的字符串必须为"TRLT5amLBoLT5Z6Fa5LqN6mkTomqR66Da4LqX5mgBwkkP5wmTZ6D===="
transform_2 没啥好看的 主要生成 "NoPqRsTuVwXyZaBcDeFgHiJkLm765432"这个密码表有兴趣对的可以看一下
看下transform_3
int __usercall transform_3@<eax>(int input_len@<edx>, int input_flag@<ecx>, int a3)
{
int v3; // esi
_BYTE *a0_; // edi
int input_lens; // ebx
int v7; //
int input__len; //
v7 = input_flag;
v3 = 0;
input__len = input_len;
if ( input_len > 0 )
{
a0_ = (_BYTE *)a3; // a0
input_lens = input_len;
do
{
base_tran_5(v3 + input_flag, input_lens, a0_);
input_flag = v7;
v3 += 5; // step 5 每次进多5步
input_lens -= 5;
a0_ += 8;
}
while ( v3 < input__len );
}
return a3;
}
主要逻辑是每次把字符串五个字符 和总长度 和密码表传入base_tran_5主要由这个函数加密
每次执行完字符串长度减5
看看这个函数内部
取每个字符 经过简单的运算得到一个数值 在用这个数值当做序号取出密码表的值当做密文
最终 到了字符串末尾 看是否是8的倍数 不是就补'='补够为止
密文已经给出
那么就可以根据密文求出序号再求出明文
在这里给一下思路该代码每次使用五个字符
比如我输入
abcdefghijklmn
那么先取abcde
在内存中就是6162636465
eax只存62636465edx存00000061
6162636465 换成二进制就是
0110000101100010011000110110010001100101
每五个位分成一组
01100 00101 10001 00110 00110 11001 00011 00101
每一组换成十进制后当成序号取密码表中的值换成密文
比如第一组 01100 换成10进制就是12
换成密文就是Z
也就是每五个明文可以换成8个密文
知道具体原理就可以解密了
用python写脚本asciis = ['N', 'o', 'P', 'q', 'R', 's', 'T', 'u', 'V', 'w', 'X', 'y', 'Z', 'a', 'B', 'c', 'D', 'e', 'F', 'g', 'H', 'i', 'J', 'k', 'L', 'm', '7', '6', '5', '4', '3', '2']
encrypt = ['T', 'R', 'L', 'T', '5', 'a', 'm', 'L', 'B', 'o', 'L', 'T', '5', 'Z', '6', 'F', 'a', '5', 'L', 'q', 'N', '6', 'm', 'k', 'T', 'o', 'm', 'q', 'R', '6', '6', 'D', 'a', '4', 'L', 'q', 'X', '5', 'm', 'g', 'B', 'w', 'k', 'k', 'P', '5', 'w', 'm', 'T', 'Z', '6', 'D', '=', '=', '=', '=']
flag =
base =
flags = ""
flagq = ""
for i in range(0,55,8):
base = i
base = i + 1
base = i + 2
base = i + 3
base = i + 4
base = i + 5
base = i + 6
base = i + 7
flag = 0
flag = 0
flag = 0
flag = 0
flag = 0
flag = 0
flag = 0
flag = 0
for q in range(0,8):
for f in range(0,32):
if (encrypt]==asciis):
flag = f
flag = flag<<35
flag = flag<<30
flag = flag<<25
flag = flag<<20
flag = flag<<15
flag = flag<<10
flag = flag<<5
temp = "%x"%(flag+flag+flag+flag+flag+flag+flag+flag)
flags += temp
for i in range(0, len(flags), 2):
flagq += chr(int(flags, 16))
print flagq
得到10n78ppn3ro00o70r2opop5s3roqq937
但是 输入却不对 用od看看
经过这个函数输入的字符串被替换了只替换小写的
用ida看看
signed int __thiscall transform_1(const char *this)
{
const char *input_; // edi
unsigned int v2; // esi
char v3; // cl
input_ = this;
v2 = 0;
if ( strlen(this) )
{
do
{
v3 = input_;
if ( (unsigned __int8)(v3 - 97) <= 25u )// 如果是小写
input_ = (v3 - 84) % 26 + 97;
if ( (unsigned __int8)(v3 - 65) <= 25u )
input_ = (v3 - 52) % 26 + 65;
++v2;
}
while ( v2 < strlen(input_) );
}
return 1;
}
因为我们输入的是小写和数字 看第一个if就行看起来就是整个小写字符串表以为
写个代码
果然是移位
那么 刚刚我od里面看到了经过这个函数移位之后的字符串你可以直接输入这个字符串就可以了
或者像我一样写个脚本 反正结果都是一样的
key = "10n78ppn3ro00o70r2opop5s3roqq937"
asciis = []
g = []
for i in range(97,123):
asciis.append(chr(((i-84)%26)+97))#生成移位之后的表
for i in range(0,32):
if (97<=ord(key)<=122):#小写才用改数字就算了
temp = ord(key)-97
g.append(asciis)
continue
g.append(key)
flag = ""
for i in g:
flag += i
print (flag)
得到10a78cca3eb00b70e2bcbc5f3ebdd937
100分的那题:1.输入一段字符串,在OD在看字符串 根据换位规则 反解
2.就是base64解密
200分的那题:1.一个小算法(不知道算法名)
2.base32改过算法表,如果懒得写b32,用加密或者解密后,按照位置替换字符也可以。 yechen123 发表于 2018-11-12 20:40
考虑进去了,这个作用是把输入字符移位相加在根据值从密码表取值
{:1_909:}我把移位的那段直接爆出来发现就是10n78ppn3ro00o70r2opop5s3roqq937 大佬。。。。。。 太强了,我第二道题连思路都没搞懂。 学到了学到了 技术贴 技术牛 学到了 学到了 大········大佬 lihaohua 发表于 2018-10-29 11:08
大········大佬
不不,师傅才是真正的大佬 学习一下 学习中,不错不错,