0


2023年第三届陕西省大学生网络安全技能大赛--本科高校组 Reverse题解

文章目录

这次比赛做出来三道题,Web&Assembly找到了工具和相关文章,但是时间不太够了hhh,收获还是不少的

一. 我的upx -d怎么坏了

1. 查看节区信息

可以看到节区被修改了,本来应该是UPX被改成了HOEY,试着修改了一下还是不能upx -d,遂选择动调
在这里插入图片描述

2. 动态调试脱壳

这里推荐使用吾爱破解专用OD打开,可以很快找到popad这个关键码,x32dbg和ollydbg都不行(不知道是不是指令集的问题或者是什么插件的问题)
打开程序后向上翻大概四五行就可以看到popad,而且这个下面有个调整栈的循环,还有个jmp 0x004012D0的远跳转,所以很可能是oep
请添加图片描述

f7跟进4012D0后,使用插件里的OllyDump脱壳
请添加图片描述

脱壳后的文件打开分析,shift+f12查看字符串,可以看到第一行有一串可疑字符串,然后跟进,再对函数按x交叉引用往上面找就可以找到主函数,可以看到是迷宫,并且能看出迷宫是每行15个元素
在这里插入图片描述

3.输出迷宫图

#include<stdio.h>#include<string.h>intmain(){char maze[]="****************S000*0000000***0**000*****0***0*00*0*000*0***000**0*0*0*0***0***00*0*0*00**0***0**0*0**0**000*0*00*00*0****0*0******0#****0*00000000***000********0***0***00000*00***0*0*****0**0***0000000*00000****************";for(int i =0; i <strlen(maze); i++){if(i  %15==0)printf("\n");printf("%c", maze[i]);}return0;}

得到迷宫图:

***************
*S000*0000000**
*0**000*****0**
*0*00*0*000*0**
*000**0*0*0*0**
*0***00*0*0*00*
*0***0**0*0**0*
*000*0*00*00*0*
***0*0******0#*
***0*00000000**
*000********0**
*0***00000*00**
*0*0*****0**0**
*0000000*00000*
***************

4.走迷宫

我以前写过一个走迷宫脚本,稍微修改一下就可以解这题的路径了
C语言实现自动走迷宫 自动输出迷宫路径
修改后:

#include<stdio.h>#include<stdlib.h>#include<stdbool.h>#include<Windows.h>// 枚举一些关键常量,可以根据迷宫的不同而修改enumstate{
    start ='S', end ='#', road ='0', wall ='*', visited ='1', successPath ='%', currentPosition ='@'}State;//路径操作符枚举enumoperate{
    up ='U', right ='R', down ='D', left ='L'}Operate;//保存路径struct{int len;unsignedchar arr[1000];}Path;//输入路径voidinputPath(unsignedchar op){
    Path.arr[Path.len]= op;
    Path.len++;}//输出路径voidprintPath(){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,unsignedchar* p){if(x >=0&& y >=0)if(x < row && y < col)return(p[x * col + y]== road || p[x * col + y]== end);return false;}//输入迷宫图//支持以矩阵形式输入,也可以输入一整行,自动处理换行符,直到读取到整个迷宫图为止voidinputMaze(unsignedchar* p,int row,int col){unsignedchar ch;printf("请输入迷宫图:\n");for(int i =0; i < row * col; i++){if((ch =getchar())!='\n')
            p[i]= ch;else--i;}}//打印迷宫图voidprintMaze(unsignedchar* 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,unsignedchar* 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;//无路可走,该条路径不行}//自动寻找起点,可以自行选择是否调用voidfindStart(unsignedchar* 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;}}}}intmain(){int row =15, col =15, x =1, y =1;//行和列,起点坐标unsignedchar* Maze =(unsignedchar*)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");return0;}

运行结果:
在这里插入图片描述

得到路径:RRRDRRURRRRRRDDDDRDDD,32位小写md5后得到flag
flag{ae2de0be8285f69db701d4dba8721a40}

二. babypython

这题一开始想着能不能将txt里的字节码转成pyc文件的二进制字节码,询问gpt得到的脚本并不行,只能老老实实生啃字节码了(还好有gpt)
使用gpt辅助分析,但是要注意有一部分混淆代码没有实际作用,可以跳过,保留关键代码然后让gpt整合分析

1.字节码简单分析

可以搜索flag快速定位关键代码,关键代码上方有一些初始化操作
在这里插入图片描述
遍历flag,元素异或8保存到value数组
在这里插入图片描述
垃圾代码,后面还提示了"This line will never be executed"
在这里插入图片描述
遍历value数组,元素+3
在这里插入图片描述
后面还有一些垃圾代码,是无用的操作和一些跳转,像这里是跳转到1362
在这里插入图片描述
跳转到最后有base64编码处理(处理完之后字符串还要倒序)
在这里插入图片描述
替换字符处理
在这里插入图片描述
文件末尾可以看到输出的值
在这里插入图片描述

2. gpt分析

这里将字节码中有用的部分截取出来给gpt分析,然后让他给出python代码,再将各个代码段整合就可以得到程序逻辑的结果了
在这里插入图片描述

3. 程序逻辑

总之,主要逻辑如下:

  1. 遍历flag,每个元素和8异或后保存到value中
  2. 遍历value,每个元素+3后保存到output中
  3. 对output进行base64编码,然后进行逆序得到obfuscated_output
  4. 对obfuscated_output中部分字符进行替换 最后输出 =1nb0A3b7AUQwB3b84mQ/E0MvJUb+EXbx5TQwF3bt52bAZncsd9c
import base64

flag ='************************************'
value =''
output =''
i =0while i <len(flag):
    temp =ord(flag[i])^8
    value +=chr(temp)
    i +=1for j inrange(len(flag)):
    temp =ord(value[j])+3
    output +=chr(temp)
obfuscated_output = base64.b64encode(output.encode()).decode()[::-1]
obfuscated_output = obfuscated_output.replace('g','1').replace('H','3').replace('W','9')print(obfuscated_output)

4.解题脚本

import base64
Encoded='=1nb0A3b7AUQwB3b84mQ/E0MvJUb+EXbx5TQwF3bt52bAZncsd9c'
Encoded= Encoded.replace('1','g').replace('3','H').replace('9','W')
decoded = base64.b64decode(Encoded[::-1])for i in decoded:print(chr((i-3)^8),end='')

flag{5dcbafe63fbf3b7d8647c1aee650ae9c}

三. BadCoffee

1. 相关文章

这题最开始也看不出是怎么做,然后就到网上查了下js逆向的混淆方法
找到几篇文章:

  1. 【JavaScript 逆向】AST 技术反混淆
  2. 使用javascript-obfuscator对js文件进行混淆 不过文章只是简单介绍了一下,并没有给出反混淆的解决方法

2.解混淆

后来找到了工具:

  1. JS逆向 | ob混淆一键还原工具这个是本地工具,下载了但是还没使用
  2. 解混淆测试版这个在线工具非常好用 只要将代码复制进去,然后选择模式1,选项全部勾选即可在这里插入图片描述
  3. 解混淆代码:
functionxxx(_0x53b7bb, _0x590286){return _0x53b7bb ^ _0x590286;}functionenc(_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;}functionfff(){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();

3.解题脚本

手动修改变量名和函数美化后的代码:

functionxor(a, b){return a ^ b;}functionenc(data){var result =[],
        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];for(let j =0; j <42; j++){
      result[j]=xor(buffer['at'](j), data["charAt"](j)['charCodeAt']());}for(let k =0; k <42; k++){
      result[k]=xor(result['at'](k), buffer['at'](41- k));}
    console["log"](result);return result;}functionmain(){var flag ="flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}",
        encodeFlag =enc(flag),
        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];for(let i =0; i <42; i++){if(encodeFlag['at'](i)!= data['at'](i)){
        console["log"]("Error");return;}}
    console["log"]("YES");return;}main();

很容易看出来是简单的异或和比较操作,解题脚本也很简单:

Encoded =[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]
array =[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 i inrange(42):print(chr(Encoded[i]^array[41-i]^array[i]),end='')

flag{I_c0uld_neu3r_undeRstand_jvaVs3rIpt!}

四. Web&Assembly

1.本地环境查看网页

在index.html所在文件夹内使用命令python -m http.server -b localhost打开本地环境
请添加图片描述
浏览器输入localhost:8000即可查看网页内容,给了一点提示
请添加图片描述

可以用jeb或者ghidra直接分析wasm文件,也可以用wabt工具反编译之后再分析

2. jeb&ghidra分析wasm文件

jeb要注意路径名(因为文件夹名为Web&Assembly有&这个符号,jeb打开文件失败)

main函数

请添加图片描述

jeb分析的f8函数比较难看,用ghidra分析,首先下载插件ghidra-wasm-plugin

下载和ghidra版本对应的zip文件后在ghidra中打开file>install extensions 点击+号选择zip文件

官方文档安装指导如下:
The easiest way to install the plugin is as a Ghidra extension. Grab a release that is compatible with your version of Ghidra - for example, if you’re using Ghidra 10.0.4, download the file beginning with ghidra_10.0.4_PUBLIC. You don’t need to unzip the file: simply launch Ghidra, go to “File -> Install Extensions”, select the + icon, and select the zip file. Restart Ghidra to load the extension and you should be good to go. Note: if you upgrade your version of Ghidra, you will need to upgrade your plugin too.

check函数(即f8函数)

uint check(char*str,char*input,int hash){
  uint uVar1;size_t strLen;int ii;int k;int j;
  undefined8 data;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  uint i;
  uint judge1;
  undefined8 table;
  undefined8 local_28;char local_20;int Hash;char*Input;char*Str;
  uint flagVar;
  bool judge2;/* 0123456789abcdef */
  local_20 = s_0123456789abcdef_ram_00010000[16];
  local_28 = s_0123456789abcdef_ram_00010000._8_8_;
  table = s_0123456789abcdef_ram_00010000._0_8_;
  Hash = hash;
  Input = input;
  Str = str;
  strLen =strlen(str);if(strLen <8){/* 退出 */
    flagVar =0;}else{/* 每次处理8个字符 */
    judge1 =0;for(i =0; uVar1 = i, strLen =strlen(Input), uVar1 < strLen; i = i +8){
      local_48 =0;
      local_50 =0;
      local_58 =0;
      data =0;for(j =0; j <8; j = j +1){*(uint *)((int)&data + j *4)=(int)(char)(Str[j]^ Input[j + i])&0xff;/* 异或 */}for(k =0; k <114; k = k +1){/* xor */XorPlus(&data,0,1,2,3);XorPlus(&data,4,5,6,7);XorPlus(&data,0,1,4,5);XorPlus(&data,2,3,6,7);}for(ii =0; ii <8; ii = ii +1){
        judge2 = true;/* 转base16并比较 */if(judge1 ==0){
          judge2 =*(char*)((int)&table +*(int*)((int)&data + ii *4)/16)==*(char*)(Hash +(ii + i)*2);}
        judge1 =1;if(!judge2){
          judge1 =(uint)(*(char*)((int)&table +*(int*)((int)&data + ii *4)%0x10)==*(char*)(Hash +(ii + i)*2+1));}}}
    flagVar = judge1;}return flagVar;}

XorPlus

voidXorPlus(int pdata,int num1,int num2,int num3,int num4){
  uint *tmp;
  
  tmp =(uint *)(pdata + num4 *4);*tmp =*tmp ^*(int*)(pdata + num1 *4)+*(int*)(pdata + num3 *4);
  tmp =(uint *)(pdata + num4 *4);*tmp =*tmp &0xff;
  tmp =(uint *)(pdata + num3 *4);*tmp =*tmp ^*(int*)(pdata + num1 *4)+*(int*)(pdata + num2 *4);
  tmp =(uint *)(pdata + num3 *4);*tmp =*tmp &0xff;
  tmp =(uint *)(pdata + num2 *4);*tmp =*tmp ^*(int*)(pdata + num3 *4)+*(int*)(pdata + num4 *4);
  tmp =(uint *)(pdata + num2 *4);*tmp =*tmp &0xff;
  tmp =(uint *)(pdata + num1 *4);*tmp =*tmp ^*(int*)(pdata + num2 *4)+*(int*)(pdata + num4 *4);
  tmp =(uint *)(pdata + num1 *4);*tmp =*tmp &0xff;return;}

解密脚本(参照这位师傅的https://jonathanbest7.github.io/)

defXorPlus(enc, a, b, c, d):
    enc[a]^=(enc[b]+ enc[d])&0xFF# &0xff的作用是保证在字节型范围内
    enc[b]^=(enc[c]+ enc[d])&0xFF
    enc[c]^=(enc[a]+ enc[b])&0xFF
    enc[d]^=(enc[a]+ enc[c])&0xFFreturn enc

defdecryption(enc):for i inrange(0x72):
        enc = XorPlus(enc,2,3,6,7)
        enc = XorPlus(enc,0,1,4,5)
        enc = XorPlus(enc,4,5,6,7)
        enc = XorPlus(enc,0,1,2,3)
    key =b'114!514!'for i inrange(len(enc)):
        enc[i]= enc[i]^ key[i]return enc
            

hex_string ="91fba5ccfef6e0905eeeb47940d25543c286b10de778fbb268ab7580414c0758"
enc =bytes.fromhex(hex_string)

flag =b''for i inrange(0,len(enc),8):
    flag += decryption(bytearray(enc[i:i+8]))#每次处理8个字节print(flag)

3. Wabt工具反编译(复现失败)

参照wasm逆向——(极客大挑战2021wasm

sudo apt install wabt 即可安装wabt

wasm2c index.wasm -o index.c 即可反编译得到.c和.h文件

将反编译得到的文件和wabt项目内的wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h三个文件放到同一个文件夹内

wabt项目可以直接在github上下载https://github.com/WebAssembly/wabt,解压后在wabt-main/wasm2c文件夹中可以找到文件

使用指令gcc -c index.c -o index.o 编译但不链接生成.o文件

生成得到.o文件后可以用ida分析(官方wp也是这么做的),笔者这里不知道为什么生成失败就不多赘述

请添加图片描述

五.参考文章

  1. 陕西省赛逆向ak
  2. 第三届陕西省赛 REVERSE
  3. [WASM逆向分析]
  4. 一步步学习Webassembly逆向分析方法
  5. WebAssembly/wabt
  6. 一种Wasm逆向静态分析方法

六.总结

  1. 这次比赛总体感觉很好,比前几天黑盾杯和国赛坐牢要好太多,虽然一些题目没见过,但是在网上查一下就能找到相关文章和工具
  2. 还要重视理论知识水平,不能做脚本小子,这些题目虽然有现成的工具做,但还是要了解一下相关原理,不然下次变一下就呆住了.例如这个的babypython,以前都是用工具或者脚本直接干pyc文件,这次只能生啃字节码(还好有gpt强大的分析能力),甚至还有很多混淆和跳转代码,也是锻炼到了啃字节码的能力
  3. 另一方面,个人的经验还是不足,还要继续多做题,多比赛,多复现,勤能补拙

本文转载自: https://blog.csdn.net/OrientalGlass/article/details/131025651
版权归原作者 OrientalGlass 所有, 如有侵权,请联系我们删除。

“2023年第三届陕西省大学生网络安全技能大赛--本科高校组 Reverse题解”的评论:

还没有评论