0x01 程序整体流程
程序整体流程: 双击运行先弹出该框:
点击确定后出现:
点击Serial/Name:
随便输入:
0x02 定位与爆破
查壳:
载入IDA,查看IDA Signature:
查看字符串:
直接就可以定位到关键位置:
图中可以看到第一个if不满足的时候会跳转到LABEL_4,然后第二个if不满足也会跳到LABEL_4,注册失败的位置,所以这里应该是需要修改两处地方
if ( sub_406930(dword_43176C) < 4 )
goto LABEL_4;
载入OD,依旧通过字符串定位到关键位置:
跟踪找到关键跳:
第一个JCC指令jge,大于等于4实现跳转,这里直接改成jmp即可,
第二个JCC指令jnz,结果不为0则跳转,这里不为0指的是标志寄存器中的ZF标志位,这里要让跳转不实现。然后需要看一下这个位置是做了什么,前面是调用了一个函数,该函数有两个参数,edx和eax,观察寄存器,可以发现为输入的Serial和真正的Serial进行比较:
那么这里实现爆破的话直接就将jnz这个判断nop掉就可以了,无论判断结果如何都会提示Good Job,最终得到:
到这里简单分析和爆破就已经完成了,接下来就是深一步挖掘这个程序是如何生成序列号进行校验的了
0x03 序列号生成算法分析
经过测试,对于输入的Name值不同,会生成不同的Serial。
回到IDA,看到第一个if,判断是否小于4
那么这里4应该就是输入的某串值的长度了,这里是传入了dword_43176C,往上看可以看到该值通过sub_403AB0返回,该函数传入v15的指针,然后v15又通过sub_41AA58处理过,回到OD中,可以看到local.4作为参数传入了sub_0041AA58中,最终local.4的值就是输入的Name所存储的地址,所以这里总的来说就是在间接寻址,最终取到Name的值,并调用strlen获取长度,和4进行比较,大于等于4就通过,OK,那么就可以得到Name为任意大于4的字符串即可。
后面的这些是对Name进行各种计算,但是最终结果与序列号生成无关,这里不展开分析:
继续往下,通过测试可以发现,当输入不同值的时候,序列号的生成只有中间的部分会变,并且值始终为4位的数字,序列号的形式为:CW-XXXX-CRACKED
。
看回IDA,这里v15为byte类型,那么取值得到的就是第一个字符,往上找可以发现dword_431750 = 0x29
这里最终dword_431750 = 0x29 x *v15 x 2
,随后将该值放入sub_406718
中进行处理,然后生成的值放入sub_4039AC中拼接,得到的值给到v16,最后与输入的Serial进行比较:
很明显,关键部分就是sub_406718,回到OD跟进该函数:
这里调用的函数较多,全部下断点,然后观察寄存器,堆栈的变化:
最终发现在调用完sub_00406BF4之后得到了处理后的数值:
此时我输入的Name为1111
,取第一个,ASCII码为31
,乘0x29
再乘0x02
最终得到0FB2
,转化为10进制就是4018
,对于Name
为1122
or1133
最终得到的结果都是4018
,真正在计算的时候只取第一个字符的ASCII码
至此关于该程序的算法计算部分就已经分析完成。
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_())
最终:
0x05 总结
作为第一个CrackMe,很简单了,但是还是分析了很久,最开始并没有想到这里生成的值其实就是处理后的10进制的数,当时分析的时候对于整个sub_406BF4都进行了分析,最终灵光一闪,这不就是python中的str函数吗,将生成的值转成10进制后再通过str转成字符串,最终拼接得到Serial。
Comments NOTHING