REVERSE-PRACTICE-BUUCTF-17

[网鼎杯 2020 青龙组]jocker

exe程序,运行后提示输入flag,无壳,用ida分析
main函数平衡栈后,F5反汇编
主要逻辑为
读取输入,检验输入长度是否为24,对输入进行变换,与已知数组比较,执行一段从encrypt函数起始地址开始的SMC,分析可知,finally函数也会完全被SMC,未经变换的输入作为参数,调用encrypt函数和finally函数
jocker-main
先写input经过wrong变换和omg验证的逆运算脚本,得到的是假flag
jocker-fakeflag
往下走,在ida中使用idapython脚本完成SMC自修改代码

from idaapi import *
from idautils import *
start_addr = 0x401500
key = 0x41
for i in range(start_addr,start_addr+187):
    PatchByte(i,Byte(i)^key)

SMC前,encrypt函数和finally函数各自包含了一大段数据
jocker-presmc
SMC后,encrypt函数和finally函数的指令间都有一些db指令,实际上为花指令
encrypt函数:
jocker-pastsmc
finally函数:
jocker-pastsmc
手动nop掉多余的db指令,让ida能够自动分析成代码
完成nop后,发现encrypt函数,在两端黑色代码中间插有一段红色代码
jocker-nop
这时的处理方法是,在encrypt函数起始地址0x401500处,右键->undefine,变成黄色的数据,再按c转成代码,这时,encrypt函数起始地址与finally函数起始地址之间的代码全部为红色,再在encrypt函数起始地址0x401500处,右键->create function,平衡栈后,F5反汇编
这里encrypt函数反汇编后,好像没有用到未经变换的input,可能是patch代码的时候出错了,不过可以猜测为input和字符串“hahahaha_do_you_find_me?”异或,再与已知的unk_403040数组比较
jocker-encrypt
写encrypt函数的逆运算脚本,得到部分flag,之所以是部分flag,是因为长度为24的unk_403040的最后5个元素为0,“d_me?”与0异或不变,或者v6=19说明了只异或了19次,而且提交失败
jocker-partflag
同encrypt函数的解析步骤,对finally函数,先nop掉多余的db指令,undefine掉黑色代码,转成数据,按c再转成代码,右键->create function,F5反汇编
没看懂这个函数,看了其他师傅的wp,说这里也是异或,能看到的5个值[37,116,112,38,58]是异或后要比较的值,由于input的最后一位一定是“}”,它异或一个值(“71”)后与58相等,而且它之前的四位也是和这个值(“71”)异或,结果是58之前的四个值
jocker-finally
写finally函数的逆运算脚本得到后半部分的flag,替换掉“flag{d07abccf8a410cd_me?”的后5位,flag即为“flag{d07abccf8a410cb37a}”
jocker-partflag

[2019红帽杯]childRE

exe程序,运行后直接输入,无壳,ida分析
交叉引用字符串“flag{MD5(your input)}”来到sub_140001610函数
sub_140001610函数主要的逻辑为
读取输入,验证输入的长度是否为31,对输入进行位置变换处理,位置变换的结果放到v2(name),经调试可知,输入为abcdefghijklmnopqrstuvwxyz12345时,v2(name)的内容为pqhrsidtujvwkebxylz1mf23n45ogca
对v2(name)调用UnDecorateSymbolName函数处理,结果放到outputstring,验证outputstring的长度62及其内容

signed __int64 sub_7FF6DC281610()
{
  signed __int64 input_len; // rax
  _QWORD *v1; // rax
  const CHAR *v2; // r11
  __int64 v3; // r10
  __int64 v4; // r9
  const CHAR *v5; // r10
  signed __int64 outputstring_len; // rcx
  __int64 v7; // rax
  signed __int64 result; // rax
  unsigned int v9; // ecx
  __int64 v10; // r9
  int v11; // er10
  __int64 v12; // r8
  __int128 input; // [rsp+20h] [rbp-38h]
  __int128 v14; // [rsp+30h] [rbp-28h]

  input = 0i64;
  v14 = 0i64;
  sub_7FF6DC281080((__int64)"%s", &input);      // 读取输入
  input_len = -1i64;
  do
    ++input_len;
  while ( *((_BYTE *)&input + input_len) );
  if ( input_len != 31 )                        // 输入的长度为31
  {
    while ( 1 )
      Sleep(0x3E8u);
  }
  v1 = sub_7FF6DC281280(&input);                // 对input的处理,通过调试可知,从31行到41行,是对input的位置变换,变换后的结果放入v2,也就是name
                                                // 例如,输入abcdefghijklmnopqrstuvwxyz12345,name=“pqhrsidtujvwkebxylz1mf23n45ogca”
  v2 = name;                                    // v2==name
  if ( v1 )
  {
    sub_7FF6DC2815C0((unsigned __int8 *)v1[1]);
    sub_7FF6DC2815C0(*(unsigned __int8 **)(v3 + 16));
    v4 = dword_7FF6DC2857E0;
    v2[v4] = *v5;
    dword_7FF6DC2857E0 = v4 + 1;
  }
  UnDecorateSymbolName(v2, outputString, 0x100u, 0);// v2,也就是name,经过UnDecorateSymbolName函数处理,结果放到outputstring中
  outputstring_len = -1i64;
  do
    ++outputstring_len;
  while ( outputString[outputstring_len] );
  if ( outputstring_len == 62 )                 // outputstring的长度为62
  {
    v9 = 0;
    v10 = 0i64;
    do                                          // do循环体在验证outputstring的内容
    {
      v11 = outputString[v10];
      v12 = v11 % 23;
      if ( table[v12] != *(_BYTE *)(v10 + 0x7FF6DC283478i64) )// (_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&
        _exit(v9);
      if ( table[v11 / 23] != *(_BYTE *)(v10 + 0x7FF6DC283438i64) )// 55565653255552225565565555243466334653663544426565555525555222
        _exit(v9 * v9);
      ++v9;
      ++v10;
    }
    while ( v9 < 0x3E );
    sub_7FF6DC281020("flag{MD5(your input)}\n", v11 / 23, v12, v10);
    result = 0i64;
  }
  else
  {
    v7 = sub_7FF6DC2818A0(std::cout);
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v7, sub_7FF6DC281A60);
    result = 0xFFFFFFFFi64;
  }
  return result;
}

先写验证outputstring的长度62及其内容的逆运算脚本,得到outputstring,为一个未修饰的C++符号名
childre-outputstring
UnDecorateSymbolName函数反修饰指定已修饰的 C++ 符号名,参考:UnDecorateSymbolName
第1个参数为已修饰的 C++ 符号名,此名称能以始终为问号 (?) 的首字符鉴别,本题中为v2(name),第2个参数指向字符串缓冲区的指针,该缓冲区接收未修饰的名字,本题中为outputstring
可知需要对outputstring符号名修饰才能得到v2(name),参考:c/c++函数名修饰规则
或者通过写C++代码,调用__FUNCDNAME__宏(FUNCDNAME:只有在函数内部才有效,返回该函数经编译器修饰后的名字。如果编译器选项中设定了/EP或/P,则__FUNCDNAME__是未定义。)直接得到函数经编译器修饰后的符号名,需要注意的是函数所在的类,域,返回类型,函数名,参数类型都必须和已知完全相同

#include<iostream>
using namespace std;
class R0Pxx {//函数所在的类
public:
	R0Pxx() {//该类的构造函数
		unsigned char a;
		My_Aut0_PWN(&a);
	}

private://函数属于私有域
	//函数的返回类型 函数名 以及参数类型
	char * My_Aut0_PWN(unsigned char*) {
		char * ret = NULL;
		//调用宏 输出该函数经编译器修饰后的符号名
		printf("%s", __FUNCDNAME__);
		return ret;
	}
};
int main() {
	new R0Pxx();
	getchar();
	return 0;
}

运行结果即为v2(name),注意是在x86架构下运行的结果
childre-name
写逆位置变换脚本即可得到输入,对输入进行md5散列即可得到flag
childre-script

[MRCTF2020]PixelShooter

apk文件,jadx-gui打开,什么都没发现
用Apktool Box反编译apk后,在PixelShooter->lib->armeabi-v7a目录下发现3个.so文件,依次分析,同样什么都没发现
突然想到这是个安卓unity游戏,而安卓unity游戏的核心逻辑一般位于assets\bin\Data\Managed\Assembly-CSharp.dll
用dnSpy打开,在类UIController的GameOver方法中找到flag
pixelshooter-flag

[ACTF新生赛2020]SoulLike

elf文件,无壳,ida分析
main函数,读取输入,验证输入的前5个字符是否为“actf{”,将输入{}内的字符放入v8,可知输入{}内的长度为12,调用sub_83A函数验证v8,且输入的最后一个字符为“}”
soullike
分析sub_83A函数,可以看到是对input{}内12个字符的超多异或运算
solulike-sub_83A
在sub_83A函数的最后,是input{}内的12个字符经过超多异或运算后的值与已知的v4到v15比较,因为会有类似input[3]^=input[2]的情况,所以不能通过调试得到每个字符最后等效的异或值是什么
不过当比较为不相同时,程序会打印输入是哪个位置的字符错了,于是可以写脚本爆破出flag
sloulike-sub_83A
爆破脚本

#coding:utf-8
from itertools import *
import subprocess

flag=""
t=""
for i in range(12):
    for j in range(32,126):
        flag ="actf{"+t+chr(j)+"}" 
        p = subprocess.Popen(["./SoulLike"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        p.stdin.write(flag)
        p.stdin.close()
        out=p.stdout.read()
        p.stdout.close()

        if "#"+str(i) not in out:
            t+=chr(j)
            break

print(flag)

运行结果
soullike-flag

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

P1umH0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值