hit_re
如果报dll缺失错误, 在https://www.dll-files.com/下载相应的 32 / 64 bits 的dll文件拷贝到C:\Windows\System32
或者C:\Windows\SysWOW64
文件夹下即可运行
无壳, 拖进IDA32, 直接逆main函数
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
int result; // eax
int v4; // eax
int v5; // [esp-4h] [ebp-414h]
int v6; // [esp-4h] [ebp-414h]
int (__cdecl *v7)(int); // [esp-4h] [ebp-414h]
int v8; // [esp+0h] [ebp-410h]
int v9; // [esp+0h] [ebp-410h]
int v10; // [esp+0h] [ebp-410h]
int v11; // [esp+0h] [ebp-410h]
int v12; // [esp+0h] [ebp-410h]
int v13; // [esp+0h] [ebp-410h]
int v14; // [esp+0h] [ebp-410h]
int v15; // [esp+0h] [ebp-410h]
int v16; // [esp+0h] [ebp-410h]
int v17; // [esp+4h] [ebp-40Ch]
int v18; // [esp+4h] [ebp-40Ch]
int v19; // [esp+4h] [ebp-40Ch]
int v20; // [esp+4h] [ebp-40Ch]
int v21; // [esp+4h] [ebp-40Ch]
int v22; // [esp+4h] [ebp-40Ch]
int v23; // [esp+10h] [ebp-400h]
int v24; // [esp+18h] [ebp-3F8h]
void *v25; // [esp+24h] [ebp-3ECh]
int v26; // [esp+30h] [ebp-3E0h]
int v27; // [esp+30h] [ebp-3E0h]
int v28; // [esp+38h] [ebp-3D8h]
int v29; // [esp+38h] [ebp-3D8h]
int v30[9]; // [esp+4Ch] [ebp-3C4h] BYREF
char v31[36]; // [esp+70h] [ebp-3A0h] BYREF
int v32[9]; // [esp+94h] [ebp-37Ch] BYREF
char v33[36]; // [esp+12Ch] [ebp-2E4h] BYREF
char T_F; // [esp+1C7h] [ebp-249h]
char act; // [esp+36Bh] [ebp-A5h]
char *q; // [esp+374h] [ebp-9Ch]
char *p; // [esp+380h] [ebp-90h]
char *str_in; // [esp+38Ch] [ebp-84h]
char v39; // [esp+39Bh] [ebp-75h]
char *j; // [esp+3A4h] [ebp-6Ch]
char *i; // [esp+3B0h] [ebp-60h]
char *moves; // [esp+3BCh] [ebp-54h]
int not_right_moves; // [esp+3C8h] [ebp-48h]
int right_moves; // [esp+3D4h] [ebp-3Ch]
char data_in[32]; // [esp+3E0h] [ebp-30h] BYREF
int v46; // [esp+40Ch] [ebp-4h]
__CheckForDebuggerJustMyCode(&unk_42E069);
print(std::cout, "your solution: ");
sub_41113B(v8, v17);
v46 = 0;
readin(std::cin, data_in);
if ( moves_count(v9, v18) != 50 ) // input count 50
goto fail;
right_moves = 0;
not_right_moves = 0;
moves = data_in;
i = (char *)sub_41105A(v10, v19);
j = (char *)sub_411005(v11, v20);
while ( i != j )
{
v39 = *i;
if ( v39 == 'd' )
{
++right_moves;
if ( not_right_moves != 6 && not_right_moves != 2 )
_exit(1);
not_right_moves = 0;
}
else
{
++not_right_moves;
}
++i;
}
if ( right_moves == 10 ) // right moves 10 times
{
str_in = data_in;
p = (char *)sub_41105A(v10, v19);
q = (char *)sub_411005(v12, v21);
while ( p != q )
{
act = *p;
switch ( act )
{
case 'a':
--left_right;
break;
case 'd':
++left_right;
break;
case 's':
++up_down;
break;
case 'w':
--up_down;
break;
default:
_exit(-1);
}
if ( map[11 * up_down + left_right] != 1 )// == 1 can move in
_exit(-2);
++p;
}
sub_4111C2((int)data_in);
v26 = sub_4113E8((int)v33);
LOBYTE(v46) = 1;
T_F = sub_4114D3("dcc1df36a15968fb206fe52294bb4130", v26);
LOBYTE(v46) = 0;
sub_4114AB(v13, v22);
if ( T_F )
{
v28 = print(std::cout, "flag is: flag{");
v27 = sub_41160E(data_in, (int)v32, 0, 0x10u);
LOBYTE(v46) = 2;
sub_4111C2(v27);
v25 = (void *)sub_4113E8((int)v31);
LOBYTE(v46) = 3;
v24 = sub_41160E(v25, (int)v30, 0, 0x10u);
LOBYTE(v46) = 4;
v4 = sub_411451(v28, v24);
v23 = print(v4, "}");
std::ostream::operator<<(v23);
LOBYTE(v46) = 3;
sub_4114AB((int)sub_411564, v14);
LOBYTE(v46) = 2;
sub_4114AB(v5, v15);
LOBYTE(v46) = 0;
sub_4114AB(v6, v16);
}
else
{
v29 = print(std::cout, "a little deviation");
v7 = sub_411564;
std::ostream::operator<<(v29);
}
system("pause");
v46 = -1;
sub_4114AB((int)v7, v14);
result = 0;
}
else
{
fail:
v46 = -1;
sub_4114AB(v10, v19);
result = -10;
}
return result;
}
主要逻辑是走出地图, 要求步数恰好为50步, 并且往右走10次, 而且往右走之前需要往其他方向走2或者6次, 提取出地图数据
import idaapi
addr = 0x0042B008
arr = []
for i in range(275):
arr.append(idaapi.get_dword(addr + 4*i))
print(arr)
打印出地图
mapdata = [1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
maps = []
for i in range(25):
maps.append(mapdata[i*11: i*11+11])
for _ in maps:
print(_)
'''
[1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0]
[1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0]
[1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1]
[0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1]
[0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1]
[0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0]
[1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0]
[1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0]
[1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1]
[1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0]
[1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0]
[1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0]
[0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1]
[1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1]
[1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1]
[1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1]
[1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1]
[1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0]
[1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0]
[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0]
[1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
'''
写一个算法搜索满足条件的路径, 并且找到一个路径需要和MD5值dcc1df36a15968fb206fe52294bb4130
比较, 匹配hash值成功的50步路径即为解
这个路径还需要注意, 因为main函数流程中要求往右的次数为10, 显然不能通过往左走凑步数, 因为往左走1次要到达右下角至少需要11次往右走, 那就不符合判断条件, 所以为了凑步数只能上下往返, 因此可以判断路径中不包含’a’, 只包含’w’, ‘s’, ‘d’, 而且根据往右走之前经过距上次往右走的步数, 要么是2步, 要么是6步, 所以可以得到固定的往返模式来凑步数, 接下来就变成算法题
def DFS(step, hlevel, path):
if step == 6:
if hlevel == 2:
print(path)
return
if hlevel == 0:
DFS(step + 1, hlevel + 1, path+'s')
elif hlevel == 1:
DFS(step + 1, hlevel + 1, path+'s')
DFS(step + 1, hlevel - 1, path+'w')
else:
DFS(step + 1, hlevel - 1, path+'w')
DFS(0, 0, '')
'''
sswsws
sswwss
swssws
swswss
'''
可见上下往返的6步模式有4种, 那么只需要用这4种模式凑步数即可, 最短路径是ssd * 10
总共30步, 所以需要引入5次往返模式多凑20步,
C
10
5
∗
4
5
=
258048
C_{10}^5 * 4^5 = 258048
C105∗45=258048种可能性进行回溯爆破, 其实只需要大概3s
from itertools import combinations
from hashlib import *
patterns = ['swswssd','swsswsd','sswwssd','sswswsd']
L = [_ for _ in range(10)]
coms = list(combinations(L, 5))
# print(coms)
# paths = ['ssd' for _ in range(10)]
# count = 0
# path = ''.join(paths)
# print(path, len(path))
def BackTrace(step, paths, comb):
if step == 10:
path = ''.join(paths)
if md5(path.encode(encoding='UTF-8')).hexdigest() == 'dcc1df36a15968fb206fe52294bb4130':
print('find the flag!!!')
print(path)
print()
return
if step not in comb:
BackTrace(step + 1, paths, comb)
else:
paths[step] = patterns[0]
BackTrace(step + 1, paths, comb)
paths[step] = patterns[1]
BackTrace(step + 1, paths, comb)
paths[step] = patterns[2]
BackTrace(step + 1, paths, comb)
paths[step] = patterns[3]
BackTrace(step + 1, paths, comb)
# print(len(coms))
for comb in coms:
paths = ['ssd' for _ in range(10)]
BackTrace(0, paths, comb)
# path = 'ssdsswswsdssdswswssdssdswsswsdsswswsdssdssdswswssd'
# print(md5(path.encode(encoding='UTF-8')).hexdigest() == 'dcc1df36a15968fb206fe52294bb4130')
pypy
python文件打包的exe, 用pyinstxtractor解析
https://github.com/extremecoders-re/pyinstxtractor
pyinstxtractor.py解压出文件夹, 这里用python3.7才行, 需要和压缩成exe文件所用的python版本一致
falca@DESKTOP-GKDU8KD:/mnt/k/competition/安网杯/re/pypy$ python3.7 pyinstxtractor.py pypy.exe
[+] Processing pypy.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.7
[+] Length of package: 6230873 bytes
[+] Found 61 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pypy.pyc
[+] Found 133 files in PYZ archive
[+] Successfully extracted pyinstaller archive: pypy.exe
You can now use a python decompiler on the pyc files within the extracted directory
然后用uncompyle6
把pypy.pyc
文件逆回py文件
uncompyle6 pypy.pyc > pypy.py
得到源码
# uncompyle6 version 3.8.0
# Python bytecode 3.7.0 (3394)
# Decompiled from: Python 3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)]
# Embedded file name: pypy.py
import hashlib
if __name__ == '__main__':
nums = []
a, b, c = (3001, 2137, 4729)
n = 100000
num = 1
while len(nums) != n:
num_copy = num
while num_copy != 1:
if num_copy % a == 0:
num_copy //= a
elif num_copy % b == 0:
num_copy //= b
elif num_copy % c == 0:
num_copy //= c
else:
break
if num_copy == 1:
nums.append(num)
num += 1
print(nums[(n - 1)])
m = hashlib.md5()
b = str(nums[(n - 1)]).encode(encoding='utf-8')
m.update(b)
result = m.hexdigest()
print('flag{' + result + '}')
# okay decompiling pypy.pyc
简单来说就是求以3001, 2137,4729为因数组成的倍数, 从小到大排序的第100000
个数的MD5值
算法题, 用优先队列heapq
优化性能, 每次向优先队列里加入Min * 3001, Min * 2137, Min * 4729
贪心取得当前最小的倍数, 当heapq弹出100000个Min, 则得到答案
from hashlib import *
from heapq import *
ps = [3001, 2137, 4729]
heap = []
heappush(heap, 1)
occupied = set()
occupied.add(1)
count = 0
Min = 0
while True:
Min = heappop(heap)
count += 1
if count == 100000: break
for i in range(3):
tmp = Min * ps[i]
if tmp not in occupied:
heappush(heap, tmp)
occupied.add(tmp)
print(Min)
print('flag{' + md5(str(Min).encode(encoding='UTF-8')).hexdigest() + '}')
'''
16405850262570740513897217717623720600448855987320514551726154873739883293720282851034424371051874484044854674904735438772866326784386465461577479345634270005309256879590279090175542403199472872896278587926077044001237403554885317937416574659330592944654425785521663598285833017718525414609
flag{ba64f885157a04966c5173fb24590032}
'''
总结
这是去年打的第一次线下赛, 时隔一年来补一下题, 当时因为禁止联网(虽然赛场全是热点, 我当时开热点还被捉了, xs), 环境配不起来REV这两题都没做出来, 后来各种事一直没补题, 现在感觉积累足够了, 差不多可以把近两年的赛题都做一做.
总体来说这两题逻辑不算很复杂, 有的函数调用链很深逻辑判断不了, 就直觉猜加动调验证, 软件加密也上了花指令反调试或者保护壳(虽然没啥干扰), 逆向出来就变成算法题了, 算法题多刷刷也不算特别难, 主要是能联网, 难度会低很多.