2023HWS_RE复现
2023HWS_RE复现
参考wp:https://oacia.cc/hws-2023/
Android
参考这篇文章:https://www.52pojie.cn/thread-1680984-1-1.html
360加固去掉
利用gg修改器+lua脚本去掉360加固后
然后就ok了,但是dex文件直接丢尽jadx会报错
把多余的数据删了就行
然后可以正常看
到so里面看,sm4加密,
enc给出,key,iv通过app里面的字符串猜出
Animals
有很多花指令
手动patch后发现还有一堆
脚本去花
import idc
def clear_junk(ea_start,ea_end):
junk=[0x74, 0x15, 0x75, 0x13, 0x8D, 0x44, 0x24, 0xFC, 0x83, 0xF0,
0x22, 0x3B, 0x04, 0x24, 0x74, 0x0A, 0xE8, 0x1F, 0x00, 0x00,
0x00, 0x74, 0x04]
while ea_start<ea_end:
if idc.get_bytes(ea_start,len(junk))==bytes(junk):
for i in range(len(junk)):
idc.patch_byte(ea_start+i,0x90)
ea_start+=len(junk)
ea_start+=1
ea_start=0x0000000000400800
ea_end=0x00000406E20
clear_junk(ea_start,ea_end)
print("ok")
修改junk里面的机器码,再进行去花
import idc
def clear_junk(ea_start,ea_end):
junk=[0x74, 0x0A, 0x75, 0x08, 0xE8, 0x10, 0x00, 0x00, 0x00, 0xEB,
0x04, 0xE8, 0x48, 0x8D, 0xBD, 0x90, 0xFE, 0xFF, 0xFF]
while ea_start<ea_end:
if idc.get_bytes(ea_start,len(junk))==bytes(junk):
for i in range(len(junk)):
idc.patch_byte(ea_start+i,0x90)
ea_start+=len(junk)
ea_start+=1
ea_start=0x0000000000400800
ea_end=0x00000406E20
clear_junk(ea_start,ea_end)
print("ok")
还有一种,这本质上同一种花
import idc
def clear_junk(ea_start,ea_end):
junk=[0x74, 0x0A, 0x75, 0x08, 0xE8, 0x10, 0x00, 0x00, 0x00, 0xEB,
0x04, 0xE8]
while ea_start<ea_end:
if idc.get_bytes(ea_start,len(junk))==bytes(junk):
for i in range(len(junk)):
idc.patch_byte(ea_start+i,0x90)
ea_start+=len(junk)
ea_start+=1
ea_start=0x0000000000400800
ea_end=0x00000406E20
clear_junk(ea_start,ea_end)
print("ok")
去完差不多这个样子
去不透明谓词
找到汇编
给寄存器赋值为0
这样就去掉了
按照这个方法开始去
代码审计
完全去掉不透明谓词和花指令
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
size_t v4; // rax
size_t v5; // rax
size_t v6; // rax
size_t v7; // rax
size_t v8; // rax
size_t v9; // rax
unsigned int v10; // eax
const char *v12; // [rsp+68h] [rbp-2C8h]
const char *v13; // [rsp+80h] [rbp-2B0h]
const char *v14; // [rsp+98h] [rbp-298h]
const char *v15; // [rsp+B0h] [rbp-280h]
const char *v16; // [rsp+C8h] [rbp-268h]
char *src; // [rsp+E0h] [rbp-250h]
int j; // [rsp+144h] [rbp-1ECh]
char v19[88]; // [rsp+148h] [rbp-1E8h] BYREF
char v20[24]; // [rsp+1A0h] [rbp-190h] BYREF
int v21; // [rsp+1B8h] [rbp-178h] BYREF
int i; // [rsp+1BCh] [rbp-174h]
char v23[256]; // [rsp+1C0h] [rbp-170h] BYREF
char *s; // [rsp+2C0h] [rbp-70h] BYREF
char *v25; // [rsp+2C8h] [rbp-68h]
char *v26; // [rsp+2D0h] [rbp-60h]
char *v27; // [rsp+2D8h] [rbp-58h]
char *v28; // [rsp+2E0h] [rbp-50h]
char *v29; // [rsp+2E8h] [rbp-48h]
pthread_t newthread[2]; // [rsp+2F0h] [rbp-40h] BYREF
int v31; // [rsp+300h] [rbp-30h]
int v32; // [rsp+304h] [rbp-2Ch]
v32 = 0;
v31 = argc;
newthread[1] = (pthread_t)argv;
sub_405FF0(argc, argv, envp);
memset(&s, 0, 0x30uLL);
s = (_BYTE *)("0. cat" + 3);
v25 = (_BYTE *)("1. dog" + 3);
v26 = (_BYTE *)("2. fox" + 3);
v27 = (_BYTE *)("3. panda" + 3);
v28 = (_BYTE *)("4. dragon" + 3);
v29 = (_BYTE *)("5. monkey" + 3);
memset(v23, 0, sizeof(v23));
for ( i = 0; i < 9; ++i )
{
v21 = 0;
puts("Welcome Animal shop");
sub_400900();
v3 = "%d";
printf("Please input my favorite animal: ");
__isoc99_scanf("%d", &v21);
switch ( v21 )
{
case 0:
src = s;
v4 = strlen(s);
strncat(v23, src, v4);
break;
case 1:
v16 = v25;
v5 = strlen(v25);
strncat("%d", v16, v5);
break;
case 2:
v15 = v26;
v6 = strlen(v26);
strncat(v23, v15, v6);
break;
case 3:
v14 = v27;
v7 = strlen(v27);
strncat("%d", v14, v7);
break;
case 4:
v13 = v28;
v8 = strlen(v28);
strncat(v23, v13, v8);
break;
case 5:
LOBYTE(v3) = 0;
v12 = v29;
v9 = strlen(v29);
strncat(v3, v12, v9);
break;
default:
puts("Input Err.");
exit(0);
}
}
pthread_create(newthread, 0LL, start_routine, 0LL);
pthread_join(newthread[0], 0LL);
puts("done.");
sub_4009A0(v19);
v10 = strlen(v23);
sub_400B30(v19, v23, v10);
sub_4059A0(v19, v20);
for ( j = 0; j < 16; ++j )
{
if ( byte_608090[j] != v20[j] )
{
puts("Wrong.");
exit(0);
}
}
puts("Win! , flag is flag{md5(input)}");
return 0;
}
这里我用md5加密硬爆没出来,后面看其他师傅wp才知道md5的常数换位置了,由于全是数字限制小,直接开爆了。
我这里用脚本
from itertools import product
from subprocess import Popen, PIPE
# 创建迭代器
char_set = "012345"
char_length = 9
all_combinations = product(char_set, repeat=char_length)
# 遍历组合并发送给进程
for combination in all_combinations:
inp = "".join(combination)
p = Popen('./main', stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
# 发送输入
for i in inp:
p.stdin.write(i.encode()+b'\n')
#print(i.encode())
p.stdin.close()
# 接收输出
res = p.stdout.read()
print(res.decode('utf-8'))
# 检查是否包含flag
if b'flag' in res:
with open('flag.txt', 'w+') as f:
f.write(inp)
print(inp)
break
E
下一个ida插件
https://www.52pojie.cn/thread-1684608-1-1.html,
下一个易语言,把易语言的静态库全提取sign
https://bbs.kanxue.com/thread-263254.htm
利用工具提取后
我这只选择了一个,没想到同目录下的全提取了
然后打开ida的目录的sig\pc
把名称加载在末尾
进ida先用e插件
识别出来一些,然后shift f5 ,
挨个导入
然后搜索字符wrong,定位逻辑
这边可以看到符号全恢复了
进行代码审计,发现我们输入的input分前后2部分进行
先进行转化操作
def en_head(y):
for i in range(len(y)):
y[i]<<=1
y[i]^=0xbf
y[i]>>=1
y[i]&=0xff
print(bytes(y).decode(),end='\n')
#后部分
def en_tail(y):
for i in range(len(y)):
y[i]>>=1
y[i]^=0x81
y[i]<<=1
y[i]&=0xff
print(bytes(y).decode(),end='\n')
这个就是检测数字,如果字符串是数字就进循环
进行调试
三方支持库命令((unsigned int)ecalc_fnUnlNumImportNum, 2, v69, 0, 131082, v67, 0, 0x80000301);
易语言存储结构研究
这里下一个易语言进行研究,主要导入eCalc的文件
导入教程: https://www.jb51.net/article/151735.htm
.版本 2
.支持库 eCalc
.程序集 程序集1
.子程序 _启动子程序, 整数型
.局部变量 局_大数a, 大数
.局部变量 局_大数b, 大数
.局部变量 局_结果, 大数
.局部变量 局_显示, 文本型
局_大数a.导入数字 (287454020)
局_大数b.导入数字 (1432778632)
局_结果 = 局_大数a.加 (局_大数b)
局_显示 = 局_结果.导出文本 ()
标准输出 (, 局_显示)
返回 (0)
静态编译
三方支持库命令((unsigned int)ecalc_fnUnlNumImportNum, 2, v7, 0, 131082, 0x11223344, 0, -2147482879);
这很明显时让v7=0x11223344
此时v7为一个指针
跟进
继续跟进
继续跟进
此时不难看到我们的数据
经过多次探索得到它的数据结构
后面存放的是链表,每4个字节存放高位信息
val=val_head*(10**9)+val
然后用此方法探索乘,减,=
最终完整的算法
flag后半部分算法
for i in range(1,901):
input_tail=input_tail-i*0x70955fc45*i+i*0x34ED1CCB+0xD1558
if input_tail==0:
break
flag上半部分同理可得
for i in range(1,801):
input_tail=input_tail-i*0x70955fc45*i+i*0x34ED1CCB+0xD1558
if input_tail==0:
break
我们exp
import string
x=string.ascii_letters+string.digits
table=[ord(i) for i in x]
def re_num(number):
x=0
for i in range(1,number):
x=x+i*0x70955fc45*i+i*0x34ED1CCB+0xD1558
return x
def re_tail(y):
for i in range(len(y)):
for j in table:
x=j
j<<=1
j^=0x81
j>>=1
j&=0xff
if j==y[i]:
print(chr(x),end='')
def re_head(y):
for i in range(len(y)):
for j in table:
x=j
j<<=1
j^=0xbf
j>>=1
j&=0xff
if j==y[i]:
print(chr(x),end='')
input_head=[ord(i) for i in str(re_num(801))]
input_tail=[ord(i) for i in str(re_num(901))]
re_head(input_head)
re_tail(input_tail)