BUGKU逆向reverse 1-8题
阅读原文时间:2023年08月22日阅读:1

练习IDA两年半

打开尘封已久的bugku,从题目中练习使用,现在都已经是新版本了 orz

入门逆向

运行baby.exe

将解压后的baby.exe拖到IDA里面

主函数中找到mov指令 可以看到这里就是flag

flag{Re_1s_S0_C0OL}

signin

下载附件

解压之后是sign_in.apk ,Android逆向我不会啊orz
丢到AndroidKiller 里面去
可以看到这里有一个checkpassword方法

不过我不知道怎么把这个直接跳过 估计是需要登录的时候直接绕过这个方法然后重打包
看到评论里面有的人用的是GDK https://github.com/charles2gan/GDA-android-reversing-Tool

GDA,一款用C++实现的强大的Dalvik字节码反编译器,具有分析速度快、内存磁盘消耗低等优点,对apk、dex、odex、oat、jar、class、aar文件的反编译能力更强。

GDK打开之后 牛逼啊

登录验证逻辑如下

private String getFlag(){
   return this.getBaseContext().getString(0x7f0b0020);
}
private void showMsgToast(String p0){
   Toast.makeText(this, p0, 1).show();
}
public void checkPassword(String p0){
   if (p0.equals(new String(Base64.decode(this.getFlag().reverse(), 0)))) {
      this.showMsgToast("Congratulations !");
   }else {
      this.showMsgToast("Try again.");
   }
   return;
}

接下来去找这个0x7f0b0020对应的值是多少
转换成十进制为

去找2131427360 这玩意,不过我直接搜索这个值找不到
手动查toString 看到在这里

所以这个值对应的是toString
GDK用起来卡卡的
toString 的值为 991YiZWOz81ZhFjZfJXdwk3X1k2XzIXZIt3ZhxmZ

按照规则 把字符串逆序 ZmxhZ3tIZXIzX2k1X3kwdXJfZjFhZ18zOWZiY199
然后base64解密,得到flag
flag{Her3_i5_y0ur_f1ag_39fbc_}

Easy_Re


额要是我知道我还运行干啥
我理解这里应该是会有一个比较操作 逆向的时候在比较操作的时候看flag值
IDA打开之后F5查看伪代码

输入的内容存储到v7变量中 然后跟v5进行对比,对比结果存储到v3中

scanf("%s", v7);
v3 = strcmp(v5.m128i_i8, v7);

所以真实的flag就在v5中,赋值操作在

v5 = _mm_loadu_si128((const __m128i *)&xmmword_413E34);

真实值在 xmmword_413E34,点击跟进

按 a 转译

获取flag

游戏过关

运行exe,看起来这个游戏好难

n是灯的序列号,m是灯的状态
如果第N个灯的m为1,则它点亮,否则它熄灭
起初所有的灯都关上了
现在你可以输入n来改变它的状态
但你应该注意一件事,如果你改变第N个灯的状态,第(N-1)个和第(N+1)个的状态也会改变
当所有灯亮起时,flag将出现
现在,输入n

乱输一通

获得flag zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}
丢到IDA里面检查一下,这次的文件比较多 搜索flag字符串 alt+t

看到输出flag的界面

汇编代码如下

F5转换为C代码,去除掉一些干扰代码

int sub_45E940()
{
  int i; // [esp+D0h] [ebp-94h]
  char v2[22]; // [esp+DCh] [ebp-88h] BYREF
  char v3[32]; // [esp+F2h] [ebp-72h] BYREF
  char v4[4]; // [esp+112h] [ebp-52h] BYREF
  char v5[64]; // [esp+120h] [ebp-44h]

  sub_45A7BE("done!!! the flag is ");
  v5[0] = 18;
  v5[1] = 64;
  v5[2] = 98;
  v5[3] = 5;
  v5[4] = 2;
  v5[5] = 4;
  v5[6] = 6;
  v5[7] = 3;
  v5[8] = 6;
  v5[9] = 48;
  v5[10] = 49;
  v5[11] = 65;
  v5[12] = 32;
  v5[13] = 12;
  v5[14] = 48;
  v5[15] = 65;
  v5[16] = 31;
  v5[17] = 78;
  v5[18] = 62;
  v5[19] = 32;
  v5[20] = 49;
  v5[21] = 32;
  v5[22] = 1;
  v5[23] = 57;
  v5[24] = 96;
  v5[25] = 3;
  v5[26] = 21;
  v5[27] = 9;
  v5[28] = 4;
  v5[29] = 62;
  v5[30] = 3;
  v5[31] = 5;
  v5[32] = 4;
  v5[33] = 1;
  v5[34] = 2;
  v5[35] = 3;
  v5[36] = 44;
  v5[37] = 65;
  v5[38] = 78;
  v5[39] = 32;
  v5[40] = 16;
  v5[41] = 97;
  v5[42] = 54;
  v5[43] = 16;
  v5[44] = 44;
  v5[45] = 52;
  v5[46] = 32;
  v5[47] = 64;
  v5[48] = 89;
  v5[49] = 45;
  v5[50] = 32;
  v5[51] = 65;
  v5[52] = 15;
  v5[53] = 34;
  v5[54] = 18;
  v5[55] = 16;
  v5[56] = 0;

  qmemcpy(v2, "{ ", 2);
  v2[2] = 18;
  v2[3] = 98;
  v2[4] = 119;
  v2[5] = 108;
  v2[6] = 65;
  v2[7] = 41;
  v2[8] = 124;
  v2[9] = 80;
  v2[10] = 125;
  v2[11] = 38;
  v2[12] = 124;
  v2[13] = 111;
  v2[14] = 74;
  v2[15] = 49;
  v2[16] = 83;
  v2[17] = 108;
  v2[18] = 94;
  v2[19] = 108;
  v2[20] = 84;
  v2[21] = 6;

  for ( i = 0; i < 56; ++i )
  {
    v2[i] ^= v5[i];
    v2[i] ^= 0x13u;
  }
  return sub_45A7BE("%s\n");
}

这里有个问题啊,就是v2和v5的长度不一致

v2的长度就是22,这样的话最后得到的结果是

少了一部分的flag,但是v3和v4的长度加起来是 32+2=34,再加上前面的22就是56,所以我们应该把v2 v3 v4组在一起 为啥这里看不出来 我感觉这里的C语言代码是有点问题
更新python脚本

v2=[123,32,18,98,119,108,65,41,124,80,125,38,124,111,74,49,83,108,94,108,84,6,96,83,44,121,104,110,32,95,117,101,99,123,127,119,96,48,107,71,92,29,81,107,90,85,64,12,43,76,86,13,114,1,117,126,0]
v5=[18,64,98,5,2,4,6,3,6,48,49,65,32,12,48,65,31,78,62,32,49,32,1,57,96,3,21,9,4,62,3,5,4,1,2,3,44,65,78,32,16,97,54,16,44,52,32,64,89,45,32,65,15,34,18,16,0]
flag=""
for i in range(len(v2)):
    flag+=chr(v2[i]^v5[i]^0x13)

print(flag)

结果如下

换一个方式 使用动态调试 也就是掏出OllyDbg
使用中文搜索引擎

选择智能搜索 然后就可以看到

双击进入

这部分的入栈位置在这里

上转至 006A7AB4

上转调用位置为 006AF66C

这里的几个汇编指令我觉得需要解释一下
可以看到每一个jnz前面都有cmp,也就是比较失败就跳转到 006AF671

如果所有的比较都成功则调用call 方法,调用006A7AB4地址的方法
这里的逻辑很显然就是这里判断灯亮灯灭的判断(IDA中看见

所以我们将这里的jmp地址修改为call地址 不管输入 1-8 的哪个数字都能拿到flag了


右键复制到可执行文件 选择

保存为 ConsoleApplication7.exe,然后运行一下,输入 1,获取flag

Easy_vb

运行之后随便点点 好像没啥用

OD打开智能搜索

树林的小秘密

下载之后我的OD载入不了64位的程序 只能IDA了
shift + F12 搜索flag

看起来是pyinstaller打包的

用对应的工具去恢复源码,工具在这,点进去就是下载
https://nchc.dl.sourceforge.net/project/pyinstallerextractor/dist/pyinstxtractor.py

把工具和待反编译的exe放到一个目录下,然后运行

python pyinstxtractor.py easy_reverse.exe


生成了 easy_reverse.exe_extracted 文件,可以看到反编译的结果为

这里这个没有后缀的就是之前打包的python文件对应的pyc文件
这个时候打开123已经可以拿到flag了,base64解密

Timer(阿里CTF)

又是一个APK
GDA打开 搜索flag

跳转查看,如下

JNI编程?这么上流

这个位置load 了 so文件

所以其实flag在这个里面

但是so文件逆向我更不会了,只能看代码逻辑
初始化onCreate方法

protected void onCreate(Bundle savedInstanceState){
   super.onCreate(savedInstanceState);
   this.setContentView(R.layout.activity_main);
   Handler handler = new Handler();
   Runnable runnable = new MainActivity$1(this, this.findViewById(0x7f0c0051), this.findViewById(0x7f0c0050), handler);
   handler.postDelayed(runnable, 0);
}

运行了这个方法

public void run(){
   MainActivity$1 tthis$0;
   this.this$0.t = System.currentTimeMillis();
   this.this$0.now = (int)(this.this$0.t / 1000);
   this.this$0.t = 1500 - (this.this$0.t % 1000);
   this.val$tv2.setText("AliCTF");
   if (((this.this$0.beg - this.this$0.now)) <= 0) {
      this.val$tv1.setText("The flag is:");
      this.val$tv2.setText("alictf{"+this.this$0.stringFromJNI2(this.this$0.k)+"}");
   }
   if (MainActivity.is2((this.this$0.beg - this.this$0.now))) {
      tthis$0 = this.this$0;
      tthis$0.k = tthis$0.k + 100;
   }else {
      tthis$0 = this.this$0;
      tthis$0.k = tthis$0.k - 1;
   }
   this.val$tv1.setText("Time Remaining\(s\):"+(this.this$0.beg - this.this$0.now));
   this.val$handler.postDelayed(this, this.this$0.t);
   return;
}

OK 虽然看起来有点费劲
现在有四个参数

  • t
  • now
  • beg
  • k

now是当前时间戳
beg是启动APK的时候的时间,在

当然加了20万秒 所以如果破解不了的话就等着吧,虽然CTF比赛很快就要结束力
t 是一个取余时间值
k是计算的时间种子,最后会传到JNI方法里面去,所以我们如果直接改if语句是没用的,因为时间种子没有修改正确

所以这里我们有两个地方需要修改,一个是计算出正确的时间种子,一个是进入if语句,先从简单的进入if语句开始

把beg改成0就OK
不过好像GDK没有这个修改重打包的功能,使用AndroidKiller

把if-gtz修改为 if-lez,从判断大于零改为判断小于或等于零
接下来需要计算正确的K,抽离其中的关键因素
使用GDA反编译之后的代码看起来费劲,换成jd-gui
先使用dex2jar 把 classes.dex转换成 jd-gui可识别的jar包

然后使用jd-gui打开jar包,现在看起来就好多了

编写python脚本如下

now=0
beg=0+200000
k=0
def is2(n):
    if n<=3:
        if n > 1:
            return True
        return False
    elif n%2==0 or n%3==0:
        return False
    else:
        i=5
        while i*i <= n:
            if 0 == n%i or n%(i+2) == 0:
                return False
            i=i+6
        return True

while beg - now > 0:
    if is2(beg-now):
        k+=100
    else:
        k-=1
    beg -= 1

print(k)

运行得到K值

k值为 1616384
修改代码 在这里直接把K强行赋值

回编译得到修改后的APK

连一下自己手机

adb install之后运行 就可以看到flag了

拿到flag

逆向入门

运行admin.exe???16位的应用程序

IDA打开感觉奇奇怪怪的,介素?

看起来类似010editer里面的格式,丢到wxmedit里面
一眼照片,这是逆向?服了

字符串丢到浏览器上 浏览器自动解码

扫码获取flag

OK现在逆向的前八题就做完了 再接再厉

END

建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注

加我拉你入群

黑糖安全公众号