170927 逆向-Reversing.kr(Position)

本文详细介绍了对Reversing.kr的Position挑战的逆向分析过程,涉及程序校验Name和Serial的算法。通过查找关键函数sub_401740,解析了Name转化为Serial的步骤,包括ASCII加常数、位操作和特定顺序相加。最终,通过数学解题和编写脚本,成功找到符合要求的Name,从而解出Correct答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1625-5 王子昂 总结《2017年9月27日》 【连续第360天总结】
A. Reversing.kr-Position
B.

Position

惯例先查壳
读Readme可以知道是一个检查Name-Serial的程序,我们需要找到对应Serial为”76876-77776”的Name

由于是个GUI程序,只能依靠API或者字符串来定位事件
IDA中没有找到对应的字符串,很是神奇
在OD中搜索却正常能搜索到,于是定位到核心事件函数sub_401740

由于函数太长就不放全部的了,基本上是同一个结构:


  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&Name);
  v1 = 0;
  v64 = 0;
  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&Serial);
  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&v63);
  LOBYTE(v64) = 2;
  CWnd::GetWindowTextW(a1 + 304, &Name);        // Name
  if ( *(_DWORD *)(Name - 12) == 4 )            // Name长度为4
  {
    i = 0;
    while ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&Name, i) >= 0x61u
         && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&Name, i) <= 0x7Au )// 字母
    {
      if ( ++i >= 4 )                           // 4个都为字母
      {
LABEL_8:
        v5 = 0;
        while ( 1 )
        {
          if ( v1 != v5 )
          {
            v6 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&Name, v5);
            if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&Name, v1) == v6 )// 4个字母各不相同
              goto Fail;
          }
          if ( ++v5 >= 4 )
          {
            if ( ++v1 < 4 )
              goto LABEL_8;                     // 4个字母各不相同
            CWnd::GetWindowTextW(a1 + 420, &Serial);
            if ( *(_DWORD *)(Serial - 12) != 11 // Serial长度为11
              || (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&Serial, 5) != 45 )// 第六个字符为-
            {
              goto Fail;
            }
            v7 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&Name, 0);
            v8 = (v7 & 1) + 5;
            v59 = ((v7 >> 4) & 1) + 5;
            v53 = ((v7 >> 1) & 1) + 5;
            v55 = ((v7 >> 2) & 1) + 5;
            v57 = ((v7 >> 3) & 1) + 5;
            v9 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&Name, 1);
            v45 = (v9 & 1) + 1;
            v51 = ((v9 >> 4) & 1) + 1;
            v47 = ((v9 >> 1) & 1) + 1;
            v10 = ((v9 >> 2) & 1) + 1;
            v49 = ((v9 >> 3) & 1) + 1;
            v11 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&v63);
            itow_s(v8 + v10, v11, 0xAu, 10);    // 将v8+v10放入v11中
            v12 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v63, 0);
            v13 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&Serial, 0);
            v2 = &v63;
            if ( v13 == v12 )
            {
              ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&v63, -1);
              v14 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&v63);
              itow_s(v57 + v49, v14, 0xAu, 10);
              v15 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&Serial, 1);
              v16 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&v63, 0);
              v2 = &v63;
              if ( v15 == v16 )
              {

流程虽然长但是挺简单的:
首先校对Name为4个各不相同的小写字母
然后按顺序取前5位二进制,第1、3个字母的数各+5,2、4个字母的数各+1
然后分别按特定顺序进行相加组合,并校对
第1、2个字母生成前五位,第3、4个字母生成后五位

如abcd
对应的前五位分别为00001, 00010, 00011, 00100
加常数并改变顺序(逆序后将第五位移至第二位)后得到结果
65555, 11211, 65655, 11121
然后按特定顺序相加
65555
11211
→76667
65655
11121
→86766
所以最终的Serial为76667-89766

很容易写出来KeyGen:

f = []
for i in range(len(name)):
    if i%2 :
        o = 1
    else:
        o = 5
    i = ord(s[i])
    f0 = []
    f0.append((i & 1) + o)
    f0.append(((i >> 4) & 1) + o)
    f0.append(((i >> 1) & 1) + o)
    f0.append(((i >> 2) & 1) + o)
    f0.append(((i >> 3) & 1) + o)
    f.append(f0)
a=[0, 4, 2, 3, 1]#顺序由IDA中分析得来
b=[3, 4, 1, 0, 2]
for i in range(5):
    print(( f[0][a[i]] + f[1][b[i]] ), end='')#前五位
print('-', end='')
for i in range(5):
    print(( f[2][a[i]] + f[3][b[i]] ), end='')#后五位

接着考虑逆向,要求Serial为76876-77776
思考了一下这里是按位操作,还有多种可能,没想到什么快捷写脚本的方法,就乖乖数学解题吧

我们知道每个位都经过了a组的+5和b组的+1,所以实际上说明它们的对应位和应该是
10210-11110
这其中特殊的是0和2,意味着对应位分别都是0和都是1;关键在于1是哪个字母带来的

Name到Serial一共经过了3步变换:
1.取前五位ASCII并加常数5/1
2.改变顺序(逆序后将第五位放至第二位)
3.分别取两个字母的特定位置数相加

逐步逆向:
首先知道提示中说了最后一个字母是’p’,Serial和为10210-11110
将p输入正向脚本可以得到12111,即01000
相加的顺序按照b组对应为00100
则能知道另一半的和为11010
按照a组顺序(04231)还原得到10011
将第二位还原至第五位然后逆序,得到01101
则第三个字母的ASCII为0b1101101,即109,即’m’

然后是前两个字母,这次虽然提示,但是由于2和0的特性,可以大大减小可能性:
一共有2个1,即最多出现2x2=4种情况,可以接受

两个数的格式必然为x01y0(两个x中必然一个是1一个是0,y同理)
分别按照a组和b组的顺序还原:
a:
x01y0
x1y00
00y1x

b:
y10x0
y0x01
10x0y

则可能的格式有:
a,b
00010, 10101
00011, 10100
00111, 10000
00100, 10001

写脚本分别输出

flag =[[0b1100010, 0b1110101],[0b1100011, 0b1110100],[0b1100111, 0b1110000],[0b1100100, 0b1110001]]
for i in flag:
    print(chr(i[0])+chr(i[1])+'mp')

bump
ctmp
gpmp
dqmp

第一个是有意义的单词,输入程序验证果然Correct
完成

除了爆破好像没想到什么快速逆向的思路(:з」∠)有大佬做过的话希望能指点一下~

C. 明日计划
Reversing.kr

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值