分类 BUUOJ 下的文章

signal

IDA 载入分析,发现是VM
2024-06-23T01:21:07.png
抄成python出来,与z3打组合技解

from z3 import *
s = Solver()
x = [BitVec(f"x{i}", 8) for i in range(15)]
vmIns = open('signal.exe', 'rb').read()[0x1e40:0x1e40+456]
Str = [0] * 1000
end = 114
v9 = 0
v4 = 0
v8 = 0
v7 = 0
v6 = 0
v5 = 0
while v9 < end:
    ins = vmIns[v9 * 4]
    if ins == 1:
        Str[v6 + 100] = v4
        v9 += 1
        v6 += 1
        v8 += 1
    elif ins == 2:
        v4 = vmIns[(v9 + 1) * 4] + Str[v8]
        v9 += 2
    elif ins == 3:
        v4 = Str[v8] - (vmIns[(v9 + 1) * 4] & 0xff)
        v9 += 2
    elif ins == 4:
        v4 = vmIns[(v9 + 1) * 4] ^ Str[v8]
        v9 += 2
    elif ins == 5:
        v4 = vmIns[(v9 + 1) * 4] * Str[v8]
        v9 += 2
    elif ins == 6:
        v9+=1
    elif ins == 7:
        print(Str[v7 + 100])
        s.add(Str[v7 + 100] == vmIns[(v9 + 1) * 4])
        v7 += 1
        v9 += 2
    elif ins == 8:
        Str[v5] = v4
        v9 += 1
        v5 += 1
    elif ins == 10:
        print("Read")
        for i, v in enumerate(x):
            # s.add(v > 0x20, v < 0x7f)
            Str[i] = v
        v9 += 1
    elif ins == 11:
        v4 = Str[v8] - 1
        v9 += 1
    elif ins == 12:
        v4 = Str[v8] + 1
        v9 += 1
    else:
        print(f"unknown insn {ins}")

print(s.check())
m = s.model()
for i in x:
    print(chr(m[i].as_long()), end ='')
'''
Read
(16 ^ x0) - 5
3*(32 ^ x1)
x2 - 2 - 1
4 ^ x3 + 1
3*x4 - 33
x5 - 1 - 1
(9 ^ x6) - 32
36 ^ 81 + x7
x8 + 1 - 1
37 + 2*x9
65 ^ 54 + x10
1*(32 + x11)
37 + 3*x12
(9 ^ x13) - 32
65 + x14 + 1
sat
757515121f3d478
'''

flag为flag{757515121f3d478}

h01k_re

把exe载入IDA,发现这些函数与反调试有关
2024-06-22T10:37:22.png

完整的检测函数

NTSTATUS sub_401990()
{
  unsigned __int8 v0; // al
  int v1; // edx
  int v2; // ecx
  NTSTATUS result; // eax
  HANDLE ProcessId; // eax
  HANDLE CurrentProcess; // eax
  HANDLE v6; // eax
  int v7; // [esp-1Ch] [ebp-44h]
  int v8; // [esp-18h] [ebp-40h]
  ULONG ReturnLength; // [esp+Ch] [ebp-1Ch] BYREF
  int ProcessInformation; // [esp+14h] [ebp-14h] BYREF
  int v11; // [esp+18h] [ebp-10h]
  WINBOOL pbDebuggerPresent; // [esp+1Ch] [ebp-Ch] BYREF
  int v13; // [esp+20h] [ebp-8h]
  int v14; // [esp+24h] [ebp-4h]

  if ( (unsigned __int8)sub_401770()
    || (unsigned __int8)sub_401820()
    || (unsigned __int8)sub_401840()
    || (unsigned __int8)sub_401860()
    || (v0 = sub_401890(), (v2 = v0) != 0) )
  {
    v14 = sub_402120();
  }
  else
  {
    v14 = 0;
  }
  v8 = v2;
  v7 = v1;
  if ( NtCurrentPeb()->BeingDebugged )
    return dword_446030(v8, v7);
  if ( NtCurrentPeb()->NtGlobalFlag )
    return dword_446030(v8, v7);
  ProcessId = CsrGetProcessId();
  if ( OpenProcess(0x400u, 0, (DWORD)ProcessId) )
    return dword_446030(v8, v7);
  CurrentProcess = GetCurrentProcess();
  if ( CheckRemoteDebuggerPresent(CurrentProcess, &pbDebuggerPresent) )
    v13 = 0;
  else
    v13 = sub_402120();
  if ( pbDebuggerPresent == 1 )
    v11 = sub_402120();
  else
    v11 = 0;
  v6 = GetCurrentProcess();
  result = NtQueryInformationProcess(v6, ProcessDebugPort, &ProcessInformation, 4u, &ReturnLength);
  if ( ProcessInformation )
    return sub_402120();
  return result;
}

分别检测下面的
NtCurrentPeb()->BeingDebugged
NtCurrentPeb()->NtGlobalFlag
CheckRemoteDebuggerPresent
NtQueryInformationProcess

除了这个,还检测VMWaresub_401890()代码如下

char sub_401890()
{
  DWORD cbData; // [esp+0h] [ebp-4Ch] BYREF
  HKEY phkResult; // [esp+4h] [ebp-48h] BYREF
  BYTE Data[64]; // [esp+8h] [ebp-44h] BYREF

  cbData = 63;
  if ( !RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\BIOS\\", 0, 0x20019u, &phkResult) )
  {
    RegQueryValueExA(phkResult, "SystemManufacturer", 0, 0, Data, &cbData);
    if ( strstr((const char *)Data, "VMWARE") )
    {
      RegCloseKey(phkResult);
      return 1;
    }
    RegCloseKey(phkResult);
  }
  return 0;
}

除了上面的,还有最开始的四个函数
2024-06-22T10:44:49.png
第一个会使用特权指令in来制造异常,不过try{}catch{}并没有检测到什么。我们先不看

接下来我们分析第二个:sub_401930()
2024-06-22T10:51:02.png
2024-06-22T10:55:09.png
分别会记录rdtsc的高32位和低32位
因为__rdtsc()返回一个64位整数,分别高位:edx,低位:eax
了解一下rdtsc

rdtsc指令, 该指令返回CPU自启动以来的时钟周期数;该时钟周期数,即处理器的时间戳。
在CPU通电启动后,首先会重置EDX和EAX,在每个时钟周期上升或下降沿到来时,会自动累计周期数,并被记录到EDX和EAX寄存器中,EDX是高位,EAX是低位。

rdtsc指令就是从该寄存器中进行获取的。

周期和频率的关系公式:T(周期)=1/f(频率)

如CPU频率f为1GHz,则其时钟周期T=1/1GHz秒,意味着每隔T秒(1ns),CPU完成一个最基本的动作,并在寄存器中,对周期数加1。

所以我们可以知道rdtsc的递增是非常快的,按照我的电脑的运行时CPU频率2.35GHz,可以大概知道每秒这个数字会增加2.35*10^9。
不过不是很懂cpuid在rdtsc之前运行的意思❓

下面我们看sub_401950()函数
2024-06-22T11:06:32.png
显然是接着上一个函数进行的基于时间的检测,如果单步调试就爆了。
因为检测时间差是40000000,也就是4*10^7,也就是 1/100 秒内必须执行完,单步显然会不行。
对于这些函数,我们直接在函数头ret就好了,不过要注意,这些函数并不是所有都是检测函数。

继续分析,下面就是一堆简单的逻辑了
2024-06-22T11:11:48.png
注意到是把自己读入到刚刚allocate好的内存里面,然后解密关键内容,这里直接上动态调试就OK了,静态分析没必要,因为你不需要了解算法内容(可能是AES❓)。
我们可以把解密后的东西给dump出来,是一个dll,下面的v13 = v14();调用getflag函数。
2024-06-22T11:14:05.png
如果函数返回是1,则success,否则执行__asm { syscall; Low latency system call }这个显然会造成异常。

下面开始分析DLL
找到DLL关键函数,这个前面还有几个check❓,出题人这么和反调试、虚拟机调试过不去吗😰。
其实我们只要动态调试,屏蔽掉所有异常,然后运行到程序中的v13 = v14();这个位置就好了(记得使用反反调试插件)
2024-06-22T11:16:54.png
我们终于找到了scanf的真正位置,下面是初始化了一堆数据,然后进虚拟机。
2024-06-22T11:18:50.png
动态调试,加硬件断点于第一个输入的值,这个scanf有一点奇怪,你如果输入的太长了,好像第一个位置就变成0了😰,简单打断点分析了一下,好像被填0了,不过我们不管这个,我们把第一个0改成a继续下断点就行。
2024-06-22T11:20:08.png
跑起来,第一次断在strlen,第二次就断在vm了
2024-06-22T11:32:07.png
我们把stack下硬件断点,发现对输入先进行xor 0x33
2024-06-22T11:33:26.png
继续跑,发现再进行 add 0x2
2024-06-22T11:34:00.png
继续跑,发现存在了这里
2024-06-22T11:34:47.png
然后把这个store的地方进行软件断点,把之前的硬件断点去掉,这样可以看到加密的全过程,也可以清楚知道都是xor 0x33并且之后add 0x2,对了,一定要注意,把第一个store的地方下硬件断点,因为加密后必定就是cmp检测,或者是继续加密...。
2024-06-22T11:37:32.png
果不其然,它在处理完后面的字符串后,很快地,又把刚刚第一个字符串push到了vStack上。
2024-06-22T11:38:37.png
这时我们继续传统艺能:对栈下断点,发现对第一个字节进行xor 0x36,第二个字符xor 0x48...,我们直接可以断xorTable的生成点,注意到getXorTable的地方其实也是拿字符串的地方
2024-06-22T11:43:07.png
最后我们继续下断点,找到了cmp table,写出z3脚本

from z3 import *
s = Solver()
a = bytes.fromhex("36 48 0D 23 0C 25 09 17 53 06 10 1A 24 2E 38 42".replace(' ',''))
b = bytes.fromhex("51 5f 72 69 6a 32 89 79 2b 52 52 56 4a 4d 60 12".replace(' ',''))
x = [BitVec(f"x{i}", 8) for i in range(len(a))]
for i in range(len(a)):
    s.add(((x[i] ^ 0x33) + 0x2 ) ^ a[i] == b[i])

print(s.check())
m = s.model()

for i in range(len(a)):
    print(chr(m[x[i]].as_long()), end='')
# V&N{W&M_Easy_Re}

careful

题目给出了一个恶意样本,请分析出样本请求服务器的域名(flag的形式为DASCTF{md5(域名)}) 其中md5值都是小写

拖入IDA,发现是一个解析域名,并建立TCP链接的过程,建立这个链接后,recv通过拼接,来CreateProcessA,运行恶意代码。
2024-06-18T08:04:29.png
显然htons是端口(8888),inet_addr函数获取的是ip地址,ip地址是从gethostbyname函数来的。

  for ( i = 0; i < v4; ++i )
    name[i] = __ROR1__(*((_BYTE *)v13 + i), i & 7);

name的解密过程是上述这样的,我们动调后可以发现是welcome_toxxxxxx.com,我们尝试了一下是错误的答案。
通过x64dbg动态分析,我们可以发现调用了这个函数
2024-06-18T08:09:23.png
我们发现这个函数的调用堆栈有initterm,通过学习这个后,我们可以发现是全局变量的初始化过程。
2024-06-18T08:10:44.png
这个函数掌管了全局变量的初始化,可以注意到401010这个函数返回了一个BOOL,我们大概可以写出伪代码。

BOOL a = sub_401010();
int main(){
 ...
}

这样的话,sub_401010()函数会在main函数运行之前抢先运行,这样也就达到了提前修改代码(SMC)的目的。
SMC会把gethostbyname()函数的前5个字节jmp到下面这个函数
2024-06-18T08:13:04.png

name[0] = *a1 ^ 0x3D;
name[1] = a1[1] ^ 0x10;
name[2] = a1[2] ^ 0x1F;
name[3] = a1[3] ^ 0x17;
name[4] = a1[4] ^ 0x30;
name[5] = a1[5] ^ 0x2C;
name[6] = a1[6] ^ 0xB;
name[7] = a1[7];
name[8] = a1[8] ^ 0x35;
name[9] = a1[9] ^ 0x60;
name[10] = a1[10] ^ 0x16;
name[11] = a1[11] ^ 0x2C;
name[12] = a1[12] ^ 0x51;
name[13] = a1[13] ^ 0x43;
name[14] = a1[14] ^ 8;
name[15] = a1[15] ^ 0x45;
name[16] = a1[16] ^ 0x57;
name[17] = a1[17];
name[18] = a1[18];
name[19] = a1[19];
name[20] = a1[20];
name[21] = a1[21];

显然,通过SMC,动态修改了gethostbyname的行为,并且最后真正调用了gethostbyname
2024-06-18T08:14:06.png
相当于这是一个Hook,动态调试后,我们拿到真正的字符串

Just_An_APIH00k11.com

2024-06-18T08:15:22.png

DASCTF{f18566f93963f72f463fdfa2d163c37c}

R4ndom

程序在__libc_csu_init里面分别偷偷调用了a和b函数。
a函数负责进行ptrace反调试
2024-06-18T08:00:00.png
b函数则初始化了srand
2024-06-18T08:00:34.png
写一个cpp(在linux下运行,因为win和linux下rand不一样)
收集randTable(count=42)

#include <iostream>
using namespace std;

int main(){
        cout << "count:";
        int c;
        cin >> c;
        cout << "[";
        for(int i = 0;i < c; i++)
        {
                if(i != 0) cout << ", ";
                cout << rand();
        }

        cout << "]";
        return 0;
}

然后从程序里面抄出来Table放z3里面,并使用z3的Array和Select实现对于表的取值

table = [...]
z3Table = Array('z3-table', BitVecSort(8), BitVecSort(8))
for i in range(len(table)): s.add(z3Table[i] == table[i])
然后使用Select(x, i)  # x[i]
from z3 import *
table = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]
randTable = [1339546161, 401979842, 1084912717, 882247430, 1048050849, 1950252444, 1188894224, 1035316485, 1004145938, 292996994, 698777994, 1572612448, 187344395, 1998928827, 490869405, 1976471722, 861192779, 1683160727, 1968082326, 1836887756, 1806801098, 176220730, 2616407, 375557835, 1798606494, 1079742625, 657352025, 1384338308, 1249182411, 600742620, 1626386373, 441244924, 1002722462, 563815442, 1323492354, 2050773311, 366584239, 364902931, 938606148, 1370730177, 657899925, 1637384142] 
ans = [0x3513AB8AB2D7E6EE, 0x2EEDBA9CB9C97B02, 0x16E4F8C8EEFA4FBD, 0x383014F4983B6382, 0xEA32360C3D843607, 0xA655]

aa = b""
for i in ans:
    aa += i.to_bytes(8, 'little')
ans = list(aa)

s = Solver()
x = [BitVec(f"x{i}", 8) for i in range(42)]

table_z3 = Array('t1', BitVecSort(8), BitVecSort(8))
for i in range(len(table)): s.add(table_z3[i] == table[i])

for i in range(42):
    v3 = x[i]
    v4 = randTable[i]
    s.add(Select(table_z3, (16 * ((v3 + v4 % 255) >> 4) + 15) & (v3 + v4 % 255)) == ans[i])
    s.add(x[i] >= 0x20)
    s.add(x[i] <= 0x7f)

print(s.check())
m = s.model()
for i in range(42):
    print(chr(m[x[i]].as_long()), end='')
sat
flag{B8452786-DD8E-412C-E355-2B6F27DAB5F9}