首页 > 其他分享 >NewStar easygui re wp

NewStar easygui re wp

时间:2024-11-01 18:20:15浏览次数:6  
标签:v93 0x8f Src v29 0x7 NewStar re 0xff easygui

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}

**这道题很质量**

标签:v93,0x8f,Src,v29,0x7,NewStar,re,0xff,easygui
From: https://blog.csdn.net/LH1013886337/article/details/143425222

相关文章

  • 一个简单的 ASP.NET Core 依赖注入例子,提高代码的可维护性和可扩展性
    前言:什么是依赖注入依赖注入可以提高代码的可维护性、可测试性、可替换性和可扩展性,降低组件之间的耦合度,使得代码更加清晰和灵活,ASP.NETCore提供了内置的依赖注入容器,可以帮助我们轻松地将服务注册到容器中。本文主要通过一个简单的例子来阐述ASP.NETCore依赖注入的使用......
  • MS Projcet 和 Redmine 在进行项目管理的时候有什么区别
    在进行项目管理的时候,MSProject和Redmine有以下主要的区别:1.平台属性不同;2.功能侧重点不同;3.使用者不同;4.兼容性不同;5.成本差异。总的来说,MSProject适用于规模较大、复杂度较高的项目管理,而Redmine适用于小型团队、开源社区以及迭代开发速度较快的项目管理。1.平台属性不同......
  • 遭遇gregn45.dll缺失?完整的应对gregn45.dll缺失修复指南全解析
    在使用电脑的过程中,有时会遇到应用程序或游戏提示“找不到gregn45.dll”或“gregn45.dll缺失”等错误消息。这通常意味着系统无法找到或加载这个关键的动态链接库(DLL)文件,导致相关应用程序或游戏无法正常运行。为了解决这个问题,以下是一份完整的应对gregn45.dll缺失的修复指南。......
  • 《保卫萝卜》客户端缺失pthreadvce2.dll 文件?详解《保卫萝卜》Luobo.exe 加载 pthread
    在享受《保卫萝卜》这款趣味横生的塔防游戏时,部分玩家可能会遇到游戏无法启动的问题,屏幕上弹出错误提示:“由于找不到pthreadvce2.dll,无法继续执行代码。”这一错误通常意味着你的电脑系统中缺失了pthreadvce2.dll这个关键的动态链接库(DLL)文件。别担心,本文将为你提供详细的修......
  • Android使用timer和thread实现定时器
    说明:两种方法实现android定时器,定时执行任务第一种方式:step1:packagecom.example.iosdialogdemo;importandroid.os.Bundle;importandroid.os.Handler;importandroidx.appcompat.app.AppCompatActivity;importjava.util.Timer;publicclassTimerActivityextends......
  • 系统网络通信异常Read timed out排查及解决过程
    用户反馈应用连接数据库经常出现网络通信异常相关sql语句在数据库在数据库客户端能正常执行,执行时间不到1s检查数据库参数,运行日志,网络方面,驱动版本都正常查看应用报错日志,里面有readtimdout报错,一般是连接超时导致的 检查durid连接池配置如下 Druid连接池conne......
  • ThreadLocal的使用以及原理
    ThreadLocal的使用以及原理  概要  ThreadLocal是java提供的一个方便对象在本线程内不同方法中进行传递和获取的类。用它定义的变量,仅在本线程中可见和维护,不受其他线程的影响,与其他线程相互隔离。  一、ThreadLocal能解决什么问题?  当涉及一个对象需要在很多......
  • el-progress进度条颜色渐变
    效果图:代码:<el-progressstroke-width="4"type="line":percentage="91.23"class="custom-progress"color="transparent">......
  • Nginx反向代理报错400 Request Header Or Cookie Too Large
    RequestHeaderOrCookieTooLarge1、问题2、原因3、解决1、问题Nginx反向代理报错400RequestHeaderOrCookieTooLarge400RequestHeaderOrCookieTooLarge2、原因假设需要重定向到:www.baidu.com报错的配置如下:location/my-pc{acce......
  • react是什么
    React是一个用于构建用户界面的JavaScript库。它由Facebook开发并维护,广泛用于构建单页应用程序(SPA)和移动应用程序。React的核心思想是通过组件化的方式来构建用户界面,使得代码更加模块化、可重用和易于维护。React的主要特点组件化:React将用户界面划分为多个独立的、可重......