[安洵杯 2019]crackMe

news2024/11/25 12:32:28

直接就退出程序了

找到关键函数了,好像用到了 hook

还有一个

嘿嘿,看着就是像 base64 只是 补‘=’改成了‘ ! ’

交叉引用啊,翻到一个应该是最后比较函数

1UTAOIkpyOSWGv/mOYFY4R!!

那一坨对 a1数组的操作没看懂

先总结一下就是 有一个大小写转换,类base64,两两交换位置,1,2,3

先解码得到乱码,估计就是后面两个先,123,132,312,321应该就这四种情况

试了两个没搞出来,烦 0_0 

靠,是对base表进行了大小写转换

看wp感觉自己分析的还是有点问题

这是main函数报红处汇编,扒到一个函数

都是在对字节的操作

就是 a2字节序改变-->v5[0-3] , 对a1异或-->v5[4-35],最后 v5[32-35]取字节-->a3

v5[i] = v5[i+4]^sub_411760(v5[i+1]^v5[i+2]^v5[i+3]^(key+4*i))

 en , y 是 用户输入 ,知道 y , z 就可以求出 x 

那就对 y z 进行交叉引用

对 x   

没有与输入的操作,可以直接动调获取

Handler_0第9行,这里注册了一个UEH函数,看看

在Windows编程中,UEH(Unhandled Exception Handler)通常指未处理异常处理器,处理程序在应用程序发生未处理的异常时执行特定的操作。在Windows中,可以使用 SetUnhandledExceptionFilter 函数来设置未处理异常的处理程序,可以帮助你在未处理的异常发生时更好地调试和维护应用程序。

额,别人的是这样的

(v3 >> (6 * (3 - k))) & 0x3F

(6*(3-k))计算位移量  ,

(v3>>x)&0x3f  -->偏移后结果只取其二进制的低6位

v3 >> (6 * (3 - 1)) = v3 >> 12
v3 = 0x12345678
0x12345678 >> 12 = 0x00012345
0x00012345 & 0x3F = 0x05  // 二进制: 00000101

对Table进行凯撒偏移24位

UEH这个函数返回时eip被修改为sub_121136,去看看,发现了最初比较函数

其实还没完,Hadler_0(对x交叉引用找到的)是如何被调用的,对其交叉引用

是注册了一个VEH函数来调用的

在Windows操作系统中,VEH(Vectored Exception Handling)是一种高级的异常处理机制,允许应用程序注册和管理异常处理程序。VEH提供了一种机制,可以在异常处理的各个阶段(包括未处理异常阶段)插入自定义处理程序。

再后面的 wp 就更有点懵逼了

继续往上面翻,找到了这个

int __cdecl sub_1227B0(int a1, char *String2, int a3)
{
  DWORD LastError; // eax
  DWORD flOldProtect[3]; // [esp+D0h] [ebp-90h] BYREF
  int (__stdcall *v6[3])(int, int, int, int); // [esp+DCh] [ebp-84h] BYREF
  LPCVOID lpAddress; // [esp+E8h] [ebp-78h]
  struct _MEMORY_BASIC_INFORMATION Buffer; // [esp+F4h] [ebp-6Ch] BYREF
  LPVOID lpBaseAddress; // [esp+118h] [ebp-48h]
  int v10; // [esp+124h] [ebp-3Ch]
  char *String1; // [esp+130h] [ebp-30h]
  int i; // [esp+13Ch] [ebp-24h]
  int v13; // [esp+148h] [ebp-18h]
  int v14; // [esp+154h] [ebp-Ch]

  v14 = a1;
  v13 = a1 + *(_DWORD *)(a1 + 60) + 24;
  for ( i = *(_DWORD *)(v13 + 104) + a1; i; i += 20 )
  {
    String1 = (char *)GetModuleHandleW(0) + *(_DWORD *)(i + 12);
    if ( !stricmp(String1, String2) )
      break;
  }
  if ( !i )
    return 0;
  v10 = *(_DWORD *)(i + 16) + a1;
  if ( !v10 )
    return 0;
  while ( 1 )
  {
    if ( !*(_DWORD *)v10 )
      return 0;
    lpBaseAddress = (LPVOID)v10;
    if ( *(_DWORD *)v10 == a3 )
      break;
    v10 += 4;
  }
  lpAddress = (LPCVOID)(v10 >> 12 << 12);
  VirtualQuery(lpAddress, &Buffer, 0x3E8u);
  VirtualProtect((LPVOID)lpAddress, Buffer.RegionSize, 0x40u, &Buffer.Protect);
  v6[0] = sub_121023;
  if ( WriteProcessMemory((HANDLE)0xFFFFFFFF, lpBaseAddress, v6, 4u, 0) )
  {
    VirtualProtect(Buffer.BaseAddress, Buffer.RegionSize, Buffer.Protect, flOldProtect);
    return 1;
  }
  else
  {
    LastError = GetLastError();
    printf("%d\n", LastError);
    return 0;
  }
}

对里面调用的函数进行符号还原,前面就是找到 user32.dll 对应的 IMAGE_IMPORT_DESCRIPTOR 结构体地址,然后找到 MessageBoxW 对应的 IMAGE_THUNK_DATA 结构体地址,用VirtualProtect修改页属性为可写,用WriteProcessMemory将IMAGE_THUNK_DATA字段覆写为sub_121023函数地址

总结一下,典型的IAT hook,将MessageBoxW的IAT地址替换为了sub_411023的函数地址,该函数完成了VEH的注册

还没完,继续分析这个IAT hook函数是被谁调用的,引用回溯到了rdata这里,往上翻翻

就是sub_121e40继续往上

这个地方被tmainCRTStartup调用了 ?

就是上面那个调用是在tmainCRTStartup里应该

看看tmainCRTStartUp函数,32行这里initterm_e调用了rdata区域里保存的函数,对全局/静态C++类的构造函数进行了初始化

// write access to const memory has been detected, the output may be wrong!
int __tmainCRTStartup()
{
  int v1; // [esp+18h] [ebp-24h]
  signed __int32 v2; // [esp+1Ch] [ebp-20h]
  signed __int32 v3; // [esp+20h] [ebp-1Ch]

  v2 = *(_DWORD *)(j__NtCurrentTeb() + 4);
  v1 = 0;
  while ( 1 )
  {
    v3 = _InterlockedCompareExchange(dword_12A6EC, v2, 0);
    if ( !v3 )
      break;
    if ( v3 == v2 )
    {
      v1 = 1;
      break;
    }
  }
  if ( dword_12A6FC == 1 )
  {
    j__amsg_exit(31);
    goto LABEL_13;
  }
  if ( dword_12A6FC )
  {
    dword_12A2DC = 1;
    goto LABEL_13;
  }
  dword_12A6FC = 1;
  if ( !j__initterm_e((_PIFV *)&First, (_PIFV *)&Last) )
  {
LABEL_13:
    if ( dword_12A6FC == 1 )
    {
      j__initterm((_PVFV *)&dword_127000, (_PVFV *)&dword_127208);
      dword_12A6FC = 2;
    }
    if ( dword_12A6FC != 2
      && CrtDbgReportW(
           2,
           L"f:\\dd\\vctools\\crt\\crtw32\\dllstuff\\crtexe.c",
           553,
           0,
           L"%s",
           L"__native_startup_state == __initialized") == 1 )
    {
      __debugbreak();
    }
    if ( !v1 )
      _InterlockedExchange(dword_12A6EC, 0);
    if ( dword_12A714 )
    {
      if ( j___IsNonwritableInCurrentImage(&dword_12A714) )
        dword_12A714(0, 2, 0);
    }
    CrtSetCheckCount(1);
    _initenv = envp;
    main(argc, (const char **)argv, (const char **)envp);
  }
  return 255;
}

en, 从结果一步步推导原因,这就很 reverse

上面的分析是根据结果查找原因,倒着推回去的比较乱,下面再梳理总结下

异常处理的注册

  • 程序初始化时,调用链为start->tmainCRTStartUp->initterm_e->IAT hook,修改MessageBoxW函数的IAT表,在主函数中调用MessageBoxW,实际调用的是注册VEH的函数,并对base64编码表进行了变换
  • 在main函数中对SEH进行了注册
  • 在VEH handler中对UEH进行了注册

异常处理的回调

  • 需要知道一个知识点,Windows 用户态异常发生先找调试器,没有再找 VEH,VEH 处理不了再找 SEH, SEH 还处理不了找 UEF

  • main函数中触发内存写异常,本程序各级异常处理的返回状态都是未完成处理,会继续往下级调异常处理函数,所以本程序的调用顺序为VEH->SEH->UEH

  • VEH中对sm4的box进行了初始化

  • SEH中对input进行sm4加密,获得output

  • UEH中对output进行变种的base64加密,获得Str1,并且和变换过的固定字符串Str2进行比较

总之,题目的算法分析和还原很简单,麻烦的是这些算法没有集中在main中,而是分散到了各个异常处理函数里面,跳来跳去的对分析造成了干扰,不过只要足够有耐心,不断向上查找引用,还是能分析完的

https://www.cnblogs.com/z5onk0/p/17506136.html

嗯,也是一道经典的 hook 题了,很多相关知识需要学学。

 被误导了,不是凯撒

import base64
import sm4
table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# base表大小写转换
new_table=''
for char in table:
    tmp=ord(char)
    if 97<=tmp<=122:
        new_table+=chr(tmp-32)
    elif 65<=tmp<=90:
        new_table+=chr(tmp+32)
    else:
        new_table+=char
# base表 %24
table+='='
test=new_table
new_table=new_table[24:]+new_table[:24]+'!'
def kaisha(enc):
    str=''
    for char in enc:
        tmp=ord(char)
        if 97<=tmp<=122:
            if tmp+24<=122:
                str+=chr(tmp+24)
            else:
                str+=chr(tmp+24-122+97)
        elif 65<=tmp<=90:
            if tmp+24<=90:
                str+=chr(tmp+24)
            else:
                str+=chr(tmp+24-90+65)
        else:
            str+=char
    return str
print(new_table)
print(kaisha(test)+'!')

str2="1UTAOIkpyOSWGv/mOYFY4R!!"
# 字符串两两交换
str2_swap=''
for i in range(0,len(str2)-1,2):
    str2_swap+=str2[i+1]+str2[i]

# base64解密
b64str=''
for str in str2_swap:
    b64str+=table[new_table.find(str)]
enflag=base64.b64decode(b64str)

# sm4 解密
key=sm4.SM4Key(b"where_are_u_now?")
flag=key.decrypt(enflag)
print(flag)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1689327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang的基本使用

目录 变量的声明 Golang常用容器 defer 有趣的多态 结构体标签和reflect 反射 Golang最强的协程 channel go可能造成的内存泄露 变量的声明 方法 1:有类型,有var,不赋值 在Golang中默认值为0 方法 2:无类型,有var,赋值 方法 3:无类型,无var,赋值 多变量声明 多变…

C语言 数组——查找算法的函数实现

目录 线性查找&#xff08;Linear Search&#xff09; 线性查找的性能 猜数游戏 二分查找&#xff08;Binary Search&#xff09; 并非吹毛求疵&#xff0c;鸡蛋里挑骨头 二分查找的性能 线性查找&#xff08;Linear Search&#xff09; 不 要求数据表是已排好序的  …

AI巨头争相与Reddit合作:为何一个古老的论坛成为AI训练的“宝藏”?

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

这种电脑原来这么耗电……震惊了粉丝小姐姐

前言 在今年1月份的时候&#xff0c;一位来自重庆的小姐姐加了小白&#xff0c;咨询电脑的问题&#xff1a; 哦豁&#xff0c;这个电脑看着确实闪闪发光&#xff0c;是真的很漂亮&#xff5e;&#xff08;嗯&#xff0c;小姐姐也很漂亮&#xff09; 电脑无法开机&#xff0c;按…

华为云发布ServiceStage:内置优秀业界实践「云应用管理和运维」模板

迅猛发展的业务让企业IT系统架构愈加复杂&#xff0c;为满足业务快速迭代的需求&#xff0c;重点企业完成从传统单体应用架构到分布式微服务架构的升级。微服务架构虽可提升研发效率&#xff0c;但数量庞大的微服务实例间错综复杂的依赖关系无疑增加了部署运维难度。 同时&…

Go源码--sync库(1)sync.Once和

简介 这篇主要介绍 sync.Once、sync.WaitGroup和sync.Mutex sync.Once once 顾名思义 只执行一次 废话不说 我们看源码 英文介绍直接略过了 感兴趣的建议读一读 获益匪浅 其结构体如下 Once 是一个严格只执行一次的object type Once struct {// 建议看下源码的注解&#xf…

汇编语言(STC89C52)

指令是计算机计算CPU根据人的意图来执行某种操作的命令。一台计算机所执行的全部指令的集合&#xff0c;称为这个CPU的指令系统。而想要使计算机按照人们的要求完成一项工作&#xff0c;就必须让CPU按顺序执行预设的操作&#xff0c;即逐条执行人们编写的指令。这种按照人民要求…

暴雨信息液冷计算解决方案亮相CCIG 2024

5月24日&#xff0c;2024中国图象图形大会&#xff08;CCIG&#xff09;在陕西西安正式开幕。作为涵盖图像图形各专业领域的综合性的全国性学术会议&#xff0c;CCIG面向开放创新、交叉融合的发展趋势&#xff0c;为图像图形相关领域的专家学者和产业界的同仁&#xff0c;搭建了…

本是梦中人,常作花下客。心中自往来,知我有几个。

我们总是喜欢拿“顺其自然”来敷衍人生道路上的荆棘坎坷&#xff0c;却很少承认&#xff0c;真正的顺其自然&#xff0c; 其实是竭尽所能之后的不强求&#xff0c; 而非两手一摊的不作为。 一花凋零荒芜不了整个春天&#xff0c; 一次挫折也荒废不了整个人生。 多年后&#x…

头歌openGauss-存储过程第1关:创建存储过程

编程要求 1、创建第1个存储过程&#xff0c;并调用&#xff1b; 1&#xff09;创建存储过程&#xff0c;查询emp表数据&#xff1b; 2&#xff09;调用存储过程&#xff1b; --创建存储过程&#xff0c;获得计算机&#xff08;cs&#xff09;系学生选课情况并将结果写入临时表t…

免费wordpress中文主题

免费大图wordpress主题 首页是一张大图的免费wordpress主题模板。简洁实用&#xff0c;易上手。 https://www.jianzhanpress.com/?p5857 免费WP模板下载 顶部左侧导航条的免费WP模板&#xff0c;后台简洁&#xff0c;新手也可以下载使用。 https://www.jianzhanpress.com/…

d20(184-190)-勇敢开始Java,咖啡拯救人生

目录 网络通信 网络通信三要素&#xff08;IP地址&#xff0c;端口号&#xff0c;协议 IP地址 InetAddress 端口号 协议 传输层的两个通信协议 UDP通信 java.net.Datagramsocket类 客户端 服务端 UDP通信多收多发 客户端 服务端 TCP通信 java.net.Socket类 客…

【MYSQL】分数排名

表: Scores ---------------------- | Column Name | Type | ---------------------- | id | int | | score | decimal | ---------------------- id 是该表的主键&#xff08;有不同值的列&#xff09;。 该表的每一行都包含了一场比赛的分数。Score 是…

Go微服务: 日志系统ELK的应用

概述 基于前文&#xff0c;我们已经了解并搭建完成ELK的所有环境了&#xff0c;现在我们来结合应用程序来使用ELK参考前文&#xff1a;https://active.blog.csdn.net/article/details/138898538 封装日志模块 在通用工具模块: gitee.com/go-micro-services/common 这个包是通…

【C语言】——函数栈帧的创建与销毁

函数栈帧的创建与销毁 本文主要讲解了函数调用过程中其栈帧的创建与销毁&#xff0c;内容干货较多&#xff0c;希望大家认真品味。 使用C语言进行函数调用时&#xff0c;是否会有很多疑问&#xff1a; 1.局部变量是如何创建的&#xff1f; 2.局部变量在未初始化的情况下&#x…

物理服务器介绍

物理服务器介绍 概述分类按服务器应用分类按服务器结构分类塔式服务器机架式服务器刀片式服务器机架式服务器与刀片式服务器的对比按处理器个数分类按处理器架构分类 主板概述工作原理物理结构技术参数 CPU概述工作原理指令集相关技术技术参数主流产品 内存概述类型相关技术技术…

安卓分身大师4.6.0解锁会员安卓14可用机型伪装双开多开

需登录解锁会员功能&#xff0c;除了加速进入不能&#xff0c; 其他主要功能都是可以使用&#xff0c;由于验证较多一些功能需要特定操作使用&#xff0c;进行伪装时请不要直接伪装&#xff0c;先生成成功后再进行自定义伪装&#xff01;链接&#xff1a;https://pan.baidu.com…

Transformer详解(3)-多头自注意力机制

attention multi-head attention pytorch代码实现 import math import torch from torch import nn import torch.nn.functional as Fclass MultiHeadAttention(nn.Module):def __init__(self, heads8, d_model128, droput0.1):super().__init__()self.d_model d_model # 12…

通过el-tree自定义渲染网页版工作目录,实现鼠标悬浮显示完整名称、用icon区分文件和文件夹等需求

目录 一、通过el-tree自定义渲染网页版工作目录 1.1、需求介绍 1.2、使用el-tree生成文档目录 1.2.1、官方基础用法 ①效果 ②代码&#xff1a; 1.2.2、自定义文档目录&#xff08;实现鼠标悬浮显示完整名称、用icon区分文件和文件夹&#xff09; ①效果&#xff08;直接效…

JavaScript表达式和运算符

表达式 表达式一般由常量、变量、运算符、子表达式构成。最简单的表达式可以是一个简单的值。常量或变量。例&#xff1a;var a10 运算符 运算符一般用符号来表示&#xff0c;也有些使用关键字表示。运算符由3中类型 1.一元运算符&#xff1a;一个运算符能够结合一个操作数&…