使用OllyDbg从零开始Cracking 第五十二章-ASProtect v2.3.04.26脱壳-Part2
第五十二章-ASProtect v2.3.04.26脱壳-Part2上次给大家搞的一个小比赛已经结束了。由于难度比较大,所以我仅仅只收到了提交来的少数几份答案。尽管如此,还是有童鞋脱颖而出了,Hiei童鞋就成功编写出脚本将加了ASProtect壳的UnPackMe的AntiDump修复了。这里需要提一下,很多时候我们的OD加载了OllyAdvanced插件,调试加了ASProtect壳的程序无法正常运行。这是因为OllyAdvanced这款插件中的AntiRDTSC这个功能加载的驱动导致的,AntiRDTSC这个反反调试功能并不兼容所有机器。不知道大家对RDTSC了不了解,很多壳利用该指令检测某段程序执行的时间间隔以此来判断该程序是否正在被调试。但是现在开始流行多核了,已经不宜再用RDTSC指令来测试指令周期和时间了。我看了一下ASProtect的最新版本,已经没有采用RDTSC指令来进行反调试了,很可能是考虑到多核系统的兼容问题,所以我们这里就不必纠结这个问题了。好了,下面我们用上一章中介绍的方法定位到OEP,下面我们来执行下面的这个脚本看看效果。现在我们处于OEP处,这里大家需要注意一下,Hiei童鞋的这个脚本不能在老版本的OllyScript下执行,大家需要使用新版本的OllyScript插件,这里我找到了OllyScript的新版,大家自行下载附件中的ODbgScript添加到OD的插件目录下就行。添加了新版的OllyScript插件以后,我们重启OD,再次来到了OEP处,大家不要忘了关闭break-on execute这个选项(PS:这里并没有使用作者介绍的这个方法来定位OEP,作者介绍的方法已经失灵了,所以不必关闭break-on execute这个选线带来的影响),不然会出问题的。虽然这是OllyScript插件新的版本,但是由于与老版本的名字并不相同,所以OD插件菜单中新老两个插件都显示出来了。我们选择新版的OllyScript,然后定位到Hiei童鞋的脚本。这里脚本启动以后,要求我们输入待修复CALL的地址,这里我的机器该地址是19A0000。(PS:大家机器上这个待修复的地址可能不尽相同,请根据自己机器的地址来输入)地址输入完以后,单击OK,该脚本就开始执行了。稍等片刻。这里我们可以看到之前那些CALL 19A0000的指令已经被修复了,嘿嘿,HIEI童鞋写的脚本很好用,赞一个。接着使用OllyDump对其进行dump(PS:这里我就不按作者的习惯来了,我直接用OllyDump来修复IAT,所以选中了Rebuild Import这一项中Method1)。接下来直接运行dump并修复IAT的程序看看效果。我们可以看到完美运行。下面是Hiei童鞋的脚本,Hiei童鞋加了详细的注释,接下来我来给大家一一做讲解。/* -===================================================================- .::. 本脚本作者:Hiei 目的:修复ASProtect v2.3 SKE AntiDump 目标程序:UnPackMe_ASProtect.2.3.04.26.a.exe. 配置: ODbgScript v1.3x或者更高版本,执行到OEP处,忽略所有异常 日期: 2006年8月5日 - = [备注] = - 感谢:Ricardo Narvaja和Martian的指导 没有上面两位老师的指导,我就不能顺利编写这个脚本). -===================================================================-*/ var var_oepvar var_codebasevar var_codesizevar var_base_asprvar var_base_aipvar var_ini_iatvar var_dirvar var_dir_iatvar var_sigvar var_destvar var_apivar var_count cmp $VERSION,"1.30" // 检查OllyScript插件的版本 jb err_version ask "请输入ASProtect构造CALL的地址:" cmp $RESULT,0 je to_leave mov var_base_aip,$RESULT // 保存用户输入的待修复的地址 mov var_oep,eip // 将OEP保存到变量var_oep中 gmi eip,codebase // 获取主程序代码段的基地址 mov var_codebase,$RESULT // 将代码段的基地址保存到变量var_codebase中 gmi eip,codesize // 获取代码段的大小 mov var_codesize,$RESULT // 将代码段的大小保存到变量var_codesize中 add var_codesize,var_codebase // 计算代码段的结束位置 mov var_ini_iat,460814 // 将IAT的起始地址保存到变量var_ini_iat中 mov var_base_aspr, // 保存我们可得知要调用哪个API函数指令地址所在区段的基地址(该区段由ASProtect创建) add var_base_aspr,3B02E // 在基地址的基础上加上这个常量就可以定位到关键地址了 bphws var_base_aspr,"x" // 对该关键地址设置硬件执行断点 jmp to_lookfor to_lookfor: find var_codebase,#E8????????# // 从代码段基地址处开始查找以机器码E8开头的CALL cmp $RESULT,0 // 如果没有找到,就输出总共找到的CALL个数,并退出脚本 je no_calls mov var_dir,$RESULT // 如果找到的以机器码E8开头的CALL,就把该CALL的地址保存到变量var_dir中 mov var_sig,var_dir // 将找到的以机器码E8开头的CALL的地址保存到变量var_sig中 mov var_dest,var_dir // 将找到的以机器码E8开头的CALL的地址保存到变量var_dest中 add var_sig,5 // 计算该CALL下一条指令的地址并保存到变量var_sig中 inc var_dest // 将指针指向偏移量,并将指向偏移量的指针保存到变量var_dest中 mov var_dest, // 获取操作码E8后的偏移量,并保存到变量var_dest中 add var_dest,var_sig // 计算CALL的目标地址 cmp var_dest,var_base_aip // 判断CALL的目标地址是否与用户输入的地址相同,如果相同则跳转到to_execute标签处进行下一步处理 je to_execute inc var_dir // 如果CALL的目标地址与用户输入的地址不相同,则继续查找待修复CALL的地址 mov var_codebase,var_dir // 更新待查找的地址并保存到var_codebase中 jmp to_lookfor to_execute: // 是用户输入的待修复CALL的目标地址 mov eip,var_dir // 将EIP修改为待修复CALL所在的地址 run // 运行起来eob to_verify // 如果断下来了,就跳转到to_verify标签处 to_verify: cmp eip,var_base_aspr // 判断断下来的地方是不是关键地址(执行到该关键地址处时,此时的EDX中保存了实际要调用API函数的地址) jne unexpected // 如果断下来的地方不是关键地址处,说明发生异常了,直接跳转到unexpected标签处,进行异常处理 mov var_api,edx // 如果断下来的地方是关键地址处,则将EDX寄存器中实际要调用API函数的地址保存到变量var_api中 jmp to_lookfor_api // 跳转到to_lookfor_api标签处,进行下一步的处理 to_lookfor_api: cmp var_ini_iat, 460F28 // 判断是否超出了IAT的范围 je error // 如果超出了IAT的范围,说明并没有在IAT中定位到API函数对应的IAT项 cmp ,var_api // 判断是否是与该API函数地址对应的IAT项 je to_repair // 如果是与该API函数对应的IAT项,则跳转到to_repair标签处 add var_ini_iat,4 // 如果不是与该API函数对应的IAT项,将指向IAT的指针往后移4个字节,便于下一次查找 jmp to_lookfor_api // 跳转到to_lookfor_api标签处继续查找IAT项 to_repair: mov var_dir_iat,var_ini_iat // 将与该API函数对应的IAT项的指针保存到变量var_dir_iat中 ref var_dir // 搜索该待修复的CALL的参考引用处 cmp $RESULT,0 // 判断待修复的CALL的指令有没有参考引用 jne repair_jump // 如果待修复CALL的指令有参考引用,则跳转到repair_jump标签处 eval "Call dword[{var_dir_iat}]" asm var_dir,$RESULT // 将待修复的CALL汇编成CALL DWORD[对应IAT项的指针]的形式 inc var_count // 更新已修复项的计数 inc var_dir mov var_codebase,var_dir mov var_ini_iat,460814 // 将var_ini_iat变量重新设置为IAT的起始位置,以便下次查找 jmp to_lookfor repair_jump: eval "Jmp dword[{var_dir_iat}]" asm var_dir,$RESULT // 将待修复的CALL汇编成JMP DWORD[对应IAT项的指针]的形式 inc var_count inc var_dir mov var_codebase,var_dir mov var_ini_iat,460814 // 将var_ini_iat变量重新设置为IAT的起始位置,以便下次查找 jmp to_lookfor unexpected: msg "发生异常了,是否继续" cmp $RESULT,0 je to_leave run error: eval "错误,请手动修改CALL的地址: {var_dir}h" msg $RESULT run no_calls: bphwc var_base_aspr eval "任务完成! 总共修复{var_count}h 个CALL?;)" msg $RESULT jmp to_leave err_version: msg "错误,OllyScript版本过低!" ret to_leave: bphwc var_base_aspr mov eip,var_oep ret ----------------------------------------------------------------------------------------------------------------------附几张脚本的截图:我感觉上面脚本中的注释已经够详细了,不需要我再一条语句一条语句的解释了。这里我需要解释的就是其如何实现在任意机器上定位ASProtect的函数的,这里它并没有采用我说的内存访问断点的方式。这里是保存ASProtect创建区段的起始地址。mov var_base_aspr, // 保存我们可得知要调用哪个API函数指令地址所在区段的基地址(该区段由ASProtect创建)add var_base_aspr,3B02E // 在基地址的基础上加上这个常量就可以定位到关键地址了bphws var_base_aspr,"x" // 对该关键地址设置硬件执行断点jmp to_lookfor 在46c048这个地址指向的内存单元中我们可以定位到关键地址(由该关键地址指令所在处我们可以得知要实际要调用哪个API函数)所在区段的基地址,然后加上一个常量就可以得到关键地址,这样就可以实现在各个机器上通用。因为不同的机器上可以该基地址可能会不相同,但是关键地址的偏移量是相同的,这里的关键地址的偏移量就是3B02E。所在接着对该关键地址设置硬件执行断点。好了,下面从起始地址为401000的代码段开始搜索目标地址为用户输入CALL的地址。to_execute: // 是用户输入的待修复CALL的目标地址 mov eip,var_dir // 将EIP修改为待修复CALL所在的地址 run // 运行起来这里将EIP设置为待修复CALL的地址,然后运行起来,紧接着下面的eob to_verify命令是遇到中断则跳转到to_verify标签处。eob to_verify // 如果断下来了,就跳转到to_verify标签处这里是验证是否断在了之前设置的硬件执行断点的指令处,如果是,则跳转到to_verify标签进行下一步处理。to_verify: cmp eip,var_base_aspr // 判断断下来的地方是不是关键地址(到执行到该关键地址处时,此时的EDX中保存了实际要调用API函数的地址) jne unexpected // 如果断下来的地方不是关键地址处,说明发生异常了,直接跳转到unexpected标签处,进行异常处理 mov var_api,edx // 如果断下来的地方是关键地址处,则将EDX寄存器中实际要调用API函数的地址保存到变量var_api中 jmp to_lookfor_api // 跳转到to_lookfor_api标签处,进行下一步的处理这里是判断是不是断在了关键地址处(该关键地址处,EDX寄存器中保存了实际要调用API函数的地址),如果不是断在关键地址处,就表示出错了,就跳转到unexpected标签处(异常处理标签),如果断在了关键地址处,则保存EDX中的API函数地址,接着跳转到to_lookfor_api标签处进行下一步处理。to_lookfor_api: cmp var_ini_iat, 460F28 // 判断是否超出了IAT的范围 je error // 如果超出了IAT的范围,说明并没有在IAT中定位到API函数对应的IAT项 cmp ,var_api // 判断是否是与该API函数地址对应的IAT项 je to_repair // 如果是与该API函数对应的IAT项,则跳转到to_repair标签处 add var_ini_iat,4 // 如果不是与该API函数对应的IAT项,将指向IAT的指针往后移4个字节,便于下一次查找 jmp to_lookfor_api // 跳转到to_lookfor_api标签处继续查找IAT项这里判断待查找的IAT指针是否超出了IAT的范围,如果指针已经超出IAT的范围,说明没找到与之匹配的IAT项,则跳转到error标签处,请用户重新输入待修复的地址。如果在IAT中查找到了API函数对应的IAT项,则跳转到to_repair标签处进行修复处理。修复处理如下:首先判断待修复CALL指令是否存在参考引用,如果不存在存在引用处则将其汇编为CALL [对应IAT项的地址]的形式,如果存在参考引用,则将其汇编为JMP [对应IAT项的地址]的形式。然后按照上面的步骤继续修复其他的项,直到所有项都修复为止。----------------------------------------------------------------------------------------------------------------------------------------------------------------------这里非常感谢HIHE童鞋提供的这个脚本,条理非常清晰,注释也很详细。很少有人编写的脚本注释的如此之详细。为HIEI童鞋点赞。为了感谢HIEI童鞋,HIEI童鞋将会免费获得一张去XXX旅行的机票。嘿嘿,不过你得亲自过来取,哈哈哈。好了,接下来还有一个小比赛,比起这个来说要简单的多。分为两个部分(我猜这次参赛的人可能会多一些,哈哈)。首先大家需要定位到TPPpack这款壳的OEP,然后修复它的Stolen bytes,之前Martian先生已经写过一个手脱TPPpack的教程了,哈哈。大家可以随意使用HideDebugger,HideOd等反反调试插件,以及ODdbgScript等脚本插件。第二部分,大家需要编写一个脚本来修复TPPpack的IAT。大家可能分别为两部分各编写一个脚本。简而言之:Part1:编写脚本定位OEP并修复Stolen bytesPart2:编写脚本修复IAT上面说过了大家可以使用上面提到的这3个插件。(但是请大家不要再问能不能使用CmdBar命令栏插件这样的问题(PS:提这样的问题确实有点弱智,哈哈哈),嘿嘿)比赛截止日期:8月30号,在截止日期之前完成任务的童鞋可以将你们的答案邮件给我,两个答案一起发,分开发都可以。Martian写的手脱TPPpack的教程在我的博客上,网址如下:http://ricardonarvaja.info/WEB/CONCURSOS%20VIEJOS/%20CONCURSOS2004-2006/%20CONCURSO97/(PS:貌似该页面已经被移除了)本章到此结束,感谢大家的观看。希望在第53章中看到你们的名字,嘿嘿。本系列文章汉化版转载看雪论坛
感谢原作者:RicardoNarvaja(西班牙人) 原作者个人主页:http://www.ricardonarvaja.info/
感谢热心翻译的朋友:1~3章译者:BGCoder4~58章译者:安于此生
全集配套程序下载地址:链接: http://pan.baidu.com/s/1eQzTWfo 密码: vytv
[快捷回复]-感谢楼主热心分享! 此处应该有鼓励~ 太棒了,感谢! 这个很实用,谢谢分享。 可以可以,学到了。 虽然看不懂,担佩服之情连绵不绝
页:
[1]