Hgame-re-week3

发布于 2024-02-27  65 次阅读


1.findme:

用die查一下可以看到是64位的程序,使用64位的ida打开:

可以看到给出了两个像是flag的东西,事实上全都是错的 ,那么我们的关注点就在Buffer上面了:

这里出现了MZ 90,那么就猜测是exe文件头,转化为数组之后导出数组(shift + E)

可以看到像4D5A之间有很多个零,所以我们就得把之间多于的零去掉,使用脚本处理:

首先在shift + E的窗口下面的output file改写为ral,然后再在这一个窗口打开ida脚本编写的地方:

File ->script command...

编写脚本:

f = open("ral", "rb").read()
arr = [f[i] for i in range(0, len(f), 4)]
open("dump.exe", "wb").write(bytes(arr))
print("done!")

f = open("ral", "rb").read():这一个代码的作用是打开一个名为“ral”的文件(就是之前我们设置名字的那个,)以二进制模式(“rb”)读取文件的内容,并将内容存储在变量 f

arr = [f[i] for i in range(0, len(f), 4)]:这一行代码的作用是创建一个表arr,其中每一个元素使变量 f 中的数据,从零开始,每隔4给位置取一个元素

open("dump.exe","wb").write(bytes(arr)):这一行代码的作用是以二进制写入模式(“wb”)打开一个名为 “dump.exe” 的文件,然后将列表 arr 中的数据转换为字节类型,并写入到 “dump.exe” 文件中。实际上,这行代码将提取的数据写入到一个新的二进制文件 “dump.exe” 中。

print("done!"):这一行代码就是输出消息代表执行完毕

还可以使用另外一个方法,使用vs2022写一个脚本来执行:

#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<string>
#define N 100000
using namespace  std;
int main() {
   FILE *fp,*v5;
   int n=0,c;
   unsigned char str[N + 1],a,b;
   fp = fopen("findme.exe", "rb");
   v5 = fopen("real.exe", "wb");
   while ( feof(fp) ==0)
  {
       n++;
       a=fgetc(fp);
       //break;
       b=a;
         if(n>0x2440)
        {
             if(n%4==1)
            {
                 fputc(b, v5);
            }
        }
  }

   //操作结束后关闭文件
   fclose(fp);
   fclose(v5);
   return 0;
}

如我把这个代码放进一个名为CAOGAO项目里面,然后再把findme拖进其所在的文件夹里面

然后再vs2022里面直接运行就可以了,那个real.exe程序就是我们提取出来的文件了(注意是32位的)

打开我们可以看到,里面有很多的0C7h花指令,数量非常多那么我们就要用脚本去把花指令给去掉:

startaddr = 0x401068 #起始位置
endaddr = 0x401236#终点位置
for i in range(startaddr,endaddr):
if get_wide_byte(i) == 0xC7:#需要去除掉花指令

    patch_byte(i,0x90)#nop指令的16进制为90h
    print("[+] Addr {} is patched".format(hex(i)))

然后在函数开始的地方按U,P,F5就可以得到程序了

输出正确的条件是 byte_403490[v5]byte_402148[v5] 的值相等。

可以看到这里的加密是一段rc4加密

密钥初始化没问题,但是在rc4加密的地方有魔改,多了一个换位

1 #define _CRT_SECURE_NO_WARNINGS 1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include<bits/stdc++.h>
6 using namespace std;
7 void rc4(unsigned char* key, unsigned long key_length, unsigned char* data,
unsigned long data_length) {
8 unsigned char s[256] ;
9 int k[256];
10
11 for (int i = 0; i < 256; i++) {
12 s[i] = 256 - i;
13 k[i] = key[i % key_length];
14 }
15
16 for (int i = 0, j = 0; i < 256; i++) {
17 j = (j + s[i] + k[i]) % 256;
18 swap(s[i], s[j]);
19 }
20
21 int i = 0, j = 0, t = 0;
22 for (int n = 0; n < data_length; n++) {
23 i = (i + 1) % 256;
24 j = (j + s[i]) % 256;
25 swap(s[i], s[j]);
26 int t = (s[i] + s[j]) % 256;
27 data[n] -= s[256 - t];
28 }
29 }
30
31 int main() {
32 unsigned char key[] = "deadbeef";
33 unsigned char data[] = { 0x7D, 0x2B, 0x43, 0xA9, 0xB9, 0x6B, 0x93, 0x2D,
0x9A, 0xD0,
34 0x48, 0xC8, 0xEB, 0x51, 0x59, 0xE9, 0x74, 0x68, 0x8A, 0x45,
35 0x6B, 0xBA, 0xA7, 0x16, 0xF1, 0x10, 0x74, 0xD5, 0x41, 0x3C,
36 0x67, 0x7D };
37 unsigned long key_length =strlen((char*)key);
38 unsigned long data_length = strlen((char*)data);
39 rc4(key, key_length, data, data_length);
40
41 for (int i = 0; i < data_length; i++) {
42 printf("%c", data[i]);
43 }
44 printf("\n");
45 }

运行脚本就可解出来了

2.crackme:

64位的程序,直接用64位ida打开看看:

当看到main函数的末尾的时候发现程序突然就停止了,并且末尾有一个异常函数

打开流程图:

发现里面有很多的catc,try,而catch是不能被正常反调试,所以我们只能把这三处内存地址dump下来再用ida打开再来反编译:

 int __fastcall sub_0(int a1, _DWORD *a2)
{
 a2[9] += (a2[(a2[12] & 3) + 16] + a2[12]) ^ (a2[11] + ((a2[11] >> 5) ^ (16 * a2[11])));
 return -14566;
}int __fastcall sub_0(int a1, _DWORD *a2)
{
 a2[11] += (a2[((a2[12] >> 11) & 3) + 16] + a2[12]) ^ (a2[9] + ((a2[9] >> 6) ^ (32 * a2[9])));
 return -14608;
}int __fastcall sub_0(int a1, int a2)
{
 *(_DWORD *)(a2 + 48) ^= *(_DWORD *)(a2 + 60);
 return -14610;
}

把这三段catch弄出来就是这个样子,然后就可以推断出是xtea加密,并且有一个地方有魔改

然后就是写脚本了:

#include <stdio.h>
#include <stdint.h>
#include<iostream>
using namespace std;

void decrypt ( unsigned int* v, unsigned int* k) {
   unsigned int v0=v[0], v1=v[1];
   unsigned int delta=857870677;  
   unsigned int sum=0, i;        
   for(i=0;i<32;i++)
  {
       sum^=delta;
  }            
   for (i=0; i<32; i++) {      
       sum ^= delta;    
       v1 -= (((v0 >> 6) ^ ( v0 << 5 )) + v0) ^ (k[(sum >> 11) & 3] + sum);
       v0 -= (((v1 >> 5) ^ (16 * v1)) + v1) ^ (k[sum & 3] + sum);             //解密时将加密算法的顺序倒过来,还有+=变为-=
  }                                              
   v[0]=v0; v[1]=v1;//解密后再重新赋值
}


int main()
{
   unsigned int v[8];
   v[0] = 0x32FC31EA;
 v[1] = 0xF0566F42;
 v[2] = 0xF905B0B2;
 v[3] = 0x5F4551BE;
 v[4] = 0xFB3EFCBB;
 v[5] = 0x6B6ADB30;
 v[6] = 0x4839879;
 v[7] = 0x2F4378DF;
   unsigned int k[4]={1234,2345,3456,4567};
   for(int i=0;i<8;i+=2)
  {
       decrypt(v+i, k);
  }
   unsigned char a;
   for(int i=0;i<8;i++)
  {
       for(int k=0;k<4;k++)
      {
           a=*((unsigned char *)(&v[i])+k);
           printf("%c",a);
      }
    }

   return 0;
}

3.mystery:

die打开可以看到是一个操作系统是ubuntu的应用,先用ida打开看一看:

可以看到main函数里面什么也没有,只有一个ptrace PTRACE_ATTACH,这个函数在这里的作用是反调试:

跟踪自己ptrace:因为,一个进程在同一个时间只能被一个进程跟踪。如果在启动的时候,就调用了这个ptrace PTRACE_ATTACH跟踪了自己,那么这个进程将无法被其他进程附加。

直接shift + F12查看发现有如Wrong!please try again! 这样的话,那么我们就点进去交叉引用就找到一个sub_1100的函数

通过分析代码可以发现s2应该就是我们的密文,然后sub_1500里面引用s1(我们输入的值),那么就可以发现sub_150就是我们的加密函数,然后sub_13E0的第一个参数是S盒,第二个参数是key;

对sub_13E0进行引用,发现还有一个地方引用了的:

然后发现是sub_1220函数

这个函数对我们的key进行了修改,那么我们就先把key解出来:

1 //dec_key
2 #include <bits/stdc++.h>
3 using namespace std;
4 void init(unsigned char *s, unsigned char *key, unsigned long len)
5 {
6 int t[256] = {0};
7 char tmp = 0;
8 for (int i = 0; i < 256; ++i)
9 {
10 s[i] = i;
11 t[i] = key[i % len];
12 }
13 int j = 0;
14 for (int i = 0; i < 256; ++i)
15 {
16 j = (j + s[i] + t[i]) % 256;
17 swap(s[i], s[j]);
18 }
19 }
20 void crypt1(unsigned char *s, unsigned char *data, unsigned long len)
21 {
22 int i = 0, j = 0, t = 0;
23 for (int k = 0; k < len; ++k)
24 {
25 i = (i + 1) % 256;
26 j = (j + s[i]) % 256;
27 swap(s[i], s[j]);
28 t = (s[i] + s[j]) % 256;
29 data[k] ^= s[t];
30 }
31 }
32
33 unsigned char key1[] = "keykey";
34 unsigned char key[] = "ban_debug!";
35 unsigned char s[256];
36
37 void decrypt_key()
38 {
39 int len = strlen((char*)key1);
40 init(s, key1, len);
41 len = strlen((char*)key);
42 crypt1(s, key, len);
43 for (int i = 0; i < strlen((char*)key); i++)
44 {
45 printf("%d, ", key[i]);
46 }
47 printf("\n");
48 }
49 int main()
50 {
51 memset(s, 0, sizeof(s));
52 decrypt_key();
53 memset(s, 0, sizeof(s));
54 }
55
56 //key[] = {105, 13, 90, 178, 64, 234, 25, 63, 47, 106};//exp
2 #include <bits/stdc++.h>
3 using namespace std;
4 void init(unsigned char *s, unsigned char *key, unsigned long len)
5 {
6 int t[256] = {0};
7 char tmp = 0;
8 for (int i = 0; i < 256; ++i)
9 {
10 s[i] = i;
11 t[i] = key[i % len];
12 }
13 int j = 0;
14 for (int i = 0; i < 256; ++i)
15 {
16 j = (j + s[i] + t[i]) % 256;
17 swap(s[i], s[j]);
18 }
19 }
20 void crypt(unsigned char *s, unsigned char *data, unsigned long len)
21 {
22 int i = 0, j = 0, t = 0;
23 char tmp;
24 for (int k = 0; k < len; ++k)
25 {
26 i = (i + 1) % 256;
27 j = (j + s[i]) % 256;
28 swap(s[i], s[j]);
29 t = (s[i] + s[j]) % 256;
30 data[k] += s[t]; //魔改rc4
31 }
32 }
33 unsigned char cipher[] = {80, 66, 56, 77, 76, 84, 144, 111, 254, 111, 188, 105,
185, 34, 124, 22, 143, 68, 56, 74, 239, 55, 67, 192, 162, 182, 52, 44};
34 unsigned char key[] = {105, 13, 90, 178, 64, 234, 25, 63, 47, 106};
35 unsigned char s[256];
36 int main()
37 {
38 int len = strlen((char*)key);
39 init(s, key, len);
40 len = strlen((char*)cipher);
41 crypt(s, cipher, len);
42 puts((char*)cipher);
43 return 0;
44 }

然后再把结果算出来就好了

4.encrypt:

用ida打开发现里面全部都是API

通过查询可以得知:是个标准的AEX—CBC加密

BCryptOpenAlgorithmProvider 函数的作用是打开指定的密码算法提供程序,使得应用程序可以使用该程序所提供的密码算法进行加密、解密等操作。具体来说,该函数用于初始化一个算法对象,以便后续使用其进行各种加密操作。

这里的作用是在解读字符串“ChainingModeCBC”

可以看见这里这个就是key

这一块根据查询可以得知,这里就是iv所在的地方

然后在网上找工具直接解就行了。


The world's full of lonely people afraid to make the first move.