题目下载链接:https:pan.baidu.coms1dcHfAhLsxeGzXdnCTDJuuQ?pwdD0g3提取码:D0g3首先个人把这个题目归类为迷宫题目,简称Go语言迷
题目下载
链接:https://pan.baidu.com/s/1dcHfAhLsxeGzXdnCTDJuuQ?pwd=D0g3
提取码:D0g3
首先个人把这个题目归类为迷宫题目,简称Go语言迷宫
首先监测一下exe的信息
发现是Go语言写的64为exe,然后没有TLS回调,没有软件的保护
然后拖入IDA分析
分析的时候,发现,没有main函数
只有类似于
runtime.main
main.main
这样的函数
后来通过IDA和IDC的分析,发现一些启动函数,慢慢的逐层调用到main.main
然后就是分析void __cdecl main_main()
函数,他是主要开始的函数
main.main
函数,其实代码并不多
void __cdecl main_main()
{
__int64 v0; // r14
_QWORD *v1; // rax
__int64 v2; // [rsp-38h] [rbp-78h]
__int64 v3; // [rsp-38h] [rbp-78h]
__int64 v4; // [rsp-38h] [rbp-78h]
__int64 v5; // [rsp-30h] [rbp-70h]
__int64 v6; // [rsp-30h] [rbp-70h]
void *retaddr; // [rsp+40h] [rbp+0h] BYREF
while ( (unsigned __int64)&retaddr <= *(_QWORD *)(v0 + 16) )
runtime_morestack_noctxt();
fmt_Fprintln();
v2 = fmt_Fprintln();
v5 = runtime_newobject(v2);
*v1 = 0LL;
fmt_Fscanf(v3, v5);
//分割线-------------------------------------------------------------
//分析了很久,发现 上面是一些输入的处理
main_convert(v4, v6);//这里就是主要加密的函数,对我们的输入做了一些处理
godeep_tree_ApSzXJOjiFA();//这里就是进入迷宫了
}
然后搜索关键字符串,比如
right
wrong
发现只有一个地方调用了字符串right
很多个地方调用了字符串wrong
,就很奇怪
简单的分析
xx1->xx2->xx3->...->right输出函数
于是直接分析懵逼了,一个无线的套娃,不知道到底该怎么进入right输出函数
我还测试手动追踪那个过程,直接分析麻了,太多函数了,最后放弃手动追踪
然后分析继续分析main函数,第一次进入godeep_tree_ApSzXJOjiFA()
发现函数,啥也没干,
就直接判断,然后对你输出结果"wrong",否则进入下一个函数
没看见输出"right"
void godeep_tree_ApSzXJOjiFA()
{
_BYTE *v0; // rax
__int64 v1; // rcx
__int64 v2; // rbx
__int64 v3; // r14
bool v4; // zf
void *retaddr; // [rsp+8h] [rbp+0h] BYREF
_BYTE *v6; // [rsp+10h] [rbp+8h]
_BYTE *v7; // [rsp+10h] [rbp+8h]
__int64 v8; // [rsp+20h] [rbp+18h]
while ( (unsigned __int64)&retaddr <= *(_QWORD *)(v3 + 16) )
{
v7 = v0;
v8 = v1;
runtime_morestack_noctxt();
v0 = v7;
v1 = v8;
}
v4 = v2 == 0;
if ( !v2 )
{
v6 = v0;
godeep_tree_wrong();
v4 = 1;
v0 = v6;
}
if ( v4 )
runtime_panicIndex();
if ( *v0 )
godeep_tree_QBLerFck();
else
godeep_tree_PeVbW();
}
然后继续分析,发现感觉就像是无线的套娃,但是又不像是递归调用,因为每个函数的名字都不一样
然后放弃分析,直接动态调试
动态调试也很g,它时不时的监测反调试,让后修改你的RIP寄存器,然后调试的时候就直接让人崩溃
我测试动态调试了一段时间,反正调试麻了,exe动不动就监测我在调试
只要我F7/F8,我就麻了,它修改我的RIP,然后我每次都要sei ip,把IP给恢复回来
对于它的手段,我很难破解,对Go不了解
动态分析发现了一些东西,但是还是人麻了,没有太多的收获
分析结果:
每次函数的进入,都依赖于某个值的判断
if ( !v2 )//决定你要失败的情况
{
v6 = v0;
godeep_tree_wrong();
v4 = 1;
v0 = v6;
}
if ( *v0 )//根据标志v0来判断进入哪一个函数
godeep_tree_QBLerFck();
else
godeep_tree_PeVbW();
v0标志是一个数组的地址,那个数组全是01的二进制数组是char类型,每个字节都是0或者1
然后每次函数的进入,都会推动数组v0的遍历
类似于
if ( *v0 )//根据标志v0来判断进入哪一个函数
v0++;
godeep_tree_QBLerFck();
else
v0++;
godeep_tree_PeVbW();
当然,每一次函数的进入还做了其它变量的修改,我没有分析出来
但是竟然没有影响最后flag的获取,运气好
于是又去分析main函数,去探究那个01的数据流是哪里来的,多半和我们的flag输入有一定的关系
进入main函数,分析main_convert.然后还是人麻了,没有太多的收获,主要是没看见下面的操作
+ - * / & | % & 发现main_convert基本没有用到这些运算
于是人麻了,看不出来main_convert
在干嘛
然后百度了一下涉及的函数
比如简单分析了一下下面的函数
runtime_decoderune(v23);
runtime_convT32(v23);
fmt_Sprintf(v23, v25, v28, v30, v31); // 格式化字符串转化函数,涉及字符串 ; "%08b"
runtime_stringtoslicebyte(v24, v26, v29);
主要对GO语言不太了解,百度了相关的函数,也还是不理解,感觉函数有点复杂,但是这些函数应该和编码有关系
然后我就继续分析,直接放弃main_convert
的分析,直接把它当作黑盒子,这个黑盒子可能生成了01的数据流
看一下我们传递进去的数据和我们传递出来的数据有什么联系
我先测试数据
输入字符串'ABC',发现生成了3个qword
输入字符串'AB',发现生成了2个qword
输入字符串'A',发现生成了1个qword
当我输入1字节字符串的时候,然后分析那个qword,8字节,然后发现
那8个字节是字母'A'的bit位,好比01000001
于是猜想,main_convert
是把我们的每个flag[i]转化为了8字节的bit流
然后输入字母'B',发现生成8字节,是'001000010`
输入字符串'ABC',发现生成了3个qword,是字符串ABC
的二进制流
这样的话,我们就很明白了
题目把我们的字符串分解为01数据流,然后根据01决定我们进入哪一个函数
看起来就像是一个迷宫
但是问题来了,我们怎么知道去往flag迷宫终点呢?
之前手动分析了如果追踪flag迷宫终点
手动分析了34个函数,就直接放弃了,太多了
于是我想起来使用IDC脚本,IDC我以前从lyshark博客那里学了一些
但是只会copy IDC的脚本使用,不会手写
但是,这一次我必须得学会手写IDC,否则没办法做题
于是在lyshark博客的帮助下,我成功拿到了迷宫终点的路径
lyshark IDC 博客
https://www.cnblogs.com/LyShark/p/13100048.html
然后下面是我自己写的IDC脚本,不可以直接使用
需要在在已经有的基本IDC基础上,把right
输出的终点函数名字修改为flag
我只是喜欢把终点函数叫flag
,然后可以跑出路径
ICD脚本+地图终点路径 在Github存放着,旁边点击下载
https://gist.github.com/re4mile/c459aa433407461d960e72c3df51516c
于是我拿着去往地图终点的路径尝试手动翻译01,前往
然后手搓来288位,flag出来了,288位,手搓错了.woc
不可能再去检查了,因为288位的0和1本来就人麻了.所以我还是尝试写一个IDC脚本
打印一个去往地图终点的01路径
在lyshark博客的帮助下,成功写出了打印终点路径的IDC
ICD脚本+获取flag的代码 在Github存放着,旁边点击下载
https://gist.github.com/re4mile/a090ac548e4641ff6c2e311f804bd311
最后就ok了
fc03bd97-ff7b-419f-8987-78bc745d3b0a