钢琴英雄Synthesia逆向思路分享
0x00:运行软件
如图:打开软件后,需要解锁才可以自由使用全部功能。此处联想到关键字“lock”,“unlock”,“unlocked”。
0x01:动态跟踪
使用x64dbg加载目标程序Synthesia.exe,点击运行按钮,一直来到Synthesia.exe程序模块,如图所示。
0x02:搜索关键字
在反汇编窗口,右击依次选择"搜索"-->"当前模块"-->"字符串",自动跳转到"引用"选项卡,在下方的"搜索"处填写"unlock".
如图所示:找到"Congratulations! Synthesia is unlocked!"。
0x03:分析汇编代码
在上一步中直接双击字符串,会来到反汇编中字符串出现的位置,按"CTRL+K"跳转到上一级调用,按"CTRL+L"跳转到下一级调用。
如图所示:
汇编代码如下:
0080952A | E8 D1FB0600 | call synthesia.879100 | 0080952F | 8B5D 08 | mov ebx,dword ptr ss:[ebp+8] | 00809532 | 8BF0 | mov esi,eax | esi:EntryPoint 00809534 | 8B3D 90F3B500 | mov edi,dword ptr ds:[<&GetDlgItem>] | edi:EntryPoint 0080953A | B8 E4F0B600 | mov eax,synthesia.B6F0E4 | B6F0E4:L"Synthesia is NOT unlocked" 0080953F | 83FE 01 | cmp esi,1 | esi:EntryPoint 00809542 | 8975 FC | mov dword ptr ss:[ebp-4],esi | esi:EntryPoint 00809545 | B9 90F0B600 | mov ecx,synthesia.B6F090 | ecx:EntryPoint, B6F090:L"Congratulations! Synthesia is unlocked!"
关注cmp esi,1这一段,向上撸代码,esi的值来自mov esi,eax即eax,而eax的值来自synthesia.879100这个函数的返回值,由此我们可以得出结论,
如果当synthesia.879100的返回值为1时,程序就会出现"Congratulations! Synthesia is unlocked!"的提示。
0x04:修改汇编代码
按上一步的分析结果,将synthesia.879100这个函数的返回值修改为1,就会解锁程序。
在call synthesia.879100这个位置按回车键即可来到这个函数的主体,如图所示:
设计到返回值部分的汇编代码如下:
0087914B | 803D 3A7DC400 00 | cmp byte ptr ds:[C47D3A],0 | 00879152 | 74 0D | je synthesia.879161 | 00879154 | 837E 08 01 | cmp dword ptr ds:[esi+8],1 | 00879158 | 75 07 | jne synthesia.879161 | 0087915A | B8 01000000 | mov eax,1 | 0087915F | 5E | pop esi | esi:EntryPoint 00879160 | C3 | ret | 00879161 | 33C0 | xor eax,eax | 00879163 | 5E | pop esi | esi:EntryPoint 00879164 | C3 | ret |
这段函数主体有两个返回值,mov eax,1和xor eax,eax 两个,即1或者0.
我修改返回值的方法是将xor eax,eax修改为mov eax,1,即两个返回值都为1.
如图所示:
0x05:编写patch脚本
python的二进制patch方法最为直接暴力,脚本如下:
#!/usr/bin/env python3 # coding=GBK # Created at 2019/11/9 19:52 from __future__ import print_function import ctypes, sys import os,time binpath= r"C:\Program Files (x86)\Synthesia" filename = "Synthesia.exe" backup_path = r"c:\backup" if(os.path.isdir(backup_path)): print("Backup path already exsit!") else: os.mkdir(backup_path) def backfiles(): os.system("copy " + filename + " " + backup_path) print("File has been backuped to " + backup_path) #patch文件的偏移位置。 def patch_exe(): with open (backup_path+"\\" + filename,"rb") as fr: data = fr.read() data = data.replace(b'\x64\xA1\x2C\x00\x00\x00\x56\x8B\xF1', b'\xB8\x01\x00\x00\x00\xC3\x56\x8B\xF1') with open(binpath + '\\' + filename, "wb") as fw: fw.write(data) print(filename + " has been patached successfully!") #获取administrator权限 def is_admin(): try: return ctypes.windll.shell32.IsUserAnAdmin() except: return False if is_admin(): os.chdir(binpath) try: print("-------------First Step--------------\n") backfiles() time.sleep(3) except: print("Error occurred in backup procedure!") try: print("-------------Second Step--------------\n") patch_exe() time.sleep(3) except: print("Error occurred in patch procedure!\n") else: if sys.version_info[0] == 3: ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1) #python3使用的函数及参数 else: ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1) #python2使用的函数及参数