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)
posted @ 2023-07-18 14:34  雨后初霁  阅读(80)  评论(0编辑  收藏  举报