[VNCTF2023]-reserve-jijiji-WP

[VNCTF2023] Reserve-jijiji-WP

先IDA开开
在这里插入图片描述findcrypt插件看到aes加密
down一下师傅的源码看看:

// 创建一个要挂上删除队列的临时文件

HANDLE hTargetFile = CreateFile(lpszTargetFile, GENERIC_READ | GENERIC_WRITE | DELETE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);

// 把文件挂上删除队列

ntAux = pfNtSetInformationFile(hTargetFile, &stIOStatus, &stFileInfo, sizeof(stFileInfo), FILE_INFORMATION_CLASS::FileDispositionInformation);

 

// 搜索文件自身名字叫做shell的资源,读出来解密

HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_SHELL2), "shell");

DWORD dwSize = SizeofResource(NULL, hRsrc);

HGLOBAL hGlobal = LoadResource(NULL, hRsrc);

LPVOID pBuffer = LockResource(hGlobal);

memcpy(lpSource, pBuffer, dwFileSize);

memcpy(&dwOriginalSize, (void *)((unsigned long long int)pBuffer + dwFileSize), 4);

GlobalUnlock(hGlobal);

 

// 将文件映射入内存

ntAux = pfNtCreateSection(&hSectionHandle, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hTargetFile);

 

// 获取解密程序的入口点

GetImageEntryPointRVA(hTargetFile, imageEntryPointRva, dwOriginalSize)

 

// 创建进程

ntAux = pfNtCreateProcess(&hProcess, PROCESS_ALL_ACCESS, nullptr, GetCurrentProcess(), TRUE, hSectionHandle, NULL, NULL);

// 获取刚创建进程的PBI

ntAux = pfNtQueryInformationProcess(hProcess, ProcessBasicInformation, &stPBI, sizeof(stPBI), nullptr);

// 传入环境参数

ntAux = pfRtlCreateProcessParameters(&lpstProcessParams, (PUNICODE_STRING)&uTargetPath, (PUNICODE_STRING)&uDllDir, (PUNICODE_STRING)&uCurrentDir, (PUNICODE_STRING)&uTargetPath, lpEnv, (PUNICODE_STRING)&uWindowName, nullptr, nullptr, nullptr);

LPVOID lpParams = WriteParamsToProcess(hProcess, lpstProcessParams);

ReadPEB(hProcess, stPI, stPEBCopy)

WriteParamsToPEB(lpParams, hProcess, stPI)

ReadPEB(hProcess, stPI, stPEBCopy)

 

// 启动创建的进程里的主线程

ntAux = pfNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)ullProcEntry, NULL, FALSE, 0, 0, 0, NULL);

// 程序开始运行

与函数对照观看

[VNCTF2023]-reserve-jijiji-WP_第1张图片出题的s0rry师傅是静态那那个包然后aes做的
F1p1p1师傅用了动调,直接dump

请添加图片描述IDA动调到下断点解密结束
看压进栈的参数lpbuffer
[VNCTF2023]-reserve-jijiji-WP_第2张图片
按几下d

请添加图片描述[VNCTF2023]-reserve-jijiji-WP_第3张图片

接着就可以看到是一个exe文件(MZ文件头)

大小是16000h[VNCTF2023]-reserve-jijiji-WP_第4张图片

dump!

对dump出的文件进行IDA静态分析

字符串看到了那个成功之类的标志,但是没有引用

下面有一个杀进程的字符串,定位发现是反调试

请添加图片描述

	__int64 __fastcall sub_1400064F0(__int64 a1, __int64 a2){
  char *v2; // rdi
  __int64 i; // rcx
  const char *v4; // rax
  DWORD LastError; // eax
  char v7[32]; // [rsp+0h] [rbp-20h] BYREF
  char v8; // [rsp+20h] [rbp+0h] BYREF
  HANDLE hSnapshot; // [rsp+28h] [rbp+8h]
  PROCESSENTRY32 pe; // [rsp+50h] [rbp+30h] BYREF
  BOOL v11; // [rsp+194h] [rbp+174h]
  unsigned int v12; // [rsp+1B4h] [rbp+194h]
  DWORD CurrentProcessId; // [rsp+1D4h] [rbp+1B4h]
  DWORD th32ProcessID; // [rsp+1F4h] [rbp+1D4h]
  char v15[536]; // [rsp+220h] [rbp+200h] BYREF
  char v16[64]; // [rsp+438h] [rbp+418h] BYREF
  LPVOID lpAddress; // [rsp+478h] [rbp+458h]
  DWORD flOldProtect[9]; // [rsp+494h] [rbp+474h] BYREF
  LPCSTR lpFileName; // [rsp+4B8h] [rbp+498h]
  char v20[64]; // [rsp+718h] [rbp+6F8h] BYREF
  char v21[64]; // [rsp+758h] [rbp+738h] BYREF
  char v22[48]; // [rsp+798h] [rbp+778h] BYREF
  __int64 v23; // [rsp+7C8h] [rbp+7A8h]
  __int64 v24; // [rsp+7D0h] [rbp+7B0h]
  __int64 v25; // [rsp+7D8h] [rbp+7B8h]
  __int64 v26; // [rsp+7E0h] [rbp+7C0h]
  __int64 v27; // [rsp+7E8h] [rbp+7C8h]
  __int64 v28; // [rsp+7F0h] [rbp+7D0h]
  v2 = &v8;
  for ( i = 362i64; i; --i )
  {
    *(_DWORD *)v2 = 0xCCCCCCCC;                 // v2赋值
    v2 += 4;
  }
  sub_14000148D((__int64)&unk_1400180F5);
  hSnapshot = CreateToolhelp32Snapshot(2u, 0);
  v11 = Process32First(hSnapshot, &pe);
  v12 = -1;
  CurrentProcessId = GetCurrentProcessId();
  while ( v11 )
  {
    if ( CurrentProcessId == pe.th32ProcessID )
    {
      th32ProcessID = pe.th32ProcessID;
      v12 = sub_140001267(pe.th32ProcessID);    // 反调试
    }
    v11 = Process32Next(hSnapshot, &pe);
  }
  if ( v12 != -1 )                              // 被杀了
  {
    sub_140001479(v12, v15, 10i64);
    v23 = sub_140001217(v20, " > nul");
    v24 = v23;
    v25 = sub_140001217(v21, "taskkill -f /pid ");
    v26 = v25;
    v27 = sub_14000143D(v22, v25, v15);
    v28 = v27;
    sub_140001069(v16, v27, v24);
    sub_1400010F0(v22);
    sub_1400010F0(v21);
    sub_1400010F0(v20);
    v4 = (const char *)sub_14000106E(v16);
    system(v4);
    sub_1400010F0(v16);
  }
  lpAddress = (LPVOID)sub_140001357((__int64)sub_1400010C3);// sub_1400010C3应该就是代指跑的exe,即flag验证
  VirtualProtect(lpAddress, 1024ui64, 64u, flOldProtect);// 保护内存空间
  lpFileName = *(LPCSTR *)(a2 + 8);
  qword_140014470 = *(_QWORD *)(a2 + 16);
  if ( !DeleteFileA(lpFileName) )               // 不存在就报错
  {
    LastError = GetLastError();
    sub_14000123A("%d", LastError);
  }
  sub_1400010FF((__int64)sub_1400010C3, qword_140014470);
  sub_1400010C3();                              // 运行函数
  ((void (__fastcall *)(char *, void *))sub_1400013C5)(v7, &qword_1400101C8);
  return 0i64;
}

这就是关键函数

这个函数有两个参数,a2赋给了lpfilename,向上xref找到a1a2

[VNCTF2023]-reserve-jijiji-WP_第5张图片

非常的美丽

命令行参数argc和argv的解释

第一个参数argc,用于参数计数,其值等于命令行中参数的个数;第二个参数argv,用于参数向量,是一个指向字符串数组的指针。

[VNCTF2023]-reserve-jijiji-WP_第6张图片

在IDA的attach中可以看到进程参数(乐)

开跑!

ID 名称
3324 [64] VNctf2023 C:\Users\êˉèó\Desktop\VNCTF2023\jijijiWP\jijiji.exe 16000

在调试器上加参数

在反调试下断,过反调

NtQueryInformationProcess

函数原型:

NTSYSAPI NTSTATUS NTAPI NtQueryInformationProcess (
  IN HANDLE         ProcessHandle,       // 进程句柄
  IN PROCESSINFOCLASS   InformationClass,      // 信息类型
  OUT PVOID         ProcessInformation,     // 缓冲指针
  IN ULONG          ProcessInformationLength, // 以字节为单位的缓冲大小
  OUT PULONG         ReturnLength OPTIONAL     // 写入缓冲的字节数
);
1234567

第二个参数是一个枚举类型,其中与反调试有关的成员有ProcessDebugPort(0x7)、ProcessDebugObjectHandle(0x1E)和ProcessDebugFlags(0x1F)三种

ProcessDebugPort(0x7)
进程处于调试状态时,操作系统会为他分配1个调试端口(debug port) , InformationClass设为ProcessDebugPort(0x07) 时,调用NtQueryInformationProcess()函数就可以获取调试端口。若处于调试状态 , 第三个参数会被置为0xFFFFFFFF(-1);若处于非调试状态,第三个参数值会被设置为0

ProcessDebugObjectHandle(0x1E)
调试进程时,会生成一个调试对象(Debug Obiect)。 NtQueryInformationProcess() 第二个参数值为0x1E时 , 函数的第三个参数就能获取到调试对象句柄 ,进程处于调试状态->调试句柄存在->返回值不为 NULL ;处于非调试状态 , 返回值为 NULL。

ProcessDebugFlags(0x1F)
调试标志:Debug Flags 。检测调试标志的值也可以判断进程是否处于被调试状态。当 NtQueryInformationProcess() 第二个参数为0x1F时,第三个参数:调试状态:0;非调试状态:1

破解方法:直接 HOOK API;修改返回值;利用 OD 插件 strong OD 中 KernelMode 可以绕过。

[VNCTF2023]-reserve-jijiji-WP_第7张图片

第一次跑飞了,再来

[VNCTF2023]-reserve-jijiji-WP_第8张图片

跑到函数处按F7进入

结果捅了数据窝,选中这一堆,按c转化为代码,选中按p转化为函数,F5!!!

SeeTruth

[VNCTF2023]-reserve-jijiji-WP_第9张图片
[VNCTF2023]-reserve-jijiji-WP_第10张图片[VNCTF2023]-reserve-jijiji-WP_第11张图片

有一个加密右移,应该是tea加密?

加密后和byte_140014000进行比较

写脚本QAQ

#include
using namespace std;
void decrypt(unsigned int *de_words, int* key, int round){
	int delta = 0x88408067;
	unsigned int l =de_words[0],r = de_words[1],sum = 0;
	sum = delta * round;
	for(int i=0;i<round;i++){
		sum-=delta;
		r-=(((l << 4) ^ (l >> 5)) + l) ^ (sum + key[(sum >> 11) & 3]);
		l -= (((r << 4) ^ (r >> 5)) + r) ^ (sum + key[sum & 3])^sum;
	}
	de_words[0]=l;
	de_words[1]=r;

}

int main(){
	
unsigned int encrypt_flag[] =
{
    0xADD4F778, 0xA6D7F132, 0x61813290, 0x2D4A40A6,
	0x00B05F11, 0xB6D59424, 0x231BBFC6, 0xCD405B31, 
};
int key[4]={98,111,109,98};
for(int i=0;i<4;i++){
    decrypt(encrypt_flag+i*2,key,33); 
}

printf("flag{");
for(int i=0;i<8;i++){
   printf("%c%c%c%c",*((char*)&encrypt_flag[i]+0),*((char*)&encrypt_flag[i]+1),*((char*)&encrypt_flag[i]+2),*((char*)&encrypt_flag[i]+3));
}
printf("}");
    
/*encrypt
for ( k = 0; k < 4; ++k ){
    v9 = (int *)&v4[8 * k];
    v10 = *v9;
    v11 = v9[1];
    v12 = 0;
    v13 = 0x88408067;
    for ( m = 0; m < 33; ++m )
    {
      v10 += v12 ^ (v6[v12 & 3] + v12) ^ (v11 + ((v11 >> 5) ^ (16 * v11)));
      v11 += (v6[(v12 >> 11) & 3] + v12) ^ (v10 + ((v10 >> 5) ^ (16 * v10)));
      v12 += v13;
    }
    *v9 = v10;
    v9[1] = v11;
  }
*/}

[VNCTF2023]-reserve-jijiji-WP_第12张图片

赢!!!!!!!!

flag{2d326e43eb8fea8837737fc0f50f83f2}

你可能感兴趣的:(网络安全,物联网)