CG CTF WxyVM
## 吾爱破解,初来报道!(可能有些地方分析得不太正确,还望多宽(容)待!)这道题我做了两天,卡死在了写脚本上了,不断修改脚本,过程中学到了挺多!
<!-- more-->
## 寻找关键:
> 记事本打开发现是elf文件,然后在ubuntu中运行,提示让我们输入flag,随便输入,回复我们:wrong于是拖入IDA F12
```
LOAD:0000000000400238 0000001C C /lib64/ld-linux-x86-64.so.2
LOAD:0000000000400349 0000000A C libc.so.6
LOAD:0000000000400353 00000005 C puts
LOAD:0000000000400358 00000007 C strlen
LOAD:000000000040035F 00000006 C scanf
LOAD:0000000000400365 00000012 C __libc_start_main
LOAD:0000000000400377 0000000F C __gmon_start__
LOAD:0000000000400386 0000000C C GLIBC_2.2.5
.rodata:0000000000400848 0000000E C
.rodata:0000000000400856 00000011 C input your flag:
.rodata:000000000040086A 00000008 C correct
.rodata:0000000000400872 00000006 C wrong
.eh_frame:000000000040091F 00000006 C ;*3$\"
```
> 找到input your flag 鼠标双击它 来到它的位置,然后按X查看哪里引用它了
然后就来到了main函数了 F5 看下主函数的反编译
```
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4; //
signed int i; //
puts("");
puts("input your flag:");
scanf("%s", &byte_604B80); // 输入字符串存在 604B80处
v4 = 1;
sub_4005B6();
if ( strlen(&byte_604B80) != 24 ) // 604B80处的字符串长度为24
v4 = 0;
for ( i = 0; i <= 23; ++i )
{
if ( *(&byte_604B80 + i) != dword_601060 )// 604B80处的每个字节 与601060处每个双字的最后两位(即字节)要相等
v4 = 0;
}
if ( v4 )
puts("correct");
else
puts("wrong");
return 0LL;
}
```
> 这里注意下601060存的是24个双字类型的数据,所以与604B80(存的是字节类型)做比较时 只需比较的是601060处每个双字的最后两位(即字节)<br>
我们再看下 sub_4005B6()函数做了什么
```
__int64 sub_4005B6()
{
unsigned int v0; // ST04_4
__int64 result; // rax
signed int i; //
char v3; //
for ( i = 0; i <= 14999; i += 3 ) // 6010C0地址存放着 15000字节大小的 16进制数3个为1组
{
v0 = byte_6010C0; // v0=每组的第一个 16进制数
v3 = byte_6010C0; // v3 为每组的第三个 16进制数
result = v0; // result赋初值 每组的的第一个16进制数
switch ( v0 ) // 根据每组的第一个 16进制数 决定接下来进行什么操作(算法)
{
case 1u:
result = byte_6010C0; // result = 每组的第二个 16进制数
*(&byte_604B80 + result) += v3; // 604B80是我们输入字符串存放的地址(指针)
// 所以这里就相当与 604B80=604B80+v3
break;
case 2u: // 同理 604B80=604B80-v3
result = byte_6010C0;
*(&byte_604B80 + result) -= v3;
break;
case 3u: // 同理 604B80=604B80^v3
result = byte_6010C0;
*(&byte_604B80 + result) ^= v3;
break;
case 4u:
result = byte_6010C0; // 同理 604B80=604B80*v3
*(&byte_604B80 + result) *= v3;
break;
case 5u:
result = byte_6010C0; // 下面等号右面的意思:604B80,byte_6010C0==V3
*(&byte_604B80 + result) ^= *(&byte_604B80 + byte_6010C0);// 这句代码意思:
// 604B80=604B80^604B80
//
// 这里byte_6010C0的意思其实还是上面的V3
break;
default:
continue;
}
}
return result;
}
```
## 分析:
```
函数的意思:
根据6010C0处 6010C06010C0 6010C0...6010C0来决定
对604B80的24个16进制数(即我们输入的字符串)进行 不同的操作
而操作后的604B80地址(指针)存放的24个16进制数(即我们输入的字符串经函数ub_4005B6()加密了)
最后604B80处的每个16进制数(字节)再与601060处每个双字的最后两位16进制数(即字节)要相等
```
## 逆向:
```
我们一切都逆着进行
我们的操作还是根据6010C0处 6010C06010C0 6010C0...6010C0来决定
对604B80的24个16进制数(加密后的字符串==601060处每个双字的最后两位16进制数)进行 不同的操作
不过从14997到0进行循环操作
+换成-,*换成/亦或还是亦或(举例:0x8^0x3=0xB0xB^0x3=0xB0xB^0x8=0x3)
604B80=604B80-v3
604B80=604B80+v3
604B80=604B80^v3
604B80=604B80/v3
604B80=604B80^604B80
```
## 脚本
```
#coding:utf8
da=open('export_results','rb')#导出的二进制形式的15000字节的数组
bianhua=
#bianhua对应的是 601060处的数据,每个双字的最后两位16进制数手动敲上去的
#流下没有技术的泪。
dashuzu=[]
for i in range(0,15000):
temp=da.readline(1)
dashuzu.append(ord(temp))
#print(dashuzu)
da.close()
for i in range(14997,-1,-3):
v0=dashuzu
v3=dashuzu
result=v0
if v0==1:
result=dashuzu
bianhua-=v3
elif v0==2:
result=dashuzu
bianhua+=v3
elif v0==3:
result=dashuzu
bianhua^=v3
elif v0==4:
result=dashuzu
bianhua/=v3
elif v0==5:
result=dashuzu
bianhua^=bianhua]
else:
continue
#print (bianhua)
flag=''
for j in bianhua:
flag+=chr(j%256)#保证数据可以正常转成字符
print flag
# nctf{Embr4ce_Vm_j0in_R3}
```
## 总结:
```
真的好菜,要抓紧了,超越萝卜 /(刷题是我快乐,或许吧,哈哈!)
```
!(https://s2.ax1x.com/2019/08/06/ehY4nP.jpg) 学习了,谢谢分享 支持楼主继续进步 plwt 发表于 2019-9-14 15:12
支持楼主继续进步
谢谢,会的! hnwq 发表于 2019-9-14 10:33
学习了,谢谢分享
谢谢支持!!! 小白请问一下如何将这道题中 6010c0 处的数据导出
页:
[1]