CGCTF

CGCTF

第一题,hello,re。
小端序:高位数据放在高位地址,即输出相反;
大端序:高位数据放在低位地址,输出相同。
strcmp:设这两个字符串为str1,str2,
若str1==str2,则返回零;

若str1<str2,则返回负数;

若str1>str2,则返回正数。
ida:R键转换字符;f5查看伪代码。

第二题
下载后,用记事本打开文件,发现是对func函数的汇编解释
00000000004004e6 :

4004e6: 55 push rbp
//入栈
4004e7: 48 89 e5 mov rbp,rsp
//将rsp的值调入rbp
4004ea: 48 89 7d e8 mov QWORD PTR [rbp-0x18],rdi
//rdi调入【rbp-ox18】//rbp-ox18就是input【】
4004ee: 89 75 e4 mov DWORD PTR [rbp-0x1c],esi
//esi调入【rbp-ox1c】//就是输入的28
4004f1: c7 45 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x1
//掉ox1给【rbp-ox4】//ox为十六进制表达//ox1=1,即【rbp-ox4】=1
4004f8: eb 28 jmp 400522 <func+0x3c>
//转入400522
4004fa: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
//调【rbp-ox4】给eax寄存器//eax=1
4004fd: 48 63 d0 movsxd rdx,eax
//eax带符号扩展调值给rdx//rdx=1
400500: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]//
调【rbp-ox18】给rax寄存器
400504: 48 01 d0 add rax,rdx
//rdx(【rbp-ox4】=1跟rax【rbp-ox18】相加给rax
400507: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
//掉【rbp-ox4】给edx//edx=1
40050a: 48 63 ca movsxd rcx,edx
//edx带符号赋值给rcx//rcx=1
40050d: 48 8b 55 e8 mov rdx,QWORD PTR [rbp-0x18]
//【rbp-ox18】给rdx
400511: 48 01 ca add rdx,rcx
//rdx跟rcx相加,此时,rdx存储值跟rax相同,为【rbp-ox4】+【rbp-ox18】
400514: 0f b6 0a movzx ecx,BYTE PTR [rdx]
//将【rdx】无符号赋值给ecx
400517: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
//调【rbp-ox4】调给edx//edx=1
40051a: 31 ca xor edx,ecx
//edx与ecx抑或
40051c: 88 10 mov BYTE PTR [rax],dl
//调dl给rax
40051e: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1
//【rbp-ox4】+1
400522: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
//【rbp-ox4】调给eax//eax=2
400525: 3b 45 e4 cmp eax,DWORD PTR [rbp-0x1c]
//【rbp-ox1c】与eax中值比较
400528: 7e d0 jle 4004fa <func+0x14>
//if判断,不符合回4004fa
40052a: 90 nop

40052b: 5d pop rbp
//出栈
40052c: c3 ret
之后这个函数就简单了,转化为c语言:void func(char input[],int num)
{int i=1;
while(i<=num)
{input[i]^=i;
i++;}
}
第三题:
跟python有关的题目,notepa++打开是乱码,直接百度python的反编译。。。给了这个玩意儿
#!/usr/bin/env python

visit http://tool.lu/pyc/ for more information

import base64

def encode(message):
s = ‘’
for i in message://i=1
x = ord(i) ^ 32//先把i转为整形再抑或32
x = x + 16//之后x+16(此时,x还是为i的整型转换)
s += chr(x)//x转换为字符串后给s

return base64.b64encode(s)//还有个base64的加密。。。

correct = ‘XlNkVmtUI1MgXWBZXCFeKY+AaXNt’//赋值
flag = ‘’
print ‘Input flag:’
flag = raw_input()
if encode(flag) == correct://这时候明显是反过来判断
print ‘correct’
else:
print ‘wrong’
//python中base64.b64decode 可以直接解密base64
第五题//第四题直接翻译掉的
LOBYTE()是取得16进制数最低(最右边)那个字节的内容
HIBYTE()是取得16进制数最高(最左边)
昨天没有莽的出来,今天继续慢慢一个一个分析:先拖到ida64中解析:f5后得到一下函数:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
const char *v3; // rsi
signed __int64 v4; // rbx
signed int v5; // eax
char v6; // bp
char v7; // al
const char *v8; // rdi
__int64 v10; // [rsp+0h] [rbp-28h]

v10 = ‘\0’;
puts(“Input flag:”);
scanf("%s", &s1, ‘\0’);
if ( strlen(&s1) != 24 || (v3 = “nctf{”, strncmp(&s1, “nctf{”, ‘\x05’)) || *(&byte_6010BF + 24) != 125 )//s输入必须为24字符,s1必须为nctf{开头,125查ascll表是},就是最后必须是}//strncmp是相同会返回0;
{
LABEL_22:
puts(“Wrong flag!”);
exit(-1);
}
v4 = 5LL;
if ( strlen(&s1) - 1 > 5 )
{
while ( 1 )
{
v5 = *(&s1 + v4);//获取下一字节
v6 = 0;
if ( v5 > 78 )
{
v5 = (unsigned __int8)v5;//v5强制转化为char//查了下才知道_int8是char
if ( (unsigned __int8)v5 == 79 )//v5是O//ascll表
{
v7 = sub_400650((_DWORD *)&v10 + 1);//&取地址时候比+优先级高//但是,&,的优先级是从右到左的!!!//这个就是给v7v10的下一字节-1>0,并且v10的下一字节-1//因为是用的指针操作,所以改变保留
goto LABEL_14;
}
if ( v5 == 111 )//是o
{
v7 = sub_400660((int )&v10 + 1);//v10下一字节+1,同时返回v10下一字节<8
goto LABEL_14;
}
}
else
{
v5 = (unsigned __int8)v5;
if ( (unsigned __int8)v5 == 46 )//查表,.
{
v7 = sub_400670(&v10);//给v7v10-1>0,并且v10-1
goto LABEL_14;
}
if ( v5 == 48 )//继续查,0
{
v7 = sub_400680(&v10);//v10+1,同时返回v10<8
LABEL_14
v6 = v7;
goto LABEL_15;
}
}
LABEL_15:
if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v10), v10) )//a1+a2+8
a3==32或35//限制??
goto LABEL_22;
if ( ++v4 >= strlen(&s1) - 1 )
{
if ( v6 )//v6非0,就是满足上面一大串的条件
break;
LABEL_20:
v8 = “Wrong flag!”;
goto LABEL_21;
}
}
}
if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 )//最后一个必须是35//猜想v10是行???一共8行???
goto LABEL_20;
v8 = “Congratulations!”;
LABEL_21:
puts(v8);
return 0LL;
}
之后找到601060那儿,然后慢慢搞出8
8迷宫。。。得出flag
附:
类型名称 字节 其他名称 值的范围
int 4 signed –2,147,483,648 到 2,147,483,647
unsigned int 4 unsigned 0 到 4,294,967,295
__int8 1 char –128 到 127
unsigned __int8 1 unsigned char 0 到 255
__int16 2 short、short int、signed short int –32,768 到 32,767
unsigned __int16 2 unsigned short、unsigned short int 0 到 65,535
__int32 4 signed、signed int、int –2,147,483,648 到 2,147,483,647
unsigned __int32 4 unsigned、unsigned int 0 到 4,294,967,295
__int64 8 long long、signed long long –9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned __int64 8 unsigned long long 0 到 18,446,744,073,709,551,615
bool 1 无 false 或 true
char 1 无 -128 到 127(默认)

/J" data-source-localized=“0” data-original-title="" title="" style=“box-sizing: border-box;”>0 到 255(当使用 /J 编译时)
signed char 1 无 –128 到 127
unsigned char 1 无 0 到 255
short 2 short int、signed short int –32,768 到 32,767
unsigned short 2 unsigned short int 0 到 65,535
long 4 long int、signed long int –2,147,483,648 到 2,147,483,647
unsigned long 4 unsigned long int 0 到 4,294,967,295
long long 8 无(与 __int64 等效) –9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long long 8 无(与无符号的 __int64 等效) 0 到 18,446,744,073,709,551,615
enum varies 无 请参阅本文后面的备注
浮动 4 无 3.4E +/- 38(7 位数)
double 8 无 1.7E +/- 308(15 位数)
long double 与 double 相同 无 与 double 相同
wchar_t 2 __wchar_t 0 到 65,535
第六题:
开ida,想直接找main后f5的,提示说函数太大了,然后不相信,开graph view,发现有段说的太长不显示???
只好切到hex,发现。。。这是魔鬼吗???这么多数据???好的,真香。
汇编指令部分:cmp(compare)指令进行比较两个操作数的大小
例:cmp oprd1,oprd2
为第一个操作减去第二个操作数,
但不影响两个操作数的值
它影响flag的CF,ZF,OF,AF,PF
若执行指令后
ZF=1 则说明两个数相等,因为zero为1说明结果为0
当无符号时:
CF=1 则说明了有进位或借位,cmp是进行的减操作,故可以看出为借位,所以,此时oprd1<oprd2
CF=0 则说明了无借位,但此时要注意ZF是否为0,若为0,则说明结果不为0,故此时oprd1>oprd2
当有符号时:
若SF=0,OF=0 则说明了此时的值为正数,没有溢出,可以直观的看出,oprd1>oprd2
若SF=1,OF=0 则说明了此时的值为负数,没有溢出,则为oprd1<oprd2
若SF=0,OF=1 则说明了此时的值为正数,有溢出,可以看出oprd1<oprd2
若SF=1,OF=1则说明了此时的值为负数,有溢出,可以看出oprd1>oprd2
JG/JNLE 大于转移.
JGE/JNL 大于或等于转移.
还有
JE/JZ 等于转移.
JNE/JNZ 不等于时转移.
JC 有进位时转移.
JNC 无进位时转移.
JNO 不溢出时转移.
JNP/JPO 奇偶性为奇数时转移.
JNS 符号位为 “0” 时转移.
JO 溢出转移.
JP/JPE 奇偶性为偶数时转移.
JS 符号位为 “1” 时转移.
继续:看main的前部分
main proc near ; DATA XREF: start+1D↑o
var_5 = byte ptr -5
var_4 = dword ptr -4//赋值
__unwind {
push rbp
mov rbp, rsp
sub rsp, 10h//rsp-16
mov edi, offset s ; “[WxyVM 0.0.2]”
call _puts
mov edi, offset aInputYourFlag ; “input your flag:”
call _puts
mov esi, offset byte_694100
mov edi, offset format ; “%s”
mov eax, 0
call _scanf
mov [rbp+var_5], 1//rbp+var_5=1
mov edi, offset byte_694100 ; s//这俩玩意儿相等就跳掉下面的让rbp+var_5=0的操作
call _strlen
cmp rax, 19h//比较rax跟25//比较字符串数目//要是25
jz short loc_4005FE//相同跳转//要跳
loc_492D39: ; CODE XREF: main+927B0↓j
cmp [rbp+var_4], 18h
jg short loc_492D68//rbp+var_4跟24比较,是就赢了
mov eax, [rbp+var_4]
cdqe
movzx eax, ds:byte_694100[rax]
movsx edx, al
mov eax, [rbp+var_4]
cdqe
mov eax, dword_694060[rax*4]//查了下,这个dword_694060是个固定的东西,下面就是要找edx是什么玩意儿了//本来不知道的,后来想到al好像就是ax的一部分来着
cmp edx, eax
jz short loc_492D62//就是说这个edx跟eax必须相等//然后这个edx就是说byte_694100[]跟那个必须相等,之后逆推
mov [rbp+var_5], 0//这步要挑掉
loc_492D62: ;
CODE XREF: main+927A6↑j
add [rbp+var_4], 1
jmp short loc_492D39//递增,
; ---------------------------------------------------------------------------

loc_492D68: ; CODE XREF: main+92787↑j
cmp [rbp+var_5], 0
jz short loc_492D7A//rbp+var_5不能为0,不然gg
mov edi, offset aCorrect ; “correct”
call _puts
jmp short loc_492D84
; ---------------------------------------------------------------------------

loc_492D7A: ; CODE XREF: main+927B6↑j
mov edi, offset aWrong ; “wrong”
call _puts

loc_492D84: ; CODE XREF: main+927C2↑j
mov eax, 0
leave
retn
; } // starts at 4005B6
main endp
//这时候就会发现,除了一个dword数据有用,其他那一堆让ida放弃编译的dword是在逗我。。。
国庆的欢乐(水)赛:
第一题,拖入ida后找main函数,找到get_flag,之后找到那个里面的一个东西,拖到hex中看,直接找出flag。

cgctf:single
s<=81
a1[i]是0,可跳a2[i]不改变;
a1[i]非0,a2[i]必为0,且赋值给a2[i];
之后会连着来好几次++s[*(unsigned __int8 )(9 * i + j +a1)];
并且判断1到9是否为1//即被执行1次操作,之后其实 s[
(unsigned __int8 *)(9 * i + j +a1)]=s[a[i][j]],自己对于指针的应用还是不行啊,之后
这个函数就是判断有没有重复跟未输入,然后就是一个巨大无比的数独。emmm,慢慢解吧,解出来的时候整个人都舒畅了。
之后一段时间听学长,准备搞虚拟机去linux里面把自己对指针的应用弄好点。
cgctf:homuraVM,解释器的wp
while ( 1 )
{
result = *(unsigned __int8 *)(dword_202074 + a1);
if ( !(_BYTE)result )
return result;//’\0’检测字符串是否结束,结束退出
v2 = *(char *)(dword_202074 + a1);//v2=a1[i],dword_202074=i;令qword_202088=d=0;qword_202080=s=0;qword_202078是个转化后的输入的字符串不如为m[]
switch ( (unsigned int)off_1048 )
{
case 0x43u:
*(_DWORD )qword_202088 -= 2 * ((_DWORD *)qword_202080 & (_DWORD )qword_202078);//d-=2(s&m[t])
++dword_202074;//i++
break;
case 0x47u:
(_DWORD *)qword_202088;//–d;++i
++dword_202074;
break;
case 0x4Du:
*(_DWORD *)qword_202088 = *(_DWORD *)qword_202080 + *(_DWORD )qword_202078;//d=s+m[t];++i
++dword_202074;
break;
case 0x54u:
++
(_DWORD *)qword_202088;//++d;++i
++dword_202074;
break;
case 0x5Bu:
if ( *(_DWORD *)qword_202078 )//if(m[t]){++i;}else{do v3=i++;while(a1[v3]!=93);
{
++dword_202074;
}
else
{
do
v3 = dword_202074++;
while ( *(_BYTE *)(v3 + a1) != 93 );
}
break;
case 0x5Du:
if ( *(_DWORD *)qword_202078 )//if(m[t]){do --i;while(a1[i]!=91);++i}else{++i)
{
do
–dword_202074;
while ( *(_BYTE )(dword_202074 + a1) != 91 );
++dword_202074;
}
else
{
++dword_202074;
}
break;
case 0x61u:
(_DWORD *)qword_202080;//–s;++i
++dword_202074;
break;
case 0x68u:
qword_202078 = (char )qword_202078 + 4;//t++;i++
++dword_202074;
break;
case 0x6Du:
++
(_DWORD *)qword_202078;//++m[t];++i
++dword_202074;
break;
case 0x6Fu:
qword_202078 = (char )qword_202078 - 4;//t–;i++
++dword_202074;
break;
case 0x72u:
++
(_DWORD )qword_202080;//++s;++i
++dword_202074;
break;
case 0x75u:
(_DWORD *)qword_202078;//–m[t];++i
++dword_202074;
break;
case 0x76u:
*(_DWORD *)qword_202088 = *(_DWORD *)qword_202080;//d=s;++i
++dword_202074;
break;
case 0x7Bu:
if ( *(_DWORD *)qword_202088 )//if(d){++i}else{do v4=i++while(a1[v4]!125);
{
++dword_202074;
}
else
{
do
v4 = dword_202074++;
while ( *(_BYTE *)(v4 + a1) != 125 );
}
break;
case 0x7Du:
if ( *(_DWORD *)qword_202088 )//if(d){do --i;while(a1[i]!=123);++i;}else{++i}
{
do
–dword_202074;
while ( *(_BYTE )(dword_202074 + a1) != 123 );
++dword_202074;
}
else
{
++dword_202074;
}
break;
default:
continue;
}
}
}
所以其实就是{}的作用是把d=0;而[]作用是令m[t]为0
h[ur]ovMCh{mG}//t+=1;d=0;m[t]=m[t-1];s=d=0;d=s+m[t];d-=2
(s&m[t]);t+=1;d=0
[ur]作用是让m[t]为0,并且s+到m[t]-‘0’;{mG}作用是把d=0并令m[t]=d;{aG}作用是s-=d;d=0;
这样把每个东西解析下来,就发下他其实每段都是差不多的,可以分为好几个结构类似的东西
之后解析,发现是每段差不多是这样的:t += 1;
s= 0;
d = 0;
s = s[t];
// s根据a和r改变

m[t] = s+m[t-1] - (s&m[t-1])*2;//m[t-1]=s[t];

猜你喜欢

转载自blog.csdn.net/weixin_43225801/article/details/83057208