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的动态调试我们就可以得到真实的调用流程!
- 程序使用TLS函数hook掉main函数的数据对比函数也就是CMP_411348会jmp到0x412000
- 这里会进入main函数开始输入flag,将数据进行xxtea加密,然后进入被hook的函数CMP_411348
- 也就是进入0x412000,这里存在try catch结构
- 在try结构内还注册了一个seh处理函数0x411424
- try结构里还存在一个int 3用来触发异常
- catch结构有2个
- 一个是00412073,会给eax赋值1
- 一个是00412079,会执行最后是数据判断流程
- 在触发int 3 后会进入0x411424异常处理函数
- 处理完成后会进入第一个catch结构00412073,这个位置会给eax赋值1,就会导致再次调用0x411424函数
- 最后又会调用最后一个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加密会被调用两次!
整理一下整个加密逻辑:
- 先将数据进行xxtea加密
- 再将加密的数据进行两轮的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
"""