CrackMe-001

发布于 2023-04-12  36 次阅读


0x01 程序整体流程

程序整体流程: 双击运行先弹出该框:

image20210129133921188.png

点击确定后出现:

image20210129133941345.png

点击Serial/Name:

image20210129134007112.png

随便输入:

image20210129134031418.png

0x02 定位与爆破

查壳:

image20210129135014479.png

载入IDA,查看IDA Signature:

image20210129154852532.png

查看字符串:

image20210130221529547.png

直接就可以定位到关键位置:

image20210130221602179.png

图中可以看到第一个if不满足的时候会跳转到LABEL_4,然后第二个if不满足也会跳到LABEL_4,注册失败的位置,所以这里应该是需要修改两处地方

if ( sub_406930(dword_43176C) < 4 )
   goto LABEL_4;

载入OD,依旧通过字符串定位到关键位置:

image20210129140101325.png

跟踪找到关键跳:

image20210130221921461.png
image20210130221958462.png

第一个JCC指令jge,大于等于4实现跳转,这里直接改成jmp即可,

第二个JCC指令jnz,结果不为0则跳转,这里不为0指的是标志寄存器中的ZF标志位,这里要让跳转不实现。然后需要看一下这个位置是做了什么,前面是调用了一个函数,该函数有两个参数,edx和eax,观察寄存器,可以发现为输入的Serial和真正的Serial进行比较:

image20210130222656898.png

那么这里实现爆破的话直接就将jnz这个判断nop掉就可以了,无论判断结果如何都会提示Good Job,最终得到:

image20210130222830874.png

到这里简单分析和爆破就已经完成了,接下来就是深一步挖掘这个程序是如何生成序列号进行校验的了

0x03 序列号生成算法分析

经过测试,对于输入的Name值不同,会生成不同的Serial。

回到IDA,看到第一个if,判断是否小于4

image20210130223802004.png
image20210130223746764.png

那么这里4应该就是输入的某串值的长度了,这里是传入了dword_43176C,往上看可以看到该值通过sub_403AB0返回,该函数传入v15的指针,然后v15又通过sub_41AA58处理过,回到OD中,可以看到local.4作为参数传入了sub_0041AA58中,最终local.4的值就是输入的Name所存储的地址,所以这里总的来说就是在间接寻址,最终取到Name的值,并调用strlen获取长度,和4进行比较,大于等于4就通过,OK,那么就可以得到Name为任意大于4的字符串即可。

image20210130224412268.png

后面的这些是对Name进行各种计算,但是最终结果与序列号生成无关,这里不展开分析:

image20210130225038940.png

继续往下,通过测试可以发现,当输入不同值的时候,序列号的生成只有中间的部分会变,并且值始终为4位的数字,序列号的形式为:CW-XXXX-CRACKED

看回IDA,这里v15为byte类型,那么取值得到的就是第一个字符,往上找可以发现dword_431750 = 0x29

这里最终dword_431750 = 0x29 x *v15 x 2,随后将该值放入sub_406718中进行处理,然后生成的值放入sub_4039AC中拼接,得到的值给到v16,最后与输入的Serial进行比较:

image20210130230942521.png

很明显,关键部分就是sub_406718,回到OD跟进该函数:

image20210130231055362.png
image20210130231125475.png

这里调用的函数较多,全部下断点,然后观察寄存器,堆栈的变化:

image20210130231429221.png

最终发现在调用完sub_00406BF4之后得到了处理后的数值:

image20210130231833701.png

此时我输入的Name为1111,取第一个,ASCII码为31,乘0x29再乘0x02最终得到0FB2,转化为10进制就是4018,对于Name1122or1133最终得到的结果都是4018,真正在计算的时候只取第一个字符的ASCII码

image20210130232101807.png

至此关于该程序的算法计算部分就已经分析完成。

0x04 注册机编写

语言Python,图形化界面Qt5:

算法很简单,Name用python的random联合string随机生成长度为4的字符串即可,Serial则根据生成的Name的第一个字符用上面分析得到的进行处理拼接即可,下面贴上源码:

CrackMe01.py:(该py是通过pyuic5将Qt Designer中生成的ui文件转成py的,省去了编写界面的时间)

# -*- coding: utf-8 -*-

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):

   def setupUi(self, MainWindow):
       MainWindow.setObjectName("MainWindow")
       MainWindow.resize(500, 300)
       MainWindow.setMinimumSize(QtCore.QSize(500, 260))
       MainWindow.setMaximumSize(QtCore.QSize(500, 260))
       font = QtGui.QFont()
       font.setFamily("微软雅黑")
       font.setPointSize(10)
       MainWindow.setFont(font)
       MainWindow.setMouseTracking(False)
       self.centralwidget = QtWidgets.QWidget(MainWindow)
       self.centralwidget.setObjectName("centralwidget")
       self.pushButton = QtWidgets.QPushButton(self.centralwidget)
       self.pushButton.setGeometry(QtCore.QRect(140, 160, 201, 71))
       font = QtGui.QFont()
       font.setFamily("微软雅黑")
       font.setPointSize(10)
       self.pushButton.setFont(font)
       self.pushButton.setObjectName("pushButton")
       self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
       self.textEdit.setGeometry(QtCore.QRect(150, 30, 281, 41))
       self.textEdit.setObjectName("textName")
       self.textEdit_2 = QtWidgets.QTextEdit(self.centralwidget)
       self.textEdit_2.setGeometry(QtCore.QRect(150, 90, 281, 41))
       self.textEdit_2.setObjectName("textSerial")
       self.label = QtWidgets.QLabel(self.centralwidget)
       self.label.setGeometry(QtCore.QRect(51, 44, 81, 21))
       font = QtGui.QFont()
       font.setFamily("Cascadia Code PL")
       font.setPointSize(10)
       self.label.setFont(font)
       self.label.setObjectName("label")
       self.label_2 = QtWidgets.QLabel(self.centralwidget)
       self.label_2.setGeometry(QtCore.QRect(50, 100, 72, 15))
       font = QtGui.QFont()
       font.setFamily("Cascadia Code PL")
       font.setPointSize(10)
       self.label_2.setFont(font)
       self.label_2.setObjectName("label_2")
       MainWindow.setCentralWidget(self.centralwidget)
       self.menubar = QtWidgets.QMenuBar(MainWindow)
       self.menubar.setGeometry(QtCore.QRect(0, 0, 500, 29))
       self.menubar.setObjectName("menubar")
       MainWindow.setMenuBar(self.menubar)
       self.statusbar = QtWidgets.QStatusBar(MainWindow)
       self.statusbar.setObjectName("statusbar")
       MainWindow.setStatusBar(self.statusbar)

       self.retranslateUi(MainWindow)
       QtCore.QMetaObject.connectSlotsByName(MainWindow)

   def retranslateUi(self, MainWindow):
       _translate = QtCore.QCoreApplication.translate
       MainWindow.setWindowTitle(_translate("MainWindow", "CrackeMe01注册机"))
       self.pushButton.setText(_translate("MainWindow", "生成注册码"))
       self.label.setText(_translate("MainWindow", "Name:"))
       self.label_2.setText(_translate("MainWindow", "Serial:"))

main.py:

# -*- coding: utf-8 -*-

import sys
import random
import string
from PyQt5.QtWidgets import QApplication, QMainWindow
from crackMe01 import Ui_MainWindow


class MyMainWindow(QMainWindow, Ui_MainWindow):
   serial_prefix = 'CW-'
   serial_suffix = '-CRACKED'
   name = ''
   serial = ''

   def __init__(self, parent=None):
       super(MyMainWindow, self).__init__(parent)
       self.setupUi(self)
       self.pushButton.clicked.connect(self.generate_name_serial)

   def generate_name(self):
       return ''.join(random.sample(string.ascii_letters + string.digits, 4))

   def generate_serial(self, name):
       return self.serial_prefix + str(ord(name[0]) * 0x29 * 0x02) + self.serial_suffix

   def generate_name_serial(self):
       self.name = self.generate_name()
       self.serial = self.generate_serial(self.name)
       self.textEdit.setText(self.name)
       self.textEdit_2.setText(self.serial)


if __name__ == '__main__':
   app = QApplication(sys.argv)
   myWin = MyMainWindow()
   myWin.show()
   sys.exit(app.exec_())

最终:

image20210130185508238.png

0x05 总结

作为第一个CrackMe,很简单了,但是还是分析了很久,最开始并没有想到这里生成的值其实就是处理后的10进制的数,当时分析的时候对于整个sub_406BF4都进行了分析,最终灵光一闪,这不就是python中的str函数吗,将生成的值转成10进制后再通过str转成字符串,最终拼接得到Serial。