下载地址:http://www.crackmes.de/users/freesoul/serialme_2
使用工具:OllyDbg
说明:
1.找关键算法的方法:先找WinMain启动点――注册窗口类的代码中找
到窗口过程地址(并且顺便记录下控件ID,因为这个窗口的控件是用
CreateWindowEx创建的)――窗口过程中找到WM_COMMAND(111h)分支
――找到“注册”按钮按下的事件分支
2.不打算贴那种一大片的OD反汇编内容了,如果想了解我的分析过程,
将下面的内容复制后存为文本文件,在OD载入目标程序后,选择“插件
――OllyScript――Run Script”将该文本文件打开就会运行程序,在按
照弹出的消息框的要求,输入UserName及serial以后,就会中断在关键
call上。
// ==============以下=================
var x
mov x, "如果不是,置失败标志并返回"
lbl 401290, "WinMain"
lbl 402570, "__Znaj"
lbl 401C9E, "_CheckSerial"
lbl 4024D8, "redir.SetDlgItemTextA"
lbl 40805D, "szSuccess"
lbl 408067, "szFail"
lbl 4080FC, "szTable1"
lbl 408130, "szTable3"
lbl 40813B, "szTable2"
lbl 401B9E, "_badboy1"
lbl 401BB6, "_badboy2"
lbl 4024C8, "_CheckSerial.locret"
cmt 401578, "68=序号输入框ID"
cmt 401510, "67=用户名输入框ID"
cmt 4016B0, "69=注册按钮ID"
cmt 401A8B, "为存储用户名开辟空间"
cmt 401A9A, "为存储序列号开辟空间"
cmt 401AC3, "[ebp-60] == 用户名长度"
cmt 401AE7, "[ebp-64] == 序列号长度"
cmt 401AEE, "用户名至少4字符"
cmt 401AF8, "序列号必须等于27字符"
cmt 401B1E, "[ebp-14] == 用户名的指针"
cmt 401B50, "[ebp-5C] == 序列号的指针"
cmt 401B65, "参数4:序列号的长度"
cmt 401B6C, "参数3:用户名的长度"
cmt 401B73, "参数2:序列号的指针"
cmt 401B7A, "参数1:用户名的指针"
cmt 401B82, "返回值:0表示成功,非0表示失败"
cmt 401B84, "关键跳转"
cmt 401CF9, "[ebp-70]用作局部计数器"
cmt 401D0E, "检测用户名各字符是否为字母或数字"
cmt 401D1D, x
cmt 401D31, "用户名各字符只能为字母或数字"
cmt 401D36, "检测序列号第一个字符是否为'N'"
cmt 401D3B, x
cmt 401D4E, "检测序列号第二个字符是否为'a'"
cmt 401D60, "检测序列号第三个字符是否为'R'"
cmt 401D65, x
cmt 401D7A, "检测用户名第四个字符是否为'F'"
cmt 401D7F, x
cmt 401D95, "[ebp-78]用作局部计数器"
cmt 401DB7, "求用户名各字符的和"
cmt 401DC2, "并乘以96"
cmt 401DC5, "存入[ebp-80]"
cmt 401DCF, "开辟31字节空间"
cmt 401DEC, "将[ebp-80]中的结果作为十进制数转换为字符串"
cmt 401E05, "测此字符串长存入[ebp-88]"
cmt 401E0B, "[ebp-74]用做局部计数器"
cmt 401E2F, "从串尾依次取字符(第一次取的字符是结尾的0)"
cmt 401E33, "转换数字字符为对应的数值"
cmt 401E36, "并乘以对应的计数值"
cmt 401E3B, "加1后存入[ebp-8C]"
cmt 401E50, "非首轮循环,则取szTable1的第([ebp-8C]+1)个字符"
cmt 401E64, "是首轮循环"
cmt 401E6C, "则取szTable1的第(54-用户名长)个字符"
cmt 401E76, "若[ebp-8C]大于50"
cmt 401E79, "则取szTable的第(用户名长+1)个字符"
cmt 401E88, "比较序列号的第(5+[ebp-74])个字符是否等于该字符"
cmt 401E96, x
cmt 401EA5, "计数器递增"
cmt 401EE5, "相当于用户名长度被4除的余数存入[ebp-78]"
cmt 401EF3, "取[ebp-68]开始的字符串(szTable2)中第[ebp-78]个字符"
cmt 401EF9, "看序列号的第10个字符是否等于此字符"
cmt 401EFD, x
cmt 401F13, "取用户名的末字符"
cmt 401F16, "若是字母则转成大写字母"
cmt 401F3B, "若该字符不大于'M'则加2"
cmt 401F45, "若该字母大于'M'则减1"
cmt 401F4A, "看用户名的第11个字符是否等于该字符"
cmt 401F57, x
cmt 401F66, "[ebp-8C]用做局部计数器"
cmt 401F77, "此循环中处理序列号的第12至17个字符"
cmt 401FCD, "序列号第12至17个字符应为大写字母"
cmt 401FE5, "将用户名首字符转为大写存入[ebp-94]"
cmt 401FFD, "将用户名末字符转为大写后存入[ebp-98]"
cmt 40200D, "[ebp-8C]用做局部计数器"
cmt 40201A, "看用户名末字符是否大于'M'而定"
cmt 40202F, "<==ecx中是序列号的第(11+[ebp-8C])个字符"
cmt 402038, "不大于'M'时,用它加上计数的值"
cmt 40206B, "若大于'M',则减去计数的值"
cmt 402078, "比较这两字符是否相等"
cmt 40207C, x
cmt 40208B, "看用户名首字符是否大于'M'"
cmt 40209F, "<==ecx中是序列号的第(18-[ebp-8C])个字符"
cmt 4020AF, "如不大于'M'则加上计数的值"
cmt 4020E2, "否则减去计数的值"
cmt 4020E9, "比较这两字符是否相等"
cmt 4020ED, x
cmt 402109, "[ebp-8C]用做局部计数器"
cmt 402128, "检测序列号的第18至23个字符是否为数字"
cmt 402137, x
cmt 40215A, "[ebp-8C]用做局部计数器"
cmt 40217D, "用户名的每个字符乘以其索引(序数-1)"
cmt 40218A, "然后累加至[ebp-9C]"
cmt 40219C, "此和上加上100000"
cmt 4021AB, "并减去用户名长"
cmt 4021B4, "开辟25字节空间"
cmt 4021D1, "将[ebp-9C]中得到的数值转换成十进制字符串"
cmt 4021DF, "[ebp-8C]用做局部计数器"
cmt 402210, "此循环检查序列号第18至23个字符的子串是否等于刚才得到的字符串"
cmt 402214, x
cmt 402233, "检查序列号第24字符是否为连字符'-'"
cmt 402238, "如果不是,置另一失败标志"
cmt 40226F, "求序列号中第18至23字符的和"
cmt 402271, "此时存放的数值仍是用户名各字符之和乘96"
cmt 402285, "开辟20字节空间"
cmt 4022A2, "把上述子串的和转换成十进制字符串"
cmt 4022BE, "并在[ebp-AC]中存放转化后的位数"
cmt 4022D6, "看序列号第25个字符是否等于szTable3以上述和的首位数字为索引找到的字符"
cmt 4022DF, x
cmt 4022F2, "取用户名第二个字符"
cmt 402322, "case 'A'"
cmt 402331, "case 'B'"
cmt 402340, "case 'C'"
cmt 40234F, "case 'D'"
cmt 40235E, "case 'E'"
cmt 40236D, "case 'F'"
cmt 40237C, "case 'L'"
cmt 40238B, "case 'M'"
cmt 40239A, "case 'N'"
cmt 4023A9, "case 'O'"
cmt 4023B8, "case 'P'"
cmt 4023C7, "case 'Q'"
cmt 4023D3, "case 'R'"
cmt 4023DF, "case 'S'"
cmt 4023EB, "case 'T'"
cmt 4023F7, "case 'U'"
cmt 402403, "case 'V'"
cmt 40240F, "case 'W'"
cmt 40241B, "case 'X'"
cmt 402427, "case 'Y'"
cmt 402433, "case 'Z'"
cmt 40243F, "case 'G','H','I','J','K',digit"
cmt 40244F, "加'A'"
cmt 402468, "再加上上面子串最后一个数字的值"
cmt 402477, "看序列号的第26个字符是否等于该字符"
cmt 402481, "如不等则置另一失败标志"
cmt 40248E, "序列号末字符(第27字符)应该为'D'"
cmt 40249A, "序列号总长应为27字符(前面已有涉及)"
bp 401B7D
MSG "请输入用户名(4到50字符)及序列号(27字符)"
run
// ==============以上=================
算法总结:
UserName至少4字符长,只允许出现字母或数字字符。serial为27字符
算法类型:serial = f (UserName)
例:假设UserName为"PEDIY",计算serial
第1至第4个字符:
固定的,必须为"NaRF"
第5至第9个字符:
预先给定一个串
szTable1 = "Z00YWABKKD28LHMFNOP05Q4SI37TWRUE8VVUGUL9AB9AZHGZZC0"
serial的第5个字符等于szTable1中索引等于53减去UserName长度的那个字符
求UserName串各字符的和,并乘以96,此数值记为sum80。
将sum80转换成十进制字符串,结果姑且记为szSum80。
循环:从i=1到i=4(注:本来是i=0到i=4,i=0时算出序列号的第5个字符
是固定的,所以分离到上面一段了)
取szSum80的倒数第i个字符,用该字符代表的十进数码值
(即该字符减0x30)乘以i再加1,结果姑且记为temp1。看temp1
是否大于50(事实上temp1最大是9*4+1=37,所以不会发生这种
情况)。取字符szTable1[temp1]作为serial的第(5+i)个字符
循环尾
例如对于"PEDIY",长为5
serial的第5个字符等于szTable1[53-5]='Z'
('P'+'E'+'D'+'I'+'Y') * 96 = 36384
个位数是4,4*1+1=5,serial的第6个字符=szTable1[5]='A'
十位数是8,8*2+1=17,serial的第7个字符=szTable1[17]='O'
百位数是3,3*3+1=10,serial的第8个字符=szTable1[10]='2'
千位数是6,6*4+1=25,serial的第9个字符=szTable1[25]='3'
第10个字符:
预先给定一个串
szTable2 = "XYPO"
以UserName长度被4除的余数作为索引,在szTable2中找到对应的字符,作
为serial的第10个字符。
例如"PEDIY"长度5被4除余1,serial的第10个字符=szTable[1]='Y'
第11个字符:
取UserName的末字符,如果是字母则转成大写,然后看该字符是否大于
'M',如果大于则减去1,否则加2。最后结果作为serial的第11个字符
例如"PEDIY"中'Y'>'M',serial的第11个字符就是'Y'-1='X'
第12至14个字符:
循环:i=1到i=3
取UserName的末字符,如果是字母则转成大写,然后看该字符是否
大于'M',如果大于则减去i,否则加i。最后结果作为serial的第(11+i)
个字符
循环尾
如"PEDIY"中'Y'>'M',则serial的第12到14个字符依次为'Y'-1='X',
'Y'-2='W', 'Y'-3='V'
第15至17个字符:
循环:i=1到i=3
取UserName的首字符,如果是字母则转成大写,然后看该字符是否
大于'M',如果大于则减去i,否则加i。最后结果作为serial的第(18-i)
个字符
循环尾
如"PEDIY"中'P'>'M',则serial的第15到17个字符依次为'P'-3='M',
'P'-2='N','P'-1='O'
第18至23个字符:
用UserName的每个字符乘以其索引(即该字符在UserName中的序数减去
一),累加求和,此和再加上100000,然后减去UserName的长度,最后所得
结果记为temp2。由于UserName最长51字符,累加的和不会超过6位十进制,
而加上100000确保temp2正好是6位十进制。将temp2转换为十进制字符串得
到6个字符(不含结尾0),这6个字符就依次作为serial的第18至23个字符
例如对于"PEDIY"有:
'P' * 0 + 'E' * 1 + 'D' * 2 + 'I' * 3 + 'Y' * 4 = 780
780 + 100000 - 5 = 100775
因此serial的第18至23个字符就等于"100775"
第24个字符:
固定的,必须为'-'(连字符)
第25个字符:
给定一个串
szTable3 = "ACKBWNKKHD"
此外,把上面求得的serial第18至23个字符求和,姑且记为sumA8。以
此和的最高十进制位为索引,在szTable3中找到对应的字符,即为serial
的第25个字符。(由于serial第18至23个字符全为十进制数字,其ASCII码
在48至57之间。因此sumA8在288至342之间,其最高位数字非2即3)
例:'1'+'0'+'0'+'7'+'7'+'5'=308,
serial第25个字符等于szTable[3]='B'
第26个字符:
首先给定一个数组
dwIndex[26]={8,7,6,5,4,3,3,3,3,3,3,5,6,7,8,7,6,0,0,5,4,3,2,1,2,0}
取用户名第二个字符(如果是字母则转大写)减去'A',如果结果在0
至25之间,则以该值为索引在上面的数组中找到对应的项,将所得的值记
为temp3。在其他情况下,令temp3 = 3
将temp3加上'A',再加上求第25个字符时得到的sumA8的个位数字,所
得结果作为serial的第26个字符
例:'E'-'A'=4,dwIndex[4]=4,serial第26个字符等于4+'A'+8='M'
第27个字符:
固定的,必须为'D'
于是对"PEDIY"得出对应serial:"NaRFZAO23YXXWVMNO100775-BMD"
已放上原Crackme压缩包,内含我做的注册机源码
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)