solly 发表于 2020-8-24 14:59

一个自制高强度 KeygenMe,高手大佬来试试,1周内第1个完成keygen者奖500CB,不要错过

本帖最后由 solly 于 2020-8-24 15:06 编辑

一个自制高强度 KeygenMe,无壳,无虚,无反,无混,原生vs2017编译,算法流程来自本人以前破解的一个小软件,不过经过了轻量级地魔改,高手大佬们来试试,1周内第1个完成keygen者奖500CB,看看能不能撑过1周时间,大佬们不要错过哦。
{:1_918:}




界面和成功截图:

monvvv 发表于 2020-8-26 15:30

本帖最后由 monvvv 于 2020-8-26 15:34 编辑

直接IDA F5硬上(虽然有些循环被unroll了)费了亿点点时间搞好了。没怎么弄过Win32 App,有一半时间都花在了理解流程上。。。数据流:

```mermaid
sequenceDiagram

participant Group
participant Button
participant Button2

opt LBUTTONDOWN
    Button2 -->> Group: Save username to WindowText
    Note right of Button2: Cook mid_pwd with raw password
    Button2 ->> Button: Call proc
    Note right of Button: Cook raw_data<br>with mid_pwd and username
end

opt LBUTTONUP
    Button ->> Group: WM_COMMAND 0xCE7
    note right of Group: Cook data with raw_data
    note right of Button: Check and remove tail
    Button -->> Group: Save data to WindowText
    Button ->> Group: WM_COMMAND 0xCE5
    note right of Group: Check data and set to label
end```

加密:
from ctypes import c_uint32
from utils import *


def parse_sn(sn):
    v = []
    for p in sn.split('-'):
      b = bytes.fromhex(p)
      assert len(b) == 4
      v.extend(b)
    return v


def cook_raw_sn(sn):
    def f(a): return ((a ^ sn[-2]) + sn[-1]) & 0xFF

    sn = list(sn)
    k = chain(map(f, sn[:22]), sn[-2:])

    return k


def process_mid_sn(sn):
    MAGIC_KEY =

    # TEA-like encrypt
    def process_block(a, b):
      delta = 0x61C88647

      a = c_uint32(a)
      b = c_uint32(b)
      s = c_uint32(0)

      for _ in range(32):
            s.value -= delta
            a.value += (s.value ^ (b.value >> 5)) + \
                MAGIC_KEY + (MAGIC_KEY ^ b.value) + (b.value << 4)
            b.value += (s.value ^ (a.value >> 5)) + \
                MAGIC_KEY + (MAGIC_KEY ^ a.value) + (a.value << 4)

      return a.value, b.value

    t = uint32s_to_bytes(chain(
      *map(lambda ns: process_block(ns, ns), grouper(2, bytes_to_uint32s(sn)))))

    return t


def cook_username(username):
    MAGIC = b'c0DebY SOLLY'
    k = crc(username.encode()) + \
      int.from_bytes(MAGIC, 'little') + \
      int.from_bytes(MAGIC, 'little')

    return k & 0xFFFFFFFF


def mix_sn_and_username(username, sn):
    k = cook_username(username)

    delta = 0x76BDEFDB
    s = c_uint32(0)
    k = c_uint32(k)
    l = []

    for v in bytes_to_uint32s(sn):
      k.value -= delta
      # Reverse bytes
      k.value = int.from_bytes(k.value.to_bytes(4, 'big'), 'little')
      s.value = (s.value ^ v) + k.value

      l.append(s.value)

    return uint32s_to_bytes(l)


def process_data(data):
    MAGIC_KEY = [
      0x65A04939, 0x51B194D7, 0xE288C9D9,
      0x077FBB77, 0xFD05CDF1, 0xA399B665
    ]
    v = for i, v in enumerate(bytes_to_uint32s(data))]

    return uint32s_to_bytes(v)


def check(data):
    data = list(data)
    assert len(data) == 24
    ints = list(bytes_to_uint32s(data))
    if ints[-2] == 0:
      return False

    if ints[-2] ^ ints[-1] != 0xFFFFFFFF:
      return False

    if not bytes(data)[:16].decode('gbk').startswith('成'):
      return False

    return True


if __name__ == "__main__":
    username = '123456'
    sn = '95E11821-1C4A3FFE-7E978C5E-259DFD9B-BAD811D0-D6A75D31'

    raw_sn = parse_sn(sn)
    mid_sn = cook_raw_sn(raw_sn)
    final_sn = process_mid_sn(mid_sn)
    data = mix_sn_and_username(username, final_sn)
    final_data = process_data(data)

    print(check(final_data))

由于最后的检测太宽松导致存在多解。
解密:
from utils import *
from ctypes import c_uint32

TARGET_DATA = list(chain('成功了,好牛逼!'.encode(
    'gbk'), b'\xFF\xFF\xFF\xFF\x00\x00\x00\x00'))


def decode_data(data):
    MAGIC_KEY = [
      0x65A04939, 0x51B194D7, 0xE288C9D9,
      0x077FBB77, 0xFD05CDF1, 0xA399B665
    ]
    v = for i, v in enumerate(bytes_to_uint32s(data))]

    return uint32s_to_bytes(v)


def cook_username(username):
    MAGIC = b'c0DebY SOLLY'
    k = crc(username.encode()) + \
      int.from_bytes(MAGIC, 'little') + \
      int.from_bytes(MAGIC, 'little')

    return k & 0xFFFFFFFF


def extract_sn_from_data(username, data):
    k = cook_username(username)

    delta = 0x76BDEFDB
    s = c_uint32(0)
    k = c_uint32(k)
    l = []

    for v in map(c_uint32, bytes_to_uint32s(data)):
      tmp = v.value
      k.value -= delta

      # Reverse bytes
      k.value = int.from_bytes(k.value.to_bytes(4, 'big'), 'little')
      v.value -= k.value
      v.value ^= s.value

      s.value = tmp

      l.append(v.value)

    return uint32s_to_bytes(l)


def decode_final_sn(sn):
    MAGIC_KEY =

    # TEA-like decrypt
    def process_block(a, b):
      delta = 0x61C88647

      # 0XC6EF3720 is `s`'s final value in encryption
      s = c_uint32(0XC6EF3720)
      a = c_uint32(a)
      b = c_uint32(b)

      for _ in range(32):
            b.value -= (s.value ^ (a.value >> 5)) + \
                MAGIC_KEY + (MAGIC_KEY ^ a.value) + (a.value << 4)
            a.value -= (s.value ^ (b.value >> 5)) + \
                MAGIC_KEY + (MAGIC_KEY ^ b.value) + (b.value << 4)
            s.value += delta

      return a.value, b.value

    # ####
    t = uint32s_to_bytes(chain(
      *map(lambda ns: process_block(ns, ns), grouper(2, bytes_to_uint32s(sn)))))

    return t


def decode_mid_sn(sn):
    sn = list(sn)

    def f(a): return ((a - sn[-1]) & 0xFF) ^ sn[-2]

    k = chain(map(f, sn[:22]), sn[-2:])

    return k


def sn2str(sn):
    def to_hex(it): return bytes(it).hex().upper()

    s = str.join('-', map(to_hex, grouper(4, sn)))
    return s


if __name__ == "__main__":
    username = '52pojie.cn'

    data = decode_data(TARGET_DATA)
    final_sn = extract_sn_from_data(username, data)
    mid_sn = decode_final_sn(final_sn)
    sn = decode_mid_sn(mid_sn)

    print(sn2str(sn))

solly 发表于 2020-8-26 17:01

本帖最后由 solly 于 2020-8-26 23:05 编辑

monvvv 发表于 2020-8-26 15:30
直接IDA F5硬上(虽然有些循环被unroll了)费了亿点点时间搞好了。没怎么弄过Win32 App,有一半时间都花在 ...
python不熟,但看了看应该没有错,是正确的了。

这个 keygenme 主要还是考查对 windows api 及其消息回调机制的掌握,加密是用的原发贴中公开的,没有改动(查特征魔数就可确定哪几个过程,并且方便下断点),还减少了几个过程,只改动了密码。
所以只要理解了我设定的数据流程后,就可以很快解出来。

最后多解是因为没有对后两段的CRC值进行检查了,只要异或值是-1就可以了。

500CB送给你。

补充一下:
1、搞不清流程的,只要在IDA中查看一下,把前十几个函数的入口都下一个断点,就很快可以确定数据流程了。
2、发现直接使用带A和W的API函数对,如的SetXXXA和GetXXXW,可以隐式完成字符串内码转换。

fenginsc 发表于 2020-8-24 20:32

如果是Crackme你这都提供了一组码了,最笨的办法是一个个试你屏蔽的那一段,总能试出来,
但你这个是KeygenMe,那就要研究你的算法了,无壳,无虚,无反,无混那必然就是很恶心的一大串连环算法,各种移位XOR换算,没那精力研究,支持一下,期待有大神来搞定这个。

zb66 发表于 2020-8-24 20:37

技术上比不过 但是我ID绝对秒杀你们 哈哈:keai

monvvv 发表于 2020-8-24 22:21

拿魔数搜出来了:https://www.52pojie.cn/thread-1081400-1-1.html

solly 发表于 2020-8-24 23:24

monvvv 发表于 2020-8-24 22:21
拿魔数搜出来了:https://www.52pojie.cn/thread-1081400-1-1.html

对,就是这个的轻量级魔改,你可以试试。。。。。。

a38758720 发表于 2020-8-25 11:07

本帖最后由 a38758720 于 2020-8-25 11:10 编辑

晚上下载一下试试,刚看了之前lz破解的那个帖子,大佬行为。

木得感情 发表于 2020-8-25 15:40

要不起,下一个

米粒米粒 发表于 2020-8-25 19:32

小白路过支持一下

ccjspi 发表于 2020-8-25 20:53

路过给大佬点个赞

sunyuwa 发表于 2020-8-26 08:09

对于双递归都研究不明白的我,只能看看
页: [1] 2
查看完整版本: 一个自制高强度 KeygenMe,高手大佬来试试,1周内第1个完成keygen者奖500CB,不要错过