首页
社区
课程
招聘
[原创]CTF2017 第八题 loudy-CrackMe 解题报告
发表于: 2017-6-17 13:40 6493

[原创]CTF2017 第八题 loudy-CrackMe 解题报告

2017-6-17 13:40
6493

查看 main 函数,流程比较简单

OllyDBG 断 0x403540,查看 esi 所指内存,拿到 指令->执行函数 对应表

其实 check 函数就是之前发过的 402450 函数,因为之前脚本还原了代码,现在完全不一样了,但也挺简洁明了,注意其中的 strlen(ipt) == 90

很简单的流程

python 算一下 part1_bn = 15691529100101820131

其实这个题目换成求幂(对数,也叫离散对数),难度会大几倍

接下来就要上数论课了(看雪不支持 mathjax,发截图)


  [2]: https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%AE%9A%E7%90%86_%28%E6%95%B0%E8%AE%BA%29

  [3]: https://zh.wikipedia.org/wiki/%E6%A8%A1%E5%8F%8D%E5%85%83%E7%B4%A0

  [4]: https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%87%BD%E6%95%B0

因为 第三部分 因为程序的原因有几个限制


void thread1(LPVOID lpThreadParameter)
{
  while ( 1 )
  {
    tmp[] = {0x8Bu, 0xC0u, 0x8Bu, 0xFFu, 0x8Bu, 0xDBu};
    WaitForSingleObject(hMutex, 0xFFFFFFFF);
    if ( func1_flag == 1 )
    {
      flOldProtect = 0;
      VirtualProtect(&loc_401EC0, 0x2Eu, 0x40u, &flOldProtect);
      v1 = 1;
      do
      {
        *((_BYTE *)&loc_401EC0 + v1 - 1) ^= byte_4064F0[v1 - 1];
        *((_BYTE *)&loc_401EC0 + v1) ^= byte_4064F0[v1];
        v1 += 2;
      }
      while ( v1 - 1 < 46 );
	  // 将 401EC0 开始的 0x2E 个字节逐字节与 4064F0 开始的数组异或
      VirtualProtect(&loc_401EC0, 0x2Eu, flOldProtect, &flOldProtect);
      VirtualProtect(dword_4068E4, 6u, 0x40u, &flOldProtect);
      v2 = 0;
      v3 = (_BYTE *)dword_4068E4 - tmp;
      do
      {
        v4 = tmp[v2];
        v5 = &tmp[v2];
        v6 = *(&tmp[v2++] + v3);
        v5[v3] = v4;
        *v5 = v6;
      }
      while ( v2 < 6 );
	  // 直接将 tmp 数组写入 4068E4 指针所指向的地址
      VirtualProtect(dword_4068E4, 6u, flOldProtect, &flOldProtect);
      func1_flag = 0;
    }
    ReleaseMutex(hMutex);
    Sleep(50u);
  }
}

图1
图1
void thread1(LPVOID lpThreadParameter)
{
  while ( 1 )
  {
    tmp[] = {0x8Bu, 0xC0u, 0x8Bu, 0xFFu, 0x8Bu, 0xDBu};
    WaitForSingleObject(hMutex, 0xFFFFFFFF);
    if ( func1_flag == 1 )
    {
      flOldProtect = 0;
      VirtualProtect(&loc_401EC0, 0x2Eu, 0x40u, &flOldProtect);
      v1 = 1;
      do
      {
        *((_BYTE *)&loc_401EC0 + v1 - 1) ^= byte_4064F0[v1 - 1];
        *((_BYTE *)&loc_401EC0 + v1) ^= byte_4064F0[v1];
        v1 += 2;
      }
      while ( v1 - 1 < 46 );
	  // 将 401EC0 开始的 0x2E 个字节逐字节与 4064F0 开始的数组异或
      VirtualProtect(&loc_401EC0, 0x2Eu, flOldProtect, &flOldProtect);
      VirtualProtect(dword_4068E4, 6u, 0x40u, &flOldProtect);
      v2 = 0;
      v3 = (_BYTE *)dword_4068E4 - tmp;
      do
      {
        v4 = tmp[v2];
        v5 = &tmp[v2];
        v6 = *(&tmp[v2++] + v3);
        v5[v3] = v4;
        *v5 = v6;
      }
      while ( v2 < 6 );
	  // 直接将 tmp 数组写入 4068E4 指针所指向的地址
      VirtualProtect(dword_4068E4, 6u, flOldProtect, &flOldProtect);
      func1_flag = 0;
    }
    ReleaseMutex(hMutex);
    Sleep(50u);
  }
}
int sub_402450(const char *a1)
{
  dword_4068E4 = &loc_4025DC;
  dword_4068E0 = &loc_40263C;
  dword_4068EC = &loc_40269C;
  if ( (unsigned __int8)((int (*)(void))sub_4027F0)() )
  {
    v8 = strlen(a1);
    if ( v8 <= 256 )
    {
      v2 = 0;
      memset(&Dst, 0, 0xFFu);
      v9 = sub_401D50(&v2);
      if ( strlen(&v2) == 90 && v4 == 45 && v5 == 45 && v6 == 45 && v7 == 45 )
      {
        if ( !(unsigned __int8)sub_4027F0(90) )
          ExitProcess(1u);
        dword_99B6EC = 1;
        while ( dword_99B6EC )
          ;
      }
    }
  }
  return 0;
}
import idaapi
tmp_point = {0x4068E4:0x4025DC, 0x4068E0:0x40263C, 0x4068EC:0x40269C}
def dec_func(func_addr, key_addr, tmp_addr, tmp):
    func = bytearray(idaapi.get_many_bytes(func_addr, 0x2E))
    key = bytearray(idaapi.get_many_bytes(key_addr, 0x2E))
    for i in xrange(len(func)):
        func[i] ^= key[i]
    idaapi.patch_many_bytes(func_addr, str(func))
    idaapi.patch_many_bytes(tmp_point[tmp_addr], str(bytearray(tmp)))
dec_func(0x401EC0, 0x4064F0, 0x4068E4, [0x8B, 0xC0, 0x8B, 0xFF, 0x8B, 0xDB])
dec_func(0x402090, 0x406520, 0x4068E0, [0x8B, 0xC0, 0x8B, 0xDB, 0x8B, 0xFF])
dec_func(0x4021D0, 0x406550, 0x4068EC, [0x8B, 0xFF, 0x8B, 0xC0, 0x8B, 0xDB])
# 代码段
text = [0xAA, 0x15, 0x20, 0x01, 0x00, 0x00, 0xAA, 0x15, 0x40, 0x01, 0x00, 0x00, 0xA0, 0x10, 0x00, 0x00,
        0x00, 0x00, 0xA8, 0xA0, 0x10, 0xF0, 0x00, 0x00, 0x00, 0xA8, 0xA0, 0x10, 0x60, 0x01, 0x00, 0x00,
        0xA7, 0xAA, 0x11, 0x80, 0x00, 0x00, 0x00, 0xAA, 0x10, 0x60, 0x00, 0x00, 0x00, 0xAA, 0x12, 0xB0,
        0x00, 0x00, 0x00, 0xA9, 0xA2, 0xEA, 0xA6, 0x0E, 0xA0, 0x10, 0x20, 0x01, 0x00, 0x00, 0xA0, 0x11,
        0x10, 0x01, 0x00, 0x00, 0xA4, 0xA5, 0xA0, 0x10, 0x40, 0x01, 0x00, 0x00, 0xA0, 0x11, 0x10, 0x01,
        0x00, 0x00, 0xA4, 0xA5]
# 数据段
data = [0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0xBF, 0xB4, 0xD1, 0xA9, 0x43, 0x54, 0x46, 0x32,
        0x30, 0x31, 0x37, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x0A, 0x0A, 0x00, 0x00,
        0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
        0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
        0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
        0x77, 0x78, 0x79, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x58, 0x5A, 0x54, 0x52, 0x50, 0x52, 0x54, 0x5A, 0x58, 0x5A, 0x52, 0x54, 0x5A, 0x58, 0x5A, 0x44,
        0x42, 0x40, 0x42, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x50, 0x57, 0x57, 0x5D, 0x52, 0x5E, 0x57, 0x5E, 0x5C, 0x58, 0x5B, 0x5F, 0x5B, 0x5C, 0x5A, 0x48,
        0x45, 0x4A, 0x47, 0x40, 0x47, 0x42, 0x40, 0x4D, 0x48, 0x4D, 0x51, 0x57, 0x52, 0x54, 0x57, 0x51,
        0x5F, 0x59, 0x51, 0x52, 0x5F, 0x5F, 0x55, 0x58, 0x5E, 0x41, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x58, 0x55, 0x56, 0x57, 0x54, 0x54, 0x53, 0x5E, 0x51, 0x5A, 0x52, 0x5B, 0x58, 0x5D, 0x5E, 0x42,
        0x45, 0x44, 0x4B, 0x44, 0x4C, 0x41, 0x42, 0x4B, 0x48, 0x48, 0x55, 0x54, 0x5B, 0x54, 0x5C, 0x51,
        0x52, 0x5B, 0x58, 0x58, 0x5F, 0x5A, 0x55, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x70, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65,
        0x20, 0x6B, 0x65, 0x79, 0x3A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xBF, 0xB4, 0xD1, 0xA9, 0x43, 0x54, 0x46, 0x32, 0x30, 0x31, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x18, 0x0D, 0x16, 0x44, 0x02, 0x09, 0x13, 0x48, 0x1D, 0x02, 0x0E, 0x4C, 0x1F, 0x07, 0x08, 0x18,
        0x05, 0x52, 0x18, 0x11, 0x0C, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x18, 0x0D, 0x16, 0x16, 0x45, 0x0D, 0x02, 0x11, 0x49, 0x03, 0x18, 0x4C, 0x03, 0x01, 0x1B, 0x50,
        0x03, 0x1B, 0x14, 0x1C, 0x01, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x18, 0x0D, 0x16, 0x44, 0x02, 0x09, 0x13, 0x48, 0x1D, 0x02, 0x0E, 0x4C, 0x1F, 0x07, 0x08, 0x18,
        0x05, 0x52, 0x18, 0x11, 0x0C, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x18, 0x0D, 0x16, 0x16, 0x45, 0x0D, 0x02, 0x11, 0x49, 0x03, 0x18, 0x4C, 0x03, 0x01, 0x1B, 0x50,
        0x03, 0x1B, 0x14, 0x1C, 0x01, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xBF, 0xB4, 0xD1, 0xA9, 0x43, 0x54, 0x46, 0x32, 0x30, 0x31, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00]
# 填充数据段到 0x1000 长度
for _ in xrange(0x1000 - len(data)):
    data.append(0)
def toUint(arr):
    return arr[0] | (arr[1]<<8) | (arr[2]<<16) | (arr[3]<<24)
class Context:
    def __init__(self):
        self.ip = 0
    def cmdA0(self):
        c = text[self.ip + 1]
        p = toUint(text[self.ip+2: self.ip+6])
        self.ip += 6
        if 0x10 <= c <= 0x13:
            # 寄存器间 mov
            print "mov r{0}, {1}".format(c - 0x10, hex(p))
        elif c == 0x14:
            # 这2个是 寄存器和内存间 mov,1字节
            print "movb r0, [{0}]".format(hex(p))
        elif c == 0x15:
            print "movb [{0}], r0".format(hex(p))
        else:
            assert False
    def cmdA1(self):
        self.ip += 1
        # 异或结果存 r0
        print "xor r0, r1"
    def cmdA2(self):
        p = text[self.ip + 1]
        self.ip += 2
        # 1字节比较 r0 和 内存,存下比较状态
        print "equb r0, [{0}]".format(hex(p))
    def cmdA3(self):
        assert False
    def cmdA4(self):
        self.ip += 1
        # 弹出对话框
        print "msg [r0], [r1]"
    def cmdA5(self):
        self.ip += 1
        # 退出
        print "exit"
    def cmdA6(self):
        p = text[self.ip + 1]
        self.ip += 2
        # 比较状态为假则跳转相对地址
        print "jne +{0}".format(hex(p))
    def cmdA7(self):
        self.ip += 1
        # 输入到内存
        print "in [r0]"
    def cmdA8(self):
        self.ip += 1
        # 输出内存到屏幕
        print "out [r0]"
    def cmdA9(self):
        # 调用内置校验函数,返回值存 r0
        print "check [r0]"
        self.ip += 1
    def cmdAA(self):
        c = text[self.ip + 1]
        p = toUint(text[self.ip+2: self.ip+6])
        self.ip += 6
        if 0x10 <= c <= 0x12:
            # 将后2个内存进行按字节异或存第一个全局变量
            print "xorstr key{0}, [{1}], [32]".format(c - 0x10, hex(p))
        else:
            # 进行按字节异或存第一个内存
            print "xorstr [{0}], [32]".format(hex(p))
    def run(self):
        ops = [self.cmdA0, self.cmdA1, self.cmdA2, self.cmdA3, self.cmdA4, self.cmdA5,
                self.cmdA6, self.cmdA7, self.cmdA8, self.cmdA9, self.cmdAA]
        while self.ip < len(text):
            c = text[self.ip]
            ops[c - 0xA0]()
ctx = Context()
ctx.run()

print "-----------------"
def xorstr(name, addr, xoraddr):
    txt = ""
    i = 0
    while True:
        t = data[addr + i]
        if t == 0:
            break
        txt += chr(t ^ data[32 + i])
        i += 1
    print name, txt
xorstr("key0:", 0x60, 32)
xorstr("key1:", 0x80, 32)
xorstr("key2:", 0xB0, 32)
xorstr("ok:", 0x120, 32)
xorstr("fail:", 0x140, 32)
print "[0xea] = ", hex(data[0xea])
xorstr [0x120], [32] ; you got the right key!
xorstr [0x140], [32] ; your key is not right!
mov r0, 0x0
out [r0]
mov r0, 0xf0
out [r0]
mov r0, 0x160
in [r0] ; 获取用户输入,存数据地址0x160,寄存器 r0 指向它
xorstr key1, [0x80], [32] ; 1549780652036258484424751705102781884386113
xorstr key0, [0x60], [32] ; 98765432109876543210123
xorstr key2, [0xb0], [32] ; 9753124680975312468097531246809753124680
check [r0] ; 调用检验函数
equb r0, [0xea] ; [0xea] = 1,意思是上面的函数返回真
jne +0xe ; 假就跳到失败框
mov r0, 0x120
mov r1, 0x110
msg [r0], [r1] ; 成功信息框
exit
mov r0, 0x140
mov r1, 0x110
msg [r0], [r1] ; 失败信息框
exit
int __cdecl check(const char *src)
{
  if ( crc_check()                              // 检测函数是否被修改,也包含是否被软断点
    && (srclen = strlen(src), srclen <= 256)
    && (ipt[0] = 0, memset(&ipt[1], 0, 255u), 
	  v4 = base64_decode(src, srclen, ipt), strlen(ipt) == 90) // 这个strlen(ipt) == 90 要注意
                                                               // 代表整个 ipt 中不能有 00
    && ipt[20] == '-'                           // 序列号分隔符
    && ipt[21] == '-'
    && ipt[61] == '-'
    && ipt[62] == '-' )
  {
    if ( !crc_check() )
      ExitProcess(1u);
    thread_flag = 1;
    while ( thread_flag )
      ;
    if ( check_part1(ipt) )                     // 检验第一部分 ipt[:20]
    {
      thread_flag = 1;
      while ( thread_flag )
        ;
      if ( !crc_check() )
        ExitProcess(1u);
      thread_flag = 2;
      while ( thread_flag )
        ;
      if ( check_part2(ipt) )                   // 检验第二部分 ipt[22:61]
      {
        thread_flag = 2;
        while ( thread_flag )
          ;
        if ( !crc_check() )
          ExitProcess(1u);
        thread_flag = 3;
        while ( thread_flag )
          ;
        if ( check_part3(ipt) )                 // 检验第三部分 ipt[63:90]
        {
          thread_flag = 3;
          while ( thread_flag )
            ;
          if ( !crc_check() )
            ExitProcess(1u);
          result = 1;
        }
        else
        {
          result = 0;
        }
      }
      else
      {
        result = 0;
      }
    }
    else
    {
      result = 0;
    }
  }
  else
  {
    result = 0;
  }
  return result;
}
signed int  check_part1(_BYTE *a1)
{
  __asm { fcmovnbe st, st(2) }
  v1 = __rdtsc();
  v10 = (unsigned int)v1 + ((unsigned __int64)HIDWORD(v1) << 32);
  v2 = a1;
  v3 = &part1_bn[1] - a1;
  v9 = 4;
  while ( 1 )
  {
    v4 = *v2 ^ v2["abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" - a1];
    v2 += 5;
    v2[part1_bn - a1 - 5] = v4;
    v2[v3 - 5] = *(v2 - 4) ^ v2[0x405145 - (_DWORD)a1 - 5];
    v2[&part1_bn[2] - a1 - 5] = *(v2 - 3) ^ v2[0x405146 - (_DWORD)a1 - 5];
    v2[&part1_bn[3] - a1 - 5] = *(v2 - 2) ^ v2[0x405147 - (_DWORD)a1 - 5];
    v5 = v9-- == 1;
    v2[&part1_bn[4] - a1 - 5] = *(v2 - 1) ^ v2[0x405148 - (_DWORD)a1 - 5];
    if ( v5 )
      break;
    v3 = &part1_bn[1] - a1;
  }                                             // 拿ipt[:20] 与 abcdef...z 进行按字节异或存 part1_bn
  v6 = big_mul(part1_bn, key0);                 // key0 = 98765432109876543210123
  v8 = __rdtsc();                               // key1 = 1549780652036258484424751705102781884386113
  if ( !strcmp(v6, key1) && (unsigned int)v8 + ((unsigned __int64)HIDWORD(v8) << 32) - v10 < 8888888 )
  {
    operator delete(v6);
    result = 1;
  }
  else
  {
    operator delete(v6);
    result = 0;
  }
  return result;
}
signed int check_part2(_BYTE *a1)
{
  __asm { fcmovnbe st, st(2) }
  v1 = __rdtsc();
  v2 = ((unsigned __int64)HIDWORD(v1) << 32) + (unsigned int)v1;
  v3 = 0;
  do
  {
    v4 = aAbcdefghijklmn[v3] ^ a1[v3 + 22];     // abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
    v3 += 3;
    byte_99B705[v3] = v4;
    byte_99B706[v3] = *(_BYTE *)(v3 + 0x405142) ^ a1[v3 + 20];
    byte_99B707[v3] = *(_BYTE *)(v3 + 0x405143) ^ a1[v3 + 21];
  }
  while ( v3 < 39 );
  v5 = big_mul(part2_bn, part2_bn);
  big_mul_fft(v5, key2);                        // 这里是个误导,其实没有用他的结果
  if ( strcmp(v5, a13095069099216) )            // 13095069099216326605010245808779535277211541324456558063162414338128147458401
  {
    operator delete(v5);
    return 0;
  }
  operator delete(v5);
  v7 = __rdtsc();
  if ( (unsigned int)v7 + ((unsigned __int64)HIDWORD(v7) << 32) - v2 >= 0x14B230CE38i64 )
    return 0;
  return 1;
}
int check_part3(_BYTE *a1)
{
  v1 = __rdtsc();
  v23 = (unsigned int)v1 + ((unsigned __int64)HIDWORD(v1) << 32);
  bi_new(bi1);
  dst[0] = 0;
  memset(&dst[1], 0, 55u);
  v2 = a1 + 63;
  v3 = &dst[1];
  v4 = 27;
  do
  {
    v5 = *v2;
    *(v3 - 1) = *v2 / 16;
    *v3 = v5 % 16;
    ++v2;
    v3 += 2;
    --v4;
  }
  while ( v4 );
  v6 = 0;
  do
  {
    v7 = dst[v6];
    if ( v7 < 0 || v7 > 9 )
      v8 = v7 + 0x37;
    else
      v8 = v7 + '0';
    dst[v6] = v8;
    v9 = dst[v6 + 1];
    if ( v9 < 0 || v9 > 9 )
      v10 = v9 + 0x37;
    else
      v10 = v9 + '0';
    dst[v6 + 1] = v10;
    v11 = dst[v6 + 2];
    if ( v11 < 0 || v11 > 9 )
      v12 = v11 + 0x37;
    else
      v12 = v11 + 48;
    dst[v6 + 2] = v12;
    v13 = dst[v6 + 3];
    if ( v13 < 0 || v13 > 9 )
      v14 = v13 + 55;
    else
      v14 = v13 + 48;
    dst[v6 + 3] = v14;
    v15 = dst[v6 + 4];
    if ( v15 < 0 || v15 > 9 )
      v16 = v15 + 0x37;
    else
      v16 = v15 + 48;
    dst[v6 + 4] = v16;
    v17 = dst[v6 + 5];
    if ( v17 < 0 || v17 > 9 )
      v18 = v17 + 55;
    else
      v18 = v17 + '0';
    dst[v6 + 5] = v18;
    v6 += 6;
  }
  while ( v6 < 54 );                            // dst = 输入的 hex 编码
  bi_set(bi1, dst, 16);
  bi2_p = bi_new(bi2);
  bi_set(bi2_p, part1_bn, 10);                  // part1_bn = 15691529100101820131
  bi3_p = bi_new(bi3);
  bi_set(bi3_p, part2_bn, 10);                  // part2_bn = 114433688655117320765854989491151409201
  powmod(bi1, bi2, (int)&a2, bi3);
  result = 0;
  if ( v25 == 4 )
  {
    while ( v26[result] == byte_4064E0[result] )// 71639176673360967005214790689576394595
    {
      ++result;
      if ( result >= 4 )
      {
        v22 = __rdtsc();
        if ( (unsigned int)v22 + ((unsigned __int64)HIDWORD(v22) << 32) - v23 < 88888888 )
          return 1;
        break;
      }
    }
    result = 0;
  }
  return result * 4;
}
import gmpy2
import base64
key0 = 98765432109876543210123
key1 = 1549780652036258484424751705102781884386113
key2 = 13095069099216326605010245808779535277211541324456558063162414338128147458401
key3 = long("35e5341c28494bcc881e49a704971f63", 16) #71639176673360967005214790689576394595
xordt = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
part1_bi = 0
part2_bi = 0
def decode_part1():
    global part1_bi
    part1_bi = key1 / key0
    partstr = bytearray(str(part1_bi))
    for i in xrange(len(partstr)):
        partstr[i] ^= ord(xordt[i])
    return partstr
def decode_part2():
    global part2_bi
    part2_bi = long(gmpy2.isqrt(key2))
    partstr = bytearray(str(part2_bi))
    for i in xrange(len(partstr)):
        partstr[i] ^= ord(xordt[i])
    return partstr
# 先求出两部分大数
decode_part1()
decode_part2()
g = 55986991232018409201158808992848352475
m = part2_bi #"2a1eb3c9579dfa307cf5b6c8730114db"
k = long("0x"+"6"*54, 16) / m
g = g + (k-1) * m #找个能填充满 27字节,前面都是 66的最近的g
def checkok(s):
    ttt = hex(s)[2:].rstrip("L").decode("hex")
    assert len(ttt) == 27
    for xxx in ttt:
        # 不是0,并且小于 0x80
        if (ord(xxx) == 0) or ((ord(xxx) & 0x80) != 0):
            return False
    return True
while True:
    if checkok(g):
        break
    # 向前寻找
    g -= m
fake = decode_part1() + "--" + decode_part2() + "--" + hex(g)[2:].rstrip("L").decode("hex")
assert len(fake) == 90
print base64.b64encode(fake)
  • 准备 虚拟机代码段(code)
  • 准备 虚拟机数据段(data)
  • 准备 虚拟机上下文(ctx)
  • 运行虚拟机(0x403540,eax=code, esi=ctx, arg0=data)
  • 先将 ipt[:20] 与 a..z 异或得到 part1_bn
  • big_mul 是简易大数乘法,输入是两个大数字符串,输出大数字符串
  • 检测 part1_bn * 98765432109876543210123 == 1549780652036258484424751705102781884386113
  1. 拖入 IDA,发现有3个 TLS 回调,其中有1个回调启动了6个线程,但只有3个线程是解密函数,都类似如下代码
  1. 由于 4068E4 存的是指针,查找引用找到一个函数(现在还不知道是啥),但是能看到这个指针指向了这个函数段里的内
    int sub_402450(const char *a1)
    {
      dword_4068E4 = &loc_4025DC;
      dword_4068E0 = &loc_40263C;
      dword_4068EC = &loc_40269C;
      if ( (unsigned __int8)((int (*)(void))sub_4027F0)() )
      {
        v8 = strlen(a1);
        if ( v8 <= 256 )
        {
          v2 = 0;
          memset(&Dst, 0, 0xFFu);
          v9 = sub_401D50(&v2);
          if ( strlen(&v2) == 90 && v4 == 45 && v5 == 45 && v6 == 45 && v7 == 45 )
          {
            if ( !(unsigned __int8)sub_4027F0(90) )
              ExitProcess(1u);
            dword_99B6EC = 1;
            while ( dword_99B6EC )
              ;
          }
        }
      }
      return 0;
    }
  1. 为了让大家看到代码地址,特意将命名去除了,其他两个线程和这个差不多,只是解密的函数地址不一样,写个脚本还原代码
    import idaapi
    tmp_point = {0x4068E4:0x4025DC, 0x4068E0:0x40263C, 0x4068EC:0x40269C}
    def dec_func(func_addr, key_addr, tmp_addr, tmp):
        func = bytearray(idaapi.get_many_bytes(func_addr, 0x2E))
        key = bytearray(idaapi.get_many_bytes(key_addr, 0x2E))
        for i in xrange(len(func)):
            func[i] ^= key[i]
        idaapi.patch_many_bytes(func_addr, str(func))
        idaapi.patch_many_bytes(tmp_point[tmp_addr], str(bytearray(tmp)))
    dec_func(0x401EC0, 0x4064F0, 0x4068E4, [0x8B, 0xC0, 0x8B, 0xFF, 0x8B, 0xDB])
    dec_func(0x402090, 0x406520, 0x4068E0, [0x8B, 0xC0, 0x8B, 0xDB, 0x8B, 0xFF])
    dec_func(0x4021D0, 0x406550, 0x4068EC, [0x8B, 0xFF, 0x8B, 0xC0, 0x8B, 0xDB])

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 2
支持
分享
最新回复 (7)
雪    币: 3508
活跃值: (4634)
能力值: ( LV13,RANK:437 )
在线值:
发帖
回帖
粉丝
2
被数学击败了,:-(
2017-6-17 18:38
0
雪    币: 1711
活跃值: (516)
能力值: ( LV12,RANK:200 )
在线值:
发帖
回帖
粉丝
3
被数学击败了+1
2017-6-18 10:57
0
雪    币: 144
活跃值: (178)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
很详细,期待类似的文章
2017-6-18 11:50
0
雪    币: 107
活跃值: (1693)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
5
赞,第3部分也想到过是RSA,无奈用常量数字验证第3部分的时候,死活不满足m^e%n,遂放弃。
2017-6-18 20:41
0
雪    币: 16506
活跃值: (6392)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
6
不知道大佬,最后一张图由2到3是由欧拉定理怎么变形得到的?能否说下。我没找到相关的资料,我也是用欧拉定理硬推的!不过感觉好繁琐啊!
感觉你的方法变形较为简单吧。
2018-2-10 15:24
0
雪    币: 3502
活跃值: (1493)
能力值: ( LV15,RANK:1057 )
在线值:
发帖
回帖
粉丝
7
大帅锅 不知道大佬,最后一张图由2到3是由欧拉定理怎么变形得到的?能否说下。我没找到相关的资料,我也是用欧拉定理硬推的!不过感觉好繁琐啊!感觉你的方法变形较为简单吧。
我没有省略步骤啊,是哪里没懂
2018-9-28 16:32
0
雪    币: 204
活跃值: (911)
能力值: (RANK:1324 )
在线值:
发帖
回帖
粉丝
8
看错了123
最后于 2019-9-8 10:08 被mratlatsn编辑 ,原因:
2019-9-6 10:28
0
游客
登录 | 注册 方可回帖
返回
//