当前位置: 首页 > article >正文

NewStar easygui re wp

NewStar easygui re wp

参考题解:NewStar CTF week4-CSDN博客

提示先去看消息机制:深入理解windows 消息机制_⒉消息队列发送消息,消息的标识可以从键盘获取,-CSDN博客大概看下

64位无壳

shift+f12没找到关键字符串,看看函数有个winmain点进去,都是些系统函数

image-20241101093330470

发现sub_140001490不是点进去发现主要逻辑。

gui要注意MessageBox函数,一般这些函数就是解题的关键,比如点击verify就弹出了一个MessageBox

直接靠messagebox x交叉引用定位也行:

image-20241101093551011

sub函数内部:

image-20241101093624718

阅读代码

image-20241101093717639

发现有个反调试器,一般碰见这种,我们需要直接nop或者jz改为jnz…这里我直接nop了

下面的逻辑就是

        DlgItem = GetDlgItem(hWndParent, 101);
        WindowTextW = GetWindowTextW(DlgItem, String, 100);
        if ( WindowTextW <= 44 )
        {
          sub_140001000(String, (unsigned int)WindowTextW, v93);
          v12 = 0;
          if ( v93[1] == -57 )
            v12 = v93[0] == -33;
          v13 = 0;
          if ( v93[2] == 77 )
            v13 = v12;

可以理解,v93其实就是密文,这个代码那么长其实就是干校验密文

发现一个sub_140001000函数

WindowTextW<=44再对比下面的代码可以推测(也可以看sub_140001000内部推测),WindowTextW是密文长度44位(输入长度)

看sub_140001000内部

image-20241101094217858

这段代码做的是给Src赋值,a1也就是上面的string每两个字节赋值给Src

每个元素占用2个字节,因此 a1 + 2 * i 表示第 i 个元素的地址。

然后加载v29 常量

最后将 Src[j] 的值作为索引,从 v29 数组中读取一个字节的数据,并将其存储回 Src[j] 中。

也就是说,我们也可以反过来根据Src去找v29的值,最后可能解密用到

下面是第一次加密:

image-20241101095005184

紧接着两个dowhile

image-20241101094717433

第一个dowhile 我们发现我们所输入的字符串应该是44长度,根本用不了Src[v19+303],这段我们可以先不关注

第二个dowhile 是对v29进行变形

再看到下面:

image-20241101094912609

也就是加密过程是通过Src异或完成的

return (unsigned __int64)memcpy(a3, Src, v7);

我推测是要把Src赋值给a3也就是v93,后面动态调试也验证了

那么如何解出这道题?

我们可以用获取的密文(v93)去动态调试在程序运行到第一个dowhile循环的时候去替代Src

为什么这样做,因为第二次加密是个异或,我们再异或一次就得到第二次解密的完的密文,因为两个dowhile的操作都不影响Src(第一个Src操作影响不到,第二次是对v29的操作),我们可以直接到第一次加密处写解密代码。

下面下几个断点

image-20241101095546537

输入aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

这里有一个坑点就是这个输入框输入的最大长度是42,但我们要输入44位,可以修改寄存器的值

image-20241101095936296

image-20241101100001695

改成2C:

image-20241101100025560

运行到:

image-20241101100647502

替换Src:

这里我用脚本,地址根据你自己的地址来

# 导入IDA Python API
import ida_bytes

# 指定起始地址和字节数组
start_address = 0X000000E3CE76EB50
bytes_to_write = [
    0xdf, 0xc7, 0x4d, 0x14, 0xc1, 0xec, 0x08, 0xe4, 0x5f, 0x3f,
    0x03, 0xb4, 0x90, 0x4a, 0xb9, 0x8f, 0x8f, 0xfa, 0x71, 0x43,
    0xc7, 0xf1, 0x9d, 0xdd, 0x4f, 0xc0, 0x12, 0x44, 0x5c, 0x9d,
    0x88, 0x36, 0x2d, 0x16, 0x1d, 0xed, 0xbc, 0xef, 0xbb, 0x5b,
    0x9f, 0x77, 0xeb, 0x58
]

# 将字节逐个写入内存
for i, byte in enumerate(bytes_to_write):
    ida_bytes.patch_byte(start_address + i, byte)

print("Memory modification completed.")

image-20241101100715912

得到第二次解密后的:

image-20241101100809436

shift+e提出然后到第一次加密的地方写解密脚本


v29 = [
  0x31, 0x74, 0x54, 0x20, 0x03, 0x53, 0x78, 0x70, 0x3A, 0x35,
  0x65, 0x42, 0x04, 0x6B, 0x1F, 0x43, 0x06, 0x37, 0x00, 0x76,
  0x21, 0x08, 0x0B, 0x13, 0x52, 0x4B, 0x2F, 0x1A, 0x59, 0x2C,
  0x56, 0x51, 0x7F, 0x3B, 0x0E, 0x05, 0x26, 0x15, 0x25, 0x63,
  0x64, 0x7A, 0x3C, 0x29, 0x41, 0x2A, 0x12, 0x17, 0x2E, 0x39,
  0x57, 0x3D, 0x66, 0x33, 0x44, 0x6C, 0x6F, 0x47, 0x16, 0x71,
  0x5F, 0x1C, 0x14, 0x5A, 0x0C, 0x4F, 0x01, 0x30, 0x1B, 0x68,
  0x0F, 0x62, 0x3F, 0x18, 0x69, 0x6D, 0x7E, 0x5D, 0x6A, 0x28,
  0x22, 0x5B, 0x55, 0x72, 0x09, 0x5E, 0x02, 0x3E, 0x50, 0x7B,
  0x46, 0x45, 0x38, 0x10, 0x48, 0x79, 0x60, 0x36, 0x61, 0x6E,
  0x2D, 0x49, 0x7C, 0x2B, 0x34, 0x27, 0x11, 0x7D, 0x0D, 0x0A,
  0x77, 0x73, 0x58, 0x5C, 0x4C, 0x32, 0x4D, 0x1E, 0x24, 0x40,
  0x67, 0x4A, 0x4E, 0x1D, 0x07, 0x75, 0x19, 0x23, 0xA0, 0xF4,
  0x8F, 0xF8, 0x30
]

Src = [
  0x6F, 0x81, 0xA6, 0xC5, 0x63, 0xAC, 0x4B, 0xC7, 0x8F, 0x29,
  0x87, 0xA4, 0x27, 0xAA, 0xA6, 0x69, 0x4F, 0x27, 0xAE, 0xEC,
  0x27, 0x2E, 0xE7, 0xA9, 0x69, 0x87, 0x2E, 0xE5, 0x2F, 0x24,
  0xE6, 0x6F, 0x44, 0x87, 0xA9, 0x89, 0x4F, 0x26, 0x47, 0x21,
  0xAB, 0x01, 0xA7, 0xAE
]

enc = [
  0xdf, 0xc7, 0x4d, 0x14, 0xc1, 0xec, 0x8, 0xe4, 0x5f, 0x3f,
           0x3, 0xb4, 0x90, 0x4a, 0xb9, 0x8f, 0x8f, 0xfa, 0x71, 0x43,
           0xc7, 0xf1, 0x9d, 0xdd, 0x4f, 0xc0, 0x12, 0x44, 0x5c, 0x9d,
           0x88, 0x36, 0x2d, 0x16, 0x1d, 0xed, 0xbc, 0xef, 0xbb, 0x5b,
           0x9f, 0x77, 0xeb, 0x58
]

for k in range(0, 44, 4):
  a = Src[k]
  b = Src[k + 1]
  c = Src[k + 2]
  d = Src[k + 3]
  Src[k + 3] = ((a >> 5) & 0x7) | ((d << 3) & 0xf8)
  Src[k + 2] = ((d >> 5) & 0x7) | ((c << 3) & 0xf8)
  Src[k + 1] = ((c >> 5) & 0x7) | ((b << 3) & 0xf8)
  Src[k] = ((b >> 5) & 0x7) | ((a << 3) & 0xf8)

# 这个上面讲了
for i in range(len(Src)):
    print(chr(v29.index(Src[i])),end='')

print()

for i in range(44):
    print('a',end='')

这个解密很容易踩坑,我第一次写就踩了

比如Src[k+3] = ((a>>5) & 0x7) | ((d<<3)&0xf8),原来的加密中是

    for ( k = 0i64; k < v4; k += 4i64 )
    {
      v11 = Src[k + 3];
      v12 = Src[k + 2];
      v13 = ((unsigned __int8)Src[k] >> 3) | (32 * v11);
      v14 = 32 * Src[k + 1];
      Src[k + 1] = (32 * Src[k]) | ((unsigned __int8)Src[k + 1] >> 3);
      v15 = 32 * v12;
      Src[k + 2] = v14 | HIBYTE(v15);
      Src[k] = v13;
      Src[k + 3] = v15 | (v11 >> 3);
    }

Src[k+3]由v15和v11组成也就是Src[k+3]和Src[k+2]因此我写出了Src[k+3] = ((c>>5) & 0x7) | ((d<<3)&0xf8)这样的代码,实际是错的

因为其实原来的加密的意思是Src[k+3]由Src[k+2]的低5位和Src[k+3]的高3位组成,Src[k]由Src[k+3]的低5位和Src[k]的高3位组成

那么我们要还原Src[k+3]也就是由rc[k+3]的高3位和Src[k+3]的低5位拼接,这样才是正确的

这里解密写成这样也行:

for k in range(0, 44, 4):
  a = Src[k]
  b = Src[k + 1]
  c = Src[k + 2]
  d = Src[k + 3]
  Src[k + 3] = ((a >> 5) & 0xff) | ((d << 3) & 0xff)
  Src[k + 2] = ((d >> 5) & 0xff) | ((c << 3) & 0xff)
  Src[k + 1] = ((c >> 5) & 0xff) | ((b << 3) & 0xff)
  Src[k] = ((b >> 5) & 0xff) | ((a << 3) & 0xff)
0x7是低三位的意思,0x8f也差不多

全用0xff因为与的目的其实跟mod 256差不多,因为向高位移位的话如果不与可能会把高位的也搞进去比如

1111 0000 << 3 为 111 1000 0000其实我们最大是8位,但是|的时候不做处理会把高位也放进去

得到也是flag{GU!_r3v3R5e_3nG1n3er1ng_i5_v3ry_s1mpl3}

b >> 5) & 0xff) | ((a << 3) & 0xff)


> ```
> 0x7是低三位的意思,0x8f也差不多
> ```

全用0xff因为与的目的其实跟mod 256差不多,因为向高位移位的话如果不与可能会把高位的也搞进去比如

1111 0000 << 3 为 111 1000 0000其实我们最大是8位,但是|的时候不做处理会把高位也放进去

得到也是flag{GU!_r3v3R5e_3nG1n3er1ng_i5_v3ry_s1mpl3}

**这道题很质量**

http://www.kler.cn/a/381203.html

相关文章:

  • SQL,生成指定时间间隔内的事件次序号
  • jsp | servlet | spring forEach读取不了对象List
  • 微信小程序UI自动化测试实践 !
  • android EditText密码自动填充适配
  • javaEE-线程的常用方法-4
  • Qwen文章阅读笔记
  • 常见用于从 HTTP 请求中提取数据的注解
  • 使用VBA宏合并多个Excel文件的Sheet页
  • 【unique_str 源码学习】
  • 【Clikhouse 探秘】ClickHouse 物化视图:加速大数据分析的新利器
  • 小菜家教平台:基于SpringBoot+Vue打造一站式学习管理系统
  • 单链表的实现(数据结构)
  • 成为编程高手 day16
  • Python小白学习教程从入门到入坑------第二十五课 多态(语法进阶)
  • Vue.js 提供了一个事件系统,允许组件之间通过自定义事件进行通信
  • golang安装,常用框架安装,记忆点
  • ChatGPT o1与GPT-4o、Claude 3.5 Sonnet和Gemini 1.5 Pro的比较
  • 【Winform使用DataGridView实现表格数据的添加,编辑、删除、分页功能】
  • 【Go语言】| 第1课:Golang安装+环境配置+Goland下载
  • 基于Python的自然语言处理系列(50):Soft Prompt 实现
  • SAP 生产工单 bom组件 打删除标记
  • Spring Boot框架下的信息学科平台系统开发实战
  • SQL 常用更新操作
  • platform device的名字是怎么生成的?
  • 滑动窗口习题篇(下)
  • FreeRTOS学习8——开启任务调度器API函数简介