DASCTFXGFCTF2024 四月Re-unwind(SEH混淆逻辑)

SEH异常处理机制(Windows)
TLS反调试
Inline Hook内联汇编Hook
XXTEA算法
XTEA算法

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  FILE *v3; // eax
  char v5; // [esp+0h] [ebp-D8h]
  char v6; // [esp+0h] [ebp-D8h]
  char v7; // [esp+0h] [ebp-D8h]
  char v8; // [esp+0h] [ebp-D8h]

  __CheckForDebuggerJustMyCode(&unk_41D0A3);
  printf("Wellcome~~~\n", v5);
  printf("Please input your flag:\n", v6);
  v3 = _acrt_iob_func(0);
  fgets(Buffer, 33, v3);
  printf("\nChecking......\n", v7);
  xxtea(Buffer, 8, &unk_41B048);
  if ( CMP_411348(Buffer, &unk_41B000) )
    printf("You are right!  Congratulations!\n", v8);
  else
    printf("Sorry, there are something wrong...\n", v8);
  return 0;
}

发现主逻辑,先经过xxtea加密

int __stdcall TlsCallback_0_0(int a1, int a2, int a3)
{
  int result; // eax

  result = __CheckForDebuggerJustMyCode(&unk_41D0A3);
  if ( a2 == 1 )
    return sub_41105A(CMP_411348);
  return result;
}

发现存在tls线程函数:

BOOL __cdecl sub_411AC0(int a1)
{
  BOOL result; // eax
  SIZE_T NumberOfBytesRead[3]; // [esp+D0h] [ebp-18h] BYREF
  HANDLE hProcess; // [esp+DCh] [ebp-Ch]

  __CheckForDebuggerJustMyCode(&unk_41D0A3);
  sub_411140(a1);
  hProcess = GetCurrentProcess();
  NumberOfBytesRead[0] = 0;
  result = ReadProcessMemory(hProcess, lpBaseAddress, &unk_41B30C, nSize, NumberOfBytesRead);
  if ( result )
    return WriteProcessMemory(hProcess, lpBaseAddress, &byte_41B31C, nSize, NumberOfBytesRead);
  return result;
}

发现这里会hook掉CMP_411348函数!
调用链是:CMP_411348-》411c20(被hook)-》jmp 4110e6-》jmp 412000


然后就会发现最终的调用函数是412000:

push ebp
mov ebp,esp
push 0xFFFFFFFE
push unwind.41A3D0
push 0x4149D0
mov eax,dword ptr fs:[0]  //将unwind.41A3D0注册为SEH异常处理函数
push eax                  //unwind.41A3D0里面存在两个地址
add esp,0xFFFFFF1C
push ebx
push esi
push edi
lea edi,dword ptr ss:[ebp-0x34]
mov ecx,0x7
mov eax,0xCCCCCCCC
rep stosd 
mov eax,dword ptr ds:[0x41B080]
xor dword ptr ss:[ebp-0x8],eax
xor eax,ebp
mov dword ptr ss:[ebp-0x1C],eax
push eax
lea eax,dword ptr ss:[ebp-0x10]
mov dword ptr fs:[0],eax
mov dword ptr ss:[ebp-0x18],esp
mov dword ptr ss:[ebp+0xC],unwind.41B024
 __try { // __except at loc_412079
	mov dword ptr ss:[ebp-0x4],0x0
	push 0x411424
	push dword ptr fs:[0] //注册0x411424函数为SEH异常处理函数
	mov dword ptr fs:[0],esp
	int3     //这里会触发seh会调用0x411424函数
}

mov dword ptr ss:[ebp-0x4],0xFFFFFFFE 
jmp 0x4120C6
	
  __except filter // owned by 41204F
	mov eax,0x1//调用完一次0x411424函数后会来到这里,由于这里把eax变为了1,就会再次调用0x411424函数
	ret 
	
  __except(loc_412073) // owned by 41204F
	mov esp,dword ptr ss:[ebp-0x18]  //调用完两次0x411424函数后会来到这里
	mov esi,esp
	call dword ptr ds:[<GetCurrentProcess>]
	cmp esi,esp
	call 0x411285
	mov dword ptr ss:[ebp-0x24],eax
	mov dword ptr ss:[ebp-0x30],0x0
	mov esi,esp
	lea eax,dword ptr ss:[ebp-0x30]
	push eax
	mov ecx,dword ptr ds:[0x41B308]
	push ecx
	push unwind.41B30C
	mov edx,dword ptr ds:[0x41B32C]
	push edx
	mov eax,dword ptr ss:[ebp-0x24]
	push eax
	call dword ptr ds:[<WriteProcessMemory>] //这里会修复之前的hook
	cmp esi,esp
	call 0x411285
	mov dword ptr ss:[ebp-0x4],0xFFFFFFFE
	mov eax,dword ptr ss:[ebp+0xC]
	push eax
	mov ecx,dword ptr ss:[ebp+0x8]
	push ecx
	call 0x411348    //调用最终的CMP对比函数!真实的目标数据在这里
	add esp,0x8
	push edx
	mov ecx,ebp
	push eax
	lea edx,dword ptr ds:[0x412110]
	call 0x411221
	pop eax
	pop edx
	mov ecx,dword ptr ss:[ebp-0x10]
	mov dword ptr fs:[0],ecx
	pop ecx
	pop edi
	pop esi
	pop ebx
	mov ecx,dword ptr ss:[ebp-0x1C]
	xor ecx,ebp
	call 0x411177
	add esp,0xF4
	cmp ebp,esp
	call 0x411285
	mov esp,ebp
	pop ebp
	ret 

通过ida的动态的调试和xdbg的动态调试我们就可以得到真实的调用流程!

  1. 程序使用TLS函数hook掉main函数的数据对比函数也就是CMP_411348会jmp到0x412000
  2. 这里会进入main函数开始输入flag,将数据进行xxtea加密,然后进入被hook的函数CMP_411348
  3. 也就是进入0x412000,这里存在try catch结构
    • 在try结构内还注册了一个seh处理函数0x411424
    • try结构里还存在一个int 3用来触发异常
    • catch结构有2个
      • 一个是00412073,会给eax赋值1
      • 一个是00412079,会执行最后是数据判断流程
  4. 在触发int 3 后会进入0x411424异常处理函数
  5. 处理完成后会进入第一个catch结构00412073,这个位置会给eax赋值1,就会导致再次调用0x411424函数
  6. 最后又会调用最后一个catch结构,00412079这里会修复CMP_411348函数的hook,并且判断flag是否正确

最后在来分析一下0x411424函数:

int sub_411960()
{
  int i; // [esp+D0h] [ebp-8h]

  __CheckForDebuggerJustMyCode(&unk_41D0A3);
  for ( i = 0; i < 4; ++i )
    sub_4111B8(0x24u, &Buffer[8 * i], &unk_41B048);
  return 1;
}

int __cdecl sub_411D90(unsigned int a1, unsigned int *a2, int a3)
{
  int result; // eax
  unsigned int v4; // [esp+DCh] [ebp-2Ch]
  unsigned int v5; // [esp+E8h] [ebp-20h]
  unsigned int v6; // [esp+F4h] [ebp-14h]
  unsigned int i; // [esp+100h] [ebp-8h]

  __CheckForDebuggerJustMyCode(&unk_41D0A3);
  v6 = *a2;
  v5 = a2[1];
  v4 = 0;
  for ( i = 0; i < a1; ++i )
  {
    v6 += (*(a3 + 4 * (v4 & 3)) + v4) ^ (v5 + ((v5 >> 5) ^ (16 * v5)));
    v4 -= 0x61C88647;
    v5 += (*(a3 + 4 * ((v4 >> 11) & 3)) + v4) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
  }
  *a2 = v6;
  result = 4;
  a2[1] = v5;
  return result;
}

可以清晰的发现这个函数是xtea加密!!由之前的分析可知这个xtea加密会被调用两次!

整理一下整个加密逻辑:

  1. 先将数据进行xxtea加密
  2. 再将加密的数据进行两轮的xtea加密在来进行数据对比!

下面是我复现的加密算法:
加密算法:

#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
 
void btea(uint32_t *v, int n, uint32_t const key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)            /* Coding Part */
    {
        rounds = 6 + 52/n;
        sum = 0;
        z = v[n-1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p=0; p<n-1; p++)
            {
                y = v[p+1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n-1] += MX;
        }
        while (--rounds);
    }
    else if (n < -1)      /* Decoding Part */
    {
        n = -n;
        rounds = 6 + 52/n;
        sum = rounds*DELTA;
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p=n-1; p>0; p--)
            {
                z = v[p-1];
                y = v[p] -= MX;
            }
            z = v[n-1];
            y = v[0] -= MX;
            sum -= DELTA;
        }
        while (--rounds);
    }
}
/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */
 
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
    for (i=0; i < num_rounds; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
}
 
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
    for (i=0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0]=v0; v[1]=v1;
}
 

 
int main()
{
    uint32_t v[8]= {0x61616161, 0x61616161, 0x61616161, 0x61616161, 0x61616161, 0x61616161, 0x61616161, 0x61616161};
    uint32_t k[4]= {0x44,0x41,0x53,0x21};
    
    
    int n = 8; //n的绝对值表示v的长度,取正表示加密,取负表示解密
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    printf("加密前原始数据:%x %x %x %x %x %x %x %x\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]);
    btea(v, n, k);
    printf("xxtea加密后:%x %x %x %x %x %x %x %x\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]);
    uint32_t temp[2] = {0,0};
    for(int i = 0;i<4;i++)
    {
    	temp[0] = v[i*2];
    	temp[1] = v[i*2+1];
    	encipher(0x24,temp,k);
    	v[i*2] = temp[0];
    	v[i*2+1] = temp[1];
    }
    printf("xtea加密后:%x %x %x %x %x %x %x %x\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]);

    return 0;
}


解密脚本也就出来了!

#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
 
void btea(uint32_t *v, int n, uint32_t const key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)            /* Coding Part */
    {
        rounds = 6 + 52/n;
        sum = 0;
        z = v[n-1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p=0; p<n-1; p++)
            {
                y = v[p+1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n-1] += MX;
        }
        while (--rounds);
    }
    else if (n < -1)      /* Decoding Part */
    {
        n = -n;
        rounds = 6 + 52/n;
        sum = rounds*DELTA;
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p=n-1; p>0; p--)
            {
                z = v[p-1];
                y = v[p] -= MX;
            }
            z = v[n-1];
            y = v[0] -= MX;
            sum -= DELTA;
        }
        while (--rounds);
    }
}

/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */
 
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
    for (i=0; i < num_rounds; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
}
 
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
    for (i=0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0]=v0; v[1]=v1;
}
 

int main()
{
    uint32_t v[8]= {0x87AAA7C1, 0x857321B6, 0x0E71D28C, 0xCADF39F2, 0x58EFCA14, 0xD7E7D9D8, 0xF29F5C5D, 0x5F5ED45E};
    uint32_t k[4]= {0x44,0x41,0x53,0x21};
    
    
    int n = 8; //n的绝对值表示v的长度,取正表示加密,取负表示解密
    
    uint32_t temp[2] = {0,0};
    printf("解密前原始数据:%x %x %x %x %x %x %x %x\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]);
    for(int i = 0;i<4;i++)
    {
    	temp[0] = v[i*2];
    	temp[1] = v[i*2+1];
    	decipher(0x24,temp,k);
    	v[i*2] = temp[0];
    	v[i*2+1] = temp[1];
    }
    for(int i = 0;i<4;i++)
    {
    	temp[0] = v[i*2];
    	temp[1] = v[i*2+1];
    	decipher(0x24,temp,k);
    	v[i*2] = temp[0];
    	v[i*2+1] = temp[1];
    }
    printf("xtea解密后:%x %x %x %x %x %x %x %x\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]);
    btea(v, -n, k);
    int ch = 0;
    printf("原始数据:");
    for(int i=0;i<8;i++)
    {
    	printf("%c%c%c%c",v[i]&0xff,(v[i]&0xff00)>>8,(v[i]&0xff0000)>>16,(v[i]&0xff000000)>>24);
    }
    printf("\nxxtea解密后:%x %x %x %x %x %x %x %x\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]);
    return 0;
}
"""
┌──(kali㉿kali)-[~/Desktop]
└─$ ./a           
解密前原始数据:87aaa7c1 857321b6 e71d28c cadf39f2 58efca14 d7e7d9d8 f29f5c5d 5f5ed45e
xtea解密后:58be7427 3394cd3a 9324225f 1b82dbb 4b7d4b90 deefbf8f 911b6e96 38069288
原始数据:DASCTF{Gr3@t!Y0u_have_50lv3d_1T}
xxtea解密后:43534144 477b4654 74403372 75305921 7661685f 30355f65 6433766c 7d54315f
"""
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值