新160个CrackMe042-crackme、043-riijj_cm_20041121、044-tsrh-crackme逆向分析 042无壳根据字符串搜索可以知道该文件夹下需要一个名为key.dat的文件里面包含序列号暴力破解关键跳转给改成jmp之后发现该程序打不开其实已经成功了只是没有成功提示算法分析显示获取了序列号和序列号长度将序列号的前三位都与其长度进行异或之后再将第一位与0x54异或、第二位与0x4D异或、第三位与0x47异或之后该字符串的第四位与第一位进行异或并且取代原来的值第五位与第二位、第六位与第三位分别异或之后取代原来的值接下来进行一个循环将内存地址从405030位置开始存储的数据与之前异或好的序列号的前三位进行循环异或直到异或结果为0xFF,之后将位置为405030、405031、405032位置的数相乘乘积与0x2F8BF4判断是否相等相等则成功因为这里面只用到了文件中的前三个字符所以我们这里只求文件中是三个字符的情况,可以根据暴力搜索成功找到两个正确的序列号复制到key.dat内如果点击程序没有窗口弹出说明字符串正确可以写keygen:n1 [0x54, 0x4D, 0x47] n2 [0x1E, 0xBF, 0xA2] n1[0] n1[0] ^ n2[0] ^ 0x3 n1[1] n1[1] ^ n2[1] ^ 0x3 n1[2] n1[2] ^ n2[2] ^ 0x3 serial for i in range(33, 127): for j in range(33, 127): for k in range(33, 127): val0 i ^ n1[0] val1 j ^ n1[1] val2 k ^ n1[2] if val0 * val1 * val2 0x2A8BF4: serial chr(i) chr(j) chr(k) print(fserial: {serial}) print(fhex: {i:02X} {j:02X} {k:02X})043无壳绕过反反调试在x32dbg中点击运行发现一直在处于异常状态存在反反调试下载专门针对反反调试的插件Releases · x64dbg/ScyllaHide放到目录x96dbg\release\x32\plugins下在x32dbg中设置如图点击运行即可绕过反反调试直接选择VMProtect模式再补齐红框内的发现程序自带的动态链接库已经载入右键转到相应位置的反汇编下断点动态链接库内存储的是真正算法部分下断点运行输入测试用例继续步过分析暴力破解向下查看可以找到一个关键跳转相等则成功将该跳转给nop掉即可获取破解版本保存程序后没办法打开破解版程序这里只演示了源程序在x32dbg中修改关键跳转之后的结果算法分析指令全称作用操作方向rep movsdRepeat Move DoubleWord (4字节)从 esi 指向的地址复制 N 个 4字节 数据到 ediesi → edirep stosdRepeat Store DoubleWord (4字节)向 edi 指向的地址连续写入 N 个 4字节 数据值来自 eaxeax → [edi]stoswStore Word (2字节)向 edi 指向的地址写入 1 个 2字节 数据值来自 axax → [edi]stosbStore Byte (1字节)向 edi 指向的地址写入 1 个 1字节 数据值来自 alal → [edi]repne scasbRepeat Not Equal Scan Byte扫描 edi 指向的字符串查找与 al 不同的字符常用于计算字符串长度比较 [edi] vs al先是初始化获取一个程序自带的字符串之后再获取用户名和序列号并对他们两个算长度之后有四个判断跳转来验证用户名长度是否大于4小于15序列号长度是否大于4小于30之后经过一个循环将用户名的每一位都对0x62取余数作为下标在程序自带的字符串中取出字符拼接到一起全部拼接完成后与输入的序列号进行判断是否相等相等则成功可以写keygen:seedfytugjhkuijonlbpvqmcnxbvzdaeqrwtryetdgfkgphonuivmdbxfanqydexzwztqnkcfkvcpvlbmhotyiufdkdnjxuzyqhfstae usernameinput(Input username:).strip() result for ch in username: resultseed[ord(ch)%0x62] resultseed[(ord(ch)%0x62)1] print(fresult:{result})044无壳有neg直接查找x32dbg中的字符串没有收获找不到相关语句和跳转在调用堆栈中找到相关位置双击跳转到neg附近将关键跳转给nop掉即可获取去neg程序暴力破解直接进行字符串搜索找不到验证算法的核心位置在程序刚开始的位置向下可以找到获取输入的类似判断输入的用户名长度是否合格的位置在此处下断点之后运行分析向下可以看到几个跳转直接运行可以发现前两个不能跳转第三个跳转成功jmp过去程序才显示正确所以前两个给nop掉第三个给改成jmp即可暴力破解算法分析序列号前面必须为tsrh-2008-只有经过第一个函数函数内部进行一个循环将用户名的每一个字符都经过算法(a0xC)^(a0xC-0x11a0xC-len)a代表用户名的每个字符的ASCII码值len代表字符串“tsrh-2008-”的长度每次循环之后都将获得的十六进制数转换成两个字符拼接到字符串之后“tsrh-2008-”len随着字符串程度增加而增加之后将用户名进行循环每个字符的ASCII码值先1之后如果这是第一次循环那用户名的第一位与上次循环得到的字符串的第十三位字符进行异或接下来判断ASCII码值是否处于A-Z之间如果小于A则循环8直到该字符处于A-Z之间如果大于Z则循环-3直到该字符处于A-Z之间将得到的字符拼接到之后形成真正的序列号最后经过一个函数的循环对比判断输入的序列号和真实序列号是否相等相等则成功不相等就失败可以写出keygen:username input(Input username: ).strip() seed_bytes bytearray(btsrh-2008-) length 10 for ch in username: a ord(ch) idx (a 0xC) ^ (a 0xC - 0x11 a 0xC - length) seed_bytes.append(idx) length 2 seed_str seed_bytes.decode(latin-1) hex_str seed_bytes[len(tsrh-2008-):].hex().upper() print(ftsrh-2008-{hex_str}) result for i, ch in enumerate(username): b ord(ch) 1 if i 0: b b ^ ord(hex_str[2]) print(hex_str[2]) while b ord(A): b 8 while b ord(Z): b - 3 result chr(b) print(fserial:{result})前面还有一个函数和异或的运算没有用到和序列号生成算法没关系这里不做分析