陕西省赛2023-部分Reverse复现

目录

我的upx -d怎么坏了

动调脱upx壳:

babypython

BadCoffee

ob混淆:


我的upx -d怎么坏了

打开附件,发现是upx壳,应该是修改了区段名或者特征码

陕西省赛2023-部分Reverse复现_第1张图片

 陕西省赛2023-部分Reverse复现_第2张图片

 但是修改过还是不对.....(哭.jpg)应该还是改了其他

好学长告诉我直接动态调试脱壳

动调脱upx壳:

在吾爱破解版OD里面打开,

陕西省赛2023-部分Reverse复现_第3张图片

 打开位置可以看到上面一点就有popad指令,在上面下断点

陕西省赛2023-部分Reverse复现_第4张图片

 F9程序运行到下一个断点

陕西省赛2023-部分Reverse复现_第5张图片

1 处是循环处理栈空间, 2 处有个大跳转,从0x41C131跳转到0x4012D0处,可能是程序的ope入口,F7跟进0x4012D0,在此处用插件dump程序出来

陕西省赛2023-部分Reverse复现_第6张图片

 陕西省赛2023-部分Reverse复现_第7张图片

 直接点脱壳保存,就可以得到脱壳后的程序

陕西省赛2023-部分Reverse复现_第8张图片

 没显示壳了,放入到ida中,迷宫题

int sub_40143C()
{
  char Str[100]; // [esp+1Ch] [ebp-74h] BYREF
  int len; // [esp+80h] [ebp-10h]
  int v3; // [esp+84h] [ebp-Ch]
  int v4; // [esp+88h] [ebp-8h]
  int i; // [esp+8Ch] [ebp-4h]

  sub_401B10();
  printf("Please input your flag: ");
  scanf("%s", Str);
  len = strlen(Str);
  v4 = 1;
  v3 = 1;
  for ( i = 0; i < len; ++i )
  {
    switch ( Str[i] )
    {
      case 'U':                                 // 上
        --v4;
        break;
      case 'D':                                 // 下
        ++v4;
        break;
      case 'L':                                 // 左
        --v3;
        break;
      case 'R':                                 // 右
        ++v3;
        break;
    }
    if ( sub_401410(v4, v3) )                   // 迷宫图
      break;
    if ( aS0000000000000[15 * v4 + v3] == 35 )  // 走合适路线是值最终为35
    {
      printf("You are right!\nflag is flag{md5(your put)}");
      return 0;
    }
  }
  puts(Buffer);
  return 0;
}

 推荐一位学长的博客,自动走迷宫脚本(膜拜大佬.jpg):(124条消息) C语言实现自动走迷宫 自动输出迷宫路径_OrientalGlass的博客-CSDN博客

下面是引用的博客代码:


#include 
#include 
#include 
#include 
// 枚举一些关键常量,可以根据迷宫的不同而修改
enum state
{
	start = 'S', end = '#', road = '0', wall = '*', visited = '1', successPath = '%', currentPosition = '@'
}State;
//路径操作符枚举
enum operate {
	up = 'U', right = 'R', down = 'D', left = 'L'
}Operate;
//保存路径
struct
{
	int len;
	unsigned char arr[1000];
}Path;
//输入路径
void inputPath(unsigned char op)
{
	Path.arr[Path.len] = op;
	Path.len++;
}
//输出路径
void printPath()
{
	printf("\nPath:");
	while (Path.len > 0)
	{
		Path.len--;
		putchar(Path.arr[Path.len]);
	}
	printf("\n");
}
//判断是否在迷宫范围内以及是否可以走这一步
bool isLegal(int x, int y, int row, int col, unsigned char* p)
{
	if (x >= 0 && y >= 0)
		if (x < row && y < col)
			return (p[x * col + y] == road || p[x * col + y] == end);
	return false;
}
//输入迷宫图
//支持以矩阵形式输入,也可以输入一整行,自动处理换行符,直到读取到整个迷宫图为止
void inputMaze(unsigned char* p, int row, int col)
{
	unsigned char ch;
	printf("请输入迷宫图:\n");
	for (int i = 0; i < row * col; i++)
	{
		if ((ch = getchar()) != '\n')
			p[i] = ch;
		else
			--i;
	}

}
//打印迷宫图
void printMaze(unsigned char* p, int row, int col) {
	printf("\n迷宫图如下:\n");
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
			printf("%c", p[i * col + j]);
		printf("\n");
	}

}
//走迷宫
//递归查询,这里由于递归是倒序输出路径,所以需要一个倒序操作
bool walkMaze(int row, int col, unsigned char* p, int x, int y)
{
	int pos = x * col + y;	//当前位置
	if (p[pos] == end)		//到达终点
		return true;
	if (isLegal(x - 1, y, row, col, p))	//上
	{
		//printMaze(p,row,col); //如果需要可以逐步输出迷宫图
		p[pos] = visited;	//设置访问标识,防止无限递归
		if (walkMaze(row, col, p, x - 1, y))	//该路径可行,输出操作符
		{
			inputPath(up);
			p[pos] = successPath;			//用于显示该路径
			return true;
		}
	}
	if (isLegal(x, y + 1, row, col, p))	//右
	{
		//printMaze(p,row,col);
		p[pos] = visited;
		if (walkMaze(row, col, p, x, y + 1))
		{
			inputPath(right);
			p[pos] = successPath;
			return  true;
		}
	}
	if (isLegal(x + 1, y, row, col, p))	//下
	{
		//printMaze(p,row,col);
		p[pos] = visited;
		if (walkMaze(row, col, p, x + 1, y))
		{
			inputPath(down);
			p[x * col + y] = successPath;
			return true;
		}
	}
	if (isLegal(x, y - 1, row, col, p))	//左
	{
		//printMaze(p,row,col);
		p[pos] = visited;
		if (walkMaze(row, col, p, x, y - 1))
		{
			inputPath(left);
			p[pos] = successPath;
			return  true;
		}
	}
	p[pos] = visited;
	return false;	//无路可走,该条路径不行
}

//自动寻找起点,可以自行选择是否调用
void findStart(unsigned char* p, int row, int col, int* x, int* y)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (p[i * col + j] == start)
			{
				*x = i;
				*y = j;
				return;
			}
		}
	}
}

int main()
{
	int row = 15, col = 15, x = 1, y = 1;	//行和列,起点坐标
	unsigned char* Maze = (unsigned char*)malloc(row * col);	//分配空间
	inputMaze(Maze, row, col);		//输入迷宫
	printMaze(Maze, row, col);		//打印迷宫
	walkMaze(row, col, Maze, x, y);		//走迷宫
	Maze[x * col + y] = start;		//矫正起点字符
	printMaze(Maze, row, col);		//打印迷宫
	printPath();					//打印路径
	free(Maze);						//释放空间
	system("pause");
	return 0;
}

或者手动走迷宫,得到最后的结果是 RRRDRRURRRRRRDDDDRDDD

32位小写md5加密即为flag

babypython

下载附件,是python字节码,有很多混淆的无用代码,尝试问chatgpt还原源码

下面是程序有效的代码思路

定义flag,并将flag的每个字符异或8

import timeit
flag = '************************************'
value = ''
output = ''
i = 0

while i < len(flag):
    temp = flag[i] ^ 8
    value += chr(ord(temp) ^ 8)
    i += 1

 将flag每个字符加3

for i in range(len(flag)):
    temp = flag[i]
    temp = chr(ord(temp) + 3)
    output += temp

将字符串进行base64加密,切片,即将加密后的base64密文反转倒序

import base64

output = base64.encode(output.encode())
obfuscated_output = output.decode()

obfuscated_output = obfuscated_output[:-1]

 将字符串中的g替换成1,H替换成3,W替换成9



obfuscated_output = obfuscated_output.replace('g', '1')
obfuscated_output = obfuscated_output.replace('H', '3')
obfuscated_output = obfuscated_output.replace('W', '9')
invalid_variable = len(obfuscated_output) * 5

# 第二个部分
print(obfuscated_output)

将txt文本末尾的加密变换后的字符串逆序,1替换为g,3替换为H,9替换为W,进行base64解密

得到解密后的字符串qglrv@onmlqpA>qmq>mBo3A?Bn

写脚本解密,拿到flag:flag{5dcbafe63fbf3b7d8647c1aee650ae9c}

#include
#include
#include
int main(){
	int i;
	char str[]="qglrv@onmlqpA>qmq>mBo3A?Bn

BadCoffee

下载附件,是js 文件,很多代码,好学长指导,知道是ob混淆

ob混淆:

开头定义了一个大数组,然后对这个大数组里的内容进行位移,再定义一个解密函数。后面大部分的值都调用了这个解密函数,以达到混淆的效果。

这种代码即为ob混淆,不仅变量名混淆了,运行逻辑等也高度混淆,难以理解

  1. 对象属性名称替换:混淆工具会将代码中的对象属性名称替换为无意义的名称。例如,将属性名从可读性高的名称(如 "name")替换为随机生成的字符串(如 "_0x1a2b3c")。这使得代码难以理解和分析。

  2. 属性访问转换:混淆工具可能会将直接的属性访问(例如 obj.property)转换为通过字符串进行间接访问的形式(例如 obj["property"])。这增加了代码的复杂性,使属性访问变得不直观。

  3. 属性重排序:混淆工具可能会对对象的属性进行重新排序,使属性的顺序变得混乱和不可预测。这增加了代码的困惑性,使属性的逻辑关系更加难以理解。

  4. 属性值加密:混淆工具可能会对对象的属性值进行加密或编码,以防止直接获取属性值。这可以使用各种加密算法或编码方案来实现。

  5. 对象引用混淆:混淆工具可能会修改对象的引用方式,使对象的引用变得复杂和不明显。例如,将对象的引用存储在数组、函数调用或其他对象中,增加代码的混乱性。

下面是一个示例代码:

var _0x1a2b = ['\x48\x65\x6C\x6C\x6F', '\x57\x6F\x72\x6C\x64', '\x63\x6F\x6E\x73\x6F\x6C\x65', '\x6C\x6F\x67'];//即大数组
//_0x1a2b 是一个包含随机字符串的数组,'\x48\x65\x6C\x6C\x6F' 对应替代属性名 'Hello'
var _0x3c4d = {};
_0x3c4d[_0x1a2b[0x0]] = 'Hello';//偏移
_0x3c4d[_0x1a2b[0x1]] = 'World';//:属性访问使用数组索引来间接访问属性,_0x3c4d[_0x1a2b[0x0]] 表示访问属性 'Hello
_0x3c4d[_0x1a2b[0x2]] = function (_0x24e2) {
    console[_0x1a2b[0x3]](_0x24e2);
};

_0x3c4d[_0x1a2b[0x2]](_0x3c4d[_0x1a2b[0x0]] + ' ' + _0x3c4d[_0x1a2b[0x1]]);//后面都调用解密函数

具体参考这篇文章:(124条消息) ob混淆解密在线工具_在线解ob混淆_滕青山ᅠ的博客-CSDN博客和这篇文章:(124条消息) 爬虫反混淆入门--JS混淆之ob混淆_阿J~的博客-CSDN博客

推荐一个在线工具解密ob混淆:解混淆测试版 (yuanrenxue.cn)   (这个在线工具使用火狐浏览器无法使用,可能是我的浏览器问题)

下面是优化后的代码:

function xxx(_0x53b7bb, _0x590286) {
  return _0x53b7bb ^ _0x590286;
}

function enc(_0x4bda4c) {
  var _0x8ff1dd = [],
      _0x6aca75 = [233, 129, 127, 238, 145, 144, 11, 43, 87, 134, 243, 158, 197, 216, 111, 136, 152, 29, 204, 31, 26, 228, 39, 148, 215, 220, 90, 76, 251, 57, 183, 184, 150, 157, 156, 176, 13, 41, 30, 86, 244, 8];

  for (let _0x7bc200 = 0; _0x7bc200 < 42; _0x7bc200++) {
    _0x8ff1dd[_0x7bc200] = xxx(_0x6aca75['at'](_0x7bc200), _0x4bda4c["charAt"](_0x7bc200)['charCodeAt']());
  }

  for (let _0x4f674a = 0; _0x4f674a < 42; _0x4f674a++) {
    _0x8ff1dd[_0x4f674a] = xxx(_0x8ff1dd['at'](_0x4f674a), _0x6aca75['at'](41 - _0x4f674a));
  }

  console["log"](_0x8ff1dd);
  return _0x8ff1dd;
}

function fff() {
  var _0xe4960c = "flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}",
      _0x55dae6 = enc(_0xe4960c),
      _0xbb5ecd = [135, 25, 72, 151, 195, 212, 228, 212, 250, 101, 39, 77, 163, 77, 70, 167, 119, 184, 7, 77, 144, 154, 93, 10, 185, 48, 179, 77, 71, 163, 67, 61, 113, 156, 196, 136, 239, 241, 128, 93, 84, 156];

  for (let _0x37df9d = 0; _0x37df9d < 42; _0x37df9d++) {
    if (_0x55dae6['at'](_0x37df9d) != _0xbb5ecd['at'](_0x37df9d)) {
      console["log"]("Error");
      return;
    }
  }

  console["log"]("YES");
  return;
}

fff();

也可以尝试自己修改变量名和函数名进一步优化 ,优化后稍微好看一点

function xor(a,b) {
  return a ^b;
}

function enc(flag) {
  var Str3 = [],
      Str2 = [233, 129, 127, 238, 145, 144, 11, 43, 87, 134, 243, 158, 197, 216, 111, 136, 152, 29, 204, 31, 26, 228, 39, 148, 215, 220, 90, 76, 251, 57, 183, 184, 150, 157, 156, 176, 13, 41, 30, 86, 244, 8];

  for (letj = 0;j < 42;j++) {
    Str3[_0x7bc200] = xor(Str2['at'](_0x7bc200), flag["charAt"](_0x7bc200)['charCodeAt']());
  }

  for (let k = 0; k < 42; k++) {
    Str3[k] = xor(Str3['at'](k), Str2['at'](41 - k));
  }

  console["log"](Str3);
  return Str3;
}

function fff() {
  varflag = "flag{xxxxxxxxxxxxxxxxxxxxxxxxxx}",
      result = enc(flag),
      Str = [135, 25, 72, 151, 195, 212, 228, 212, 250, 101, 39, 77, 163, 77, 70, 167, 119, 184, 7, 77, 144, 154, 93, 10, 185, 48, 179, 77, 71, 163, 67, 61, 113, 156, 196, 136, 239, 241, 128, 93, 84, 156];

  for (let i = 0; i < 42; i++) {
    if (result['at'](i) != Str['at'](i)) {
      console["log"]("Error");
      return;
    }
  }

  console["log"]("YES");
  return;
}

fff();

函数的大致思路是flag^str2^str2的反转字符串=str,写脚本解密:

#include
#include
#include
int main(){
	
	char data[]={135, 25, 72, 151, 195, 212, 228, 212, 250, 101, 39, 77, 163, 77, 70, 167, 119, 184, 7, 77, 144, 154, 93, 10, 185, 48, 179, 77, 71, 163, 67, 61, 113, 156, 196, 136, 239, 241, 128, 93, 84, 156};
  char Buffer[]={233, 129, 127, 238, 145, 144, 11, 43, 87, 134, 243, 158, 197, 216, 111, 136, 152, 29, 204, 31, 26, 228, 39, 148, 215, 220, 90, 76, 251, 57, 183, 184, 150, 157, 156, 176, 13, 41, 30, 86, 244, 8};
   char flag[100];
int i;
for(i=0;i<42;i++){
	flag[i]=data[i]^Buffer[i]^Buffer[41-i];
	printf("%c",flag[i]);
}
	
	return 0;
}

未完待续...

你可能感兴趣的:(网络安全,逆向工程)