[安洵杯 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)
相关推荐
群联云防护小杜23 分钟前
如何给负载均衡平台做好安全防御
运维·服务器·网络·网络协议·安全·负载均衡
ihengshuai23 分钟前
HTTP协议及安全防范
网络协议·安全·http
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习1 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
黑客Jack1 小时前
防御 XSS 的七条原则
安全·web安全·xss
ALISHENGYA2 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo2 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
黄公子学安全2 小时前
Java的基础概念(一)
java·开发语言·python
jackiendsc2 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
云云3212 小时前
怎么通过亚矩阵云手机实现营销?
大数据·服务器·安全·智能手机·矩阵