check_in
附件:check_in.zip
init_array
通过IDA,逆向得到main函数:
1 2 3 4 5 6 7 8 9 10 11 12 13
| __int64 __fastcall main(int a1, char **a2, char **a3) { char s1[268]; int v5;
v5 = 0; gets(s1, 256LL, a3); if ( !strncmp(s1, s2, 0x100uLL) ) puts("flag is right"); else puts("flag is wrong"); return 0LL; }
|
s2在data段,但是该字符串并非真正的flag。
1 2
| .data:0000000000404040 ; char s2[] .data:0000000000404040 s2 db 'flag{check_inn}',0
|
接下来有两种思路:
- init_array 中存在函数,在main函数之前执行。(因此s2被初始化成别的值)
- 查找对s2的引用,在
sub_4011D0()
函数中对s2做了更改。
两者都指向同一个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| _BYTE *sub_4011D0() { _BYTE *result;
result = (_BYTE *)((unsigned int)s2 + 15); *(_BYTE *)((int)result - 1LL) = '_'; *(_BYTE *)(int)result = 'i'; *(_BYTE *)((int)result + 1LL) = 's'; *(_BYTE *)((int)result + 2LL) = '_'; *(_BYTE *)((int)result + 3LL) = 'n'; *(_BYTE *)((int)result + 4LL) = 'o'; *(_BYTE *)((int)result + 5LL) = 't'; *(_BYTE *)((int)result + 6LL) = '_'; *(_BYTE *)((int)result + 7LL) = 'r'; *(_BYTE *)((int)result + 8LL) = 'e'; *(_BYTE *)((int)result + 9LL) = 'a'; *(_BYTE *)((int)result + 0xALL) = 'l'; *(_BYTE *)((int)result + 0xBLL) = 'l'; *(_BYTE *)((int)result + 0xCLL) = '}'; *(_BYTE *)((int)result + 0xDLL) = '\0'; return result; }
|
所以flag是 flag{check_inn_is_not_reall}
dang-van
附件:dang-van.zip
一个更改过的base64
main函数如下,change函数中发现一个类似base64的索引表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| int __cdecl main(int argc, const char **argv, const char **envp) { char v3; // bl char v5[16]; // [rsp+0h] [rbp-40h] BYREF char v6[16]; // [rsp+10h] [rbp-30h] BYREF char v7[32]; // [rsp+20h] [rbp-20h] BYREF
std::string::string((std::string *)v5); std::operator<<<std::char_traits<char>>(&std::cout, "Input your secret: "); std::operator>><char>(&std::cin, v5); std::string::string((std::string *)v6, (const std::string *)v5); change((__int64)v7, (std::string *)v6); v3 = std::operator==<char>(v7, "ms4otszPhcr7tMmzGMkHyFn="); std::string::~string((std::string *)v7); std::string::~string((std::string *)v6); if ( v3 ) std::operator<<<std::char_traits<char>>(&std::cout, "Good boy! Submit your flag :)"); else std::operator<<<std::char_traits<char>>(&std::cout, "Too bad :("); std::string::~string((std::string *)v5); return 0; }
|
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" b = "ELF8n0BKxOCbj/WU9mwle4cG6hytqD+P3kZ7AzYsag2NufopRSIVQHMXJri51Tdv=" b_str = "ms4otszPhcr7tMmzGMkHyFn=" dict1 = {}
for i in range(len(a)): dict1[b[i]] = a[i] print(dict1)
for j in b_str: print(dict1[j],end="")
print("\n")
|
得到 RnVubnlfZW5jb2RlX2h1aCE=
,再base64解码:
1 2
| $ echo RnVubnlfZW5jb2RlX2h1aCE= | base64 -d Funny_encode_huh!
|
flag_finder
附件:flag_finder.zip
下断点调试
main函数如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| __int64 __fastcall main(int a1, char **a2, char **a3) { __int64 result; void *v4; unsigned int v5; char v6; char **v7; int v8; unsigned int v9; int v10; __int64 v11; char *dest;
v8 = a1; v7 = a2; if ( a1 == 2 ) { v11 = (unsigned int)n - 1LL; v4 = alloca(16 * (((unsigned __int64)(unsigned int)n + 15) / 0x10)); dest = (char *)&v7; strcpy((char *)&v7, a2j); v10 = 0; v9 = 0; while ( memcmp(dest, "9447", 4uLL) ) { v5 = v9 % (unsigned int)n; v6 = dest[v9 % (unsigned int)n]; dest[v5] = sub_40060D() ^ v6; ++v9; } if ( !memcmp(dest, v7[1], (unsigned int)n) ) printf("The flag is %s\n", v7[1]); else puts("Try again"); result = 0LL; } else { printf("Usage: %s <password>\n", *v7); result = 1LL; } return result; }
|
v7[1] 是我们输入的字符串,因此再memcmp(dest, v7[1], (unsigned int)n)
处下断点即可看到dest中存放的字符串(即flag):
1 2 3 4 5 6 7 8
| pwndbg> set arg aaaa pwndbg> b *0x400729 pwndbg> r ────────────────────────────────────────[ DISASM ]────────────────────────────────────────── ► 0x400729 call memcmp@plt <memcmp@plt> s1: 0x7fffffffdc10 ◂— '9447{C0ngr47ulaT1ons_p4l_buddy_y0Uv3_solved_the_re4l__H4LT1N6_prObL3M}' s2: 0x7fffffffe154 ◂— 0x4548530061616161 /* 'aaaa' */ n: 0x46
|
得到flag:9447{C0ngr47ulaT1ons_p4l_buddy_y0Uv3_solved_the_re4l__H4LT1N6_prObL3M}
debug_me
附件:debug_me.zip
反调试
main函数中将我们的输入传给 sub_4006FD()
函数处理。此时有两个方法:1)直接在for循环中下断点,查看每次执行时 *(char *)(v3[i % 3] + 2 * (i / 3))
的结果,将每个数减1就得到flag。2)重写这个函数,编译跑一遍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| __int64 __fastcall sub_4006FD(__int64 a1) { int i; __int64 v3[4];
v3[0] = (__int64)"Dufhbmf"; v3[1] = (__int64)"pG`imos"; v3[2] = (__int64)"ewUglpt"; for ( i = 0; i <= 11; ++i ) { if ( *(char *)(v3[i % 3] + 2 * (i / 3)) - *(char *)(i + a1) != 1 ) return 1LL; } return 0LL; }
|
方法1
gdb调试时发现程序无法正常运行,ctrl-c 后可以看到,程序在0x4007e4处进入了循环。(另一种方法,在gdb r
命令前,使用 catch syscall
对每一个系统调用下断点,也能跟踪到ptrace调用点)
1 2 3 4
| ──────────────────────────────────────[ DISASM ]───────────────────────────────────────────── ► 0x4007e4 jmp 0x4007e4 <0x4007e4> ↓ 0x4007e4 jmp 0x4007e4 <0x4007e4>
|
对应到二进制的 sub_4007A8()
函数,该二进制开启了反调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| __int64 sub_4007A8() { __int64 result;
if ( (unsigned int)getenv("LD_PRELOAD") ) { while ( 1 ) ; } result = ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL); if ( result < 0 ) { while ( 1 ) ; } return result; }
|
这个题比较简单,虽然开了反调试,但我们可以在调用 sub_4007A8()
函数之前下断点,gdb中修改内存或寄存器值,绕过检测,然后进入 sub_4006FD()
函数查看字符串计算结果,得到flag。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void __fastcall init(unsigned int a1, __int64 a2, __int64 a3) { __int64 v4; signed __int64 v5;
v4 = 0LL; v5 = &off_600E18 - funcs_4008D9; init_proc(); if ( v5 ) { do ((void (__fastcall *)(_QWORD, __int64, __int64))funcs_4008D9[v4++])(a1, a2, a3); while ( v4 != v5 ); } }
|
方法2
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include<stdio.h> #include<stdint.h>
int main(){
uint64_t v3[3]; v3[0] = (uint64_t)"Dufhbmf"; v3[1] = (uint64_t)"pG`imos"; v3[2] = (uint64_t)"ewUglpt";
int i = 0,a =0; for(i = 0; i <= 11; i++){ a = *(char *)(v3[i%3] + 2 *(i/3)); printf("0x%x, ",a-1); printf("%c, ",a-1); }
return 0; }
|
float
附件:float.zip
【暂未解出】
可参考WP:强网杯2021 ctf线上赛ezmath wp
本题的主要计算步骤在sub_13f3函数中,v3的初始值为0.2021,i的起始值为0x2021,乍一看v3 = 2.718281828459045 - (double)i * v3
的计算结果会小于0。而main函数中待比对的结果dbl_4020数组
中值都是大于0的浮点数。所以这里的计算很奇怪,加上init_array中有一个奇怪的计算函数,所以我们动态调试来看看v3这个位置的实际情况。调试结果如下图所示,v3的初始值是0.00048291080524950886,并不是我们之前分析的0.2021。
python:16进制与double/float之间的转换
Python 十六进制与浮点数互相转换
python中struct.pack()函数和struct.unpack()函数
转换示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import struct
num_hex = 0x3f19ad3fbd59ca39 num_double = 0.00009794904266317233
num_hex_str = '{:x}'.format(num_hex) result_double = struct.unpack('>d',bytes.fromhex(num_hex_str))[0] print("double is : {}".format(result_double))
result_hex_str = struct.pack(">d",num_double).hex() result_int = int(result_hex_str,16) print("hex is : 0x{:x}".format(result_int))
|
sympy库:解数学方程
rMath-浮点运算的逆向分析
一个例子,假设要计算以下式子中x的值:
1
| 2.718281828459045 - x*0.00048291080524950886 = 0.00009794904266317233
|
那么可以利用如下脚本
1 2 3 4 5 6 7 8 9 10
| import struct from sympy import *
Str = symbols('Str')
jie = solve(2.718281828459045 - Str*0.00048291080524950886 - 0.00009794904266317233,Str)
ret = struct.pack(">f",jie[0]).hex()
print(ret)
|
IDA使用小技巧——将数据转换成double类型的操作:
simulator
附件:simulator.zip
GoodRE
附件:GoodRE.zip
SimpleFileSytem
附件:SimpleFileSystem.zip
SoMuchCode
附件:SoMuchCode.zip