[安洵杯 2019]crackMe

直接就退出程序了

找到关键函数了,好像用到了 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 就更有点懵逼了

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

cpp 复制代码
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++类的构造函数进行了初始化

cpp 复制代码
// 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 题了,很多相关知识需要学学。

被误导了,不是凯撒

python 复制代码
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)
相关推荐
凭君语未可5 分钟前
豆包MarsCode:小C点菜问题
算法
深蓝海拓18 分钟前
Pyside6(PyQT5)中的QTableView与QSqlQueryModel、QSqlTableModel的联合使用
数据库·python·qt·pyqt
深圳讯鹏科技21 分钟前
新能源工厂如何借助防静电手环监控系统保障生产安全
安全·防静电监控系统·esd防静电监控系统·防静电监控看板
GIS数据转换器23 分钟前
城市生命线安全保障:技术应用与策略创新
大数据·人工智能·安全·3d·智慧城市
C语言魔术师25 分钟前
【小游戏篇】三子棋游戏
前端·算法·游戏
自由自在的小Bird26 分钟前
简单排序算法
数据结构·算法·排序算法
无须logic ᭄26 分钟前
CrypTen项目实践
python·机器学习·密码学·同态加密
Channing Lewis39 分钟前
flask常见问答题
后端·python·flask
Channing Lewis40 分钟前
如何保护 Flask API 的安全性?
后端·python·flask
水兵没月2 小时前
钉钉群机器人设置——python版本
python·机器人·钉钉