body { font-size: 15px; color: rgba(51, 51, 51, 1); background: rgba(255, 255, 255, 1); font-family: Helvetica, Arial, "PingFang SC", "Microsoft YaHei", "WenQuanYi Micro Hei", "tohoma,sans-serif"; margin: 0; padding: 10% }
h1 { font-size: 2.2em; font-weight: 700; line-height: 1.1; padding-top: 16px; margin-bottom: 4px }
h2, h3, h4, h5, h6 { line-height: 1.5em; margin-top: 2.2em; margin-bottom: 4px }
h2 { color: rgba(255, 255, 255, 1) !important; background-color: rgba(255, 155, 152, 1); border-left: 12px solid rgba(255, 126, 121, 1); padding: 1px 1px 1px 20px }
h3 { font-weight: 700; font-size: 1.2em; line-height: 1.4; margin: 10px 0 5px; padding-top: 10px }
h4 { font-weight: 700; text-transform: uppercase; font-size: 1.1em; line-height: 1.4; margin: 10px 0 5px; padding-top: 10px }
h5, h6 { font-size: 0.9em }
h5 { font-weight: bold; text-transform: uppercase }
h6 { font-weight: normal; color: rgba(170, 170, 170, 1) }
img { width: 100%; border-radius: 5px; display: block; margin-bottom: 15px; height: auto }
dl, ol, ul { margin-top: 12px; margin-bottom: 20px; padding-left: 5%; line-height: 1.8 }
p { margin: 0 0 20px; padding: 0; line-height: 1.8 }
a { color: rgba(242, 47, 39, 1); text-decoration: none }
a:hover { color: rgba(245, 88, 82, 1); text-decoration: underline }
a:focus { outline-offset: -2px }
blockquote { font-size: 1em; font-style: normal; padding: 30px 38px; margin: 0 0 15px; position: relative; line-height: 1.8; text-indent: 0; border: none; color: rgba(136, 136, 136, 1) }
blockquote:before { content: "“"; left: 12px; top: 0; color: rgba(224, 224, 224, 1); font-size: 4em; font-family: Arial, serif; line-height: 1em; font-weight: 700; position: absolute }
blockquote:after { content: "”"; right: 12px; bottom: -31px; color: rgba(224, 224, 224, 1); font-size: 4em; font-family: Arial, serif; line-height: 1em; font-weight: 700; position: absolute }
strong, dfn { font-weight: 700 }
em, dfn { font-style: italic; font-weight: 400 }
del { text-decoration: line-through }
pre { margin: 0 0 25px; font-size: 13px; line-height: 1.42857; word-break: break-all; word-wrap: break-word; border-radius: 4px; white-space: pre-wrap; display: block; background: rgba(248, 248, 248, 1); padding: 10px 20px; border: none; color: rgba(102, 102, 102, 1); font-family: Courier, sans-serif }
code { color: rgba(199, 37, 78, 1); background-color: rgba(249, 242, 244, 1); border-radius: 4px; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 2px 4px; font-size: 90% }
p>code { color: rgba(199, 38, 78, 1); background-color: rgba(249, 242, 244, 1); font-size: 0.95em; border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px }
figure { margin: 1em 0 }
figcaption { font-size: 0.75em; padding: 0.5em 2em; margin-bottom: 2em }
figure img { margin-bottom: 0 }
hr { border: 0; height: 1px; background: linear-gradient(90deg, rgba(204, 204, 204, 1), rgba(51, 51, 51, 1), rgba(204, 204, 204, 1)) rgba(51, 51, 51, 1) }
ol p, ul p { margin-bottom: 0 }
li { margin-bottom: 0.75em; margin-top: 0.75em }
ol#footnotes { font-size: 0.95em; padding-top: 1em; margin: 20px 0; border-top: 1px solid rgba(234, 234, 234, 1); counter-reset: footer-counter 0; list-style: none; color: rgba(85, 85, 85, 1); padding-left: 5% }
ol#footnotes li { margin-bottom: 10px; margin-left: 16px; font-weight: 400; line-height: 2; list-style-type: none }
ol#footnotes li:before { content: counter(footer-counter) ". "; counter-increment: footer-counter 1; font-weight: 800; font-size: 0.95em }
@keyframes highfade { 0% { } 20% { background-color: rgba(255, 255, 0, 1) } 100% { } }
@-webkit-keyframes highfade{0%{background-color:none}20%{background-color:yellow}100%{background-color:none}}
a:target, ol#footnotes li:target, sup a:target { animation-name: highfade; animation-duration: 2s; animation-iteration-count: 1; animation-timing-function: ease-in-out; -webkit-animation-name: highfade; -webkit-animation-duration: 2s; -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: ease-in-out }
a:target { border: 0; outline: 0 }
a:target { border: 0; outline: 0 }
a:target { border: 0; outline: 0 }
发布时间:2023年7月24日
内容概要:练一道题
img { box-shadow: 0 0 2px rgba(6, 24, 44, 0.4), 0 4px 6px -1px rgba(6, 24, 44, 0.65), inset 0 1px rgba(255, 255, 255, 0.08) }
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-1-%E8%BD%AF%E4%BB%B6%E4%BF%9D%E6%8A%A4%E6%8A%80%E6%9C%AF%5BXDbg%5D-1.png)
程序名:CrackMeDemo.tvmp.1.exe
程序界面长相如下:
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-1.png)
程序内存长相如下:
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-2.png)
程序内存字符串长相如下:
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-3.png)
看样子是个 MFC 程序(“应用程序向导生成的本地应用程序”、“CWINAPP”)
除此之外有用的信息似乎就是 “恭喜你,注册成功!” 了。
扔进 OllyDbg 里,F9跑起来。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-4.png)
查看 OllyDbg 日志,并无太多有用信息,只是知道它跑了俩个异常。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-5.png)
嗯。。想想有没有办法定位反调试代码范围。
通过例子来学习反调试,点击图片开始学习。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-6.png)
这是我刚才 CV 过来的反调试例子。看样子我们可以试试通过 API 断点,来定位反调试。
if (IsDebuggerPresent())
ExitProcess(-1);
BOOL bDebuggerPresent;
if (TRUE == CheckRemoteDebuggerPresent(GetCurrentProcess(), &bDebuggerPresent) &&
TRUE == bDebuggerPresent)
ExitProcess(-1);
在 CheckRemoteDebuggerPresent 函数下断点,程序断下后,Ctrl + F9 出 CALL。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-7.png)
似乎走到了 ".tvm" 这个代码段来了,简单的使用下我们在日后编写的分析工具 - “基于 CPU 模拟的 xxx ” 。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-8.png)
好的,成功模拟到了 0x040157A 这个地址。通过反汇编代码可以看出,这个代码块似乎含有多个反调试函数,有俩个 jcc 指令跳转到了含有 CALL ExitThread 指令的基本块。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-9.png)
F9 运行到 0x040157A 里,由于我们的反反调试插件强大,所以CheckRemoteDebuggerPresent 返回了 0 ,即程序未被调试的意思。
继续模拟程序,运行到 IsDebuggerPresent 函数中。嗯,是刚才我们学过的反调试内容。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-10.png)
将程序扔入 IDA 中 F5 继续分析。根据 IDA 提供的符号,我们大致可以看出这个反调试是在执行 MFC m = new M(b,c) 后才开始检测的。这个信息对于我们完整还原这个程序至关重要,没错,我想把这个程序还原掉~
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-12.png)
让我们看看程序检测调试到后会做些什么?
发现有个 sub_4031f0 call,学过设计模式的朋友应该知道单例模式:即一个类负责一个功能。所以 sub_403530 我是优先不管的。
if ( cObf_CheckRemoteDebug() == 1 || cObf_IsDebug() == 1 )
{
v4 = sub_403530(1); // thiscall 不用理他,估计就是销毁 Dialog 用的。
sub_4031F0(v4); // cdeclCall 有点意思~
ExitThread(8u);
}
对 sub_4031f0 扫引用,发现有个未被 Ida 识别成函数的地址(0x4021B6)。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-13.png)
转到 0x4021B6 看看,发现有个 call MessageBoxA ,字符串参数为 byte_428D64 。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-14.png)
用 OD 转到 byte_428D64 看看是什么内容。运气不错,直逼关键内容。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-15.png)
让我们对 0x4021B6 附近的代码进行简单的分析。发现一个循环代码,跳转到 loc_401F9A 看看。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-16.png)
发现了一个立即数:8。(如果你足够细心就可以发现,这是我们可输入的用户名的最大长度)
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-17.png)
看样子关键算法代码,我们已经定位到了,即在 0x00401FA9 附近。
经过刚才的分析,我们基本可以判断这个 Call 是在做 MFC 初始化 + AntiDebuger 操作了。将其记录下来~ 这是我们逆向出来的第一个符号。这是一个重要的阶段性胜利。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-11.png)
这里用最容易被 Anti 掉的过检测方式来过掉反调试。将其 leave ret 即可过掉反调试。(还有内存校验没有解决)
可以看到,这里程序已经正常运行了。hiahia。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-18.png)
OD 转到 0x00401FA9 下查看情况,似乎 IDA 有好多代码没有识别出来,有点小尴尬哈。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-19.png)
找了下函数头部 0x00401B35 ,回到 IDA ,一路选中未识别的代码,按下 C 键完成格式转换。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-20.png)
先静态分析看看函数的控制流程好了,在这里我给出我认为简洁有用的信息。
.text:00401DA6 call ?UpdateData@CWnd@@QAEHH@Z ; CWnd::UpdateData(int)
.text:00401DFC call ?GetDlgItem@CWnd@@QBEPAV1@H@Z ; CWnd::GetDlgItem(int)
.text:00401E1F call ?GetDlgItem@CWnd@@QBEPAV1@H@Z ; CWnd::GetDlgItem(int)
.text:00401E9A cmp dword ptr [ebp-8C4h], 8
.text:00401EA1 jge short loc_401F21
.text:00402204 repe cmpsd
.text:00402206 jnz short loc_40221C
.text:00402208 push 0 ; unsigned int
.text:0040220A push 0 ; lpCaption
.text:0040220C push offset Text ; lpText
.text:00402211 mov ecx, [ebp-8D0h] ; this
.text:00402217 call ?MessageBoxA@CWnd@@QAEHPBD0I@Z ; CWnd::MessageBoxA(char const *,char const *,uint)
用 OD 看看和 MsgBoxA 有关的信息 text:00402204 repe cmpsd。经过测试,发现算法不允许输入中文字符串,尴尬了。
uN: XXXXXDbg
pW: EasyXEasyXEasyXEasyXEasyXEasyXEasyXEasyXEasyX
程序是明码比较,这似乎意味着我们在算法部分可以轻松一下了 O(∩_∩)O。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-21.png)
用 IDA 进行 F5 操作。嗯,好模糊的代码,看样子需要清理花指令才能看清楚。(可是现在已经是 22:11:38 了,一天要结束了,就不写脚本清理了)
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-22.png)
数了下大致的代码行数,只有 213 行汇编,直接用 OD 调试还原掉好了。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-23.png)
好了,回到让我们到 0x0401E9A 下断点.
.text:00401E9A cmp dword ptr [ebp-8C4h], 8
.text:00401EA1 jge short loc_401F21
经过我努力的跟踪,还原出了如下代码,发现程序获取了代码段的内容并参与到了算法的计算当中,真是个不错的设计。
这样做的话,程序如果直接爆破,生成出来的 serial 必然是错的,难怪程序会直接明码比较,原来暗藏着一股气。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-24.png)
打开 VS ,编写我们的第一段 KeyGen。
// KeyGen.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
unsigned char t[0x0010] =
{
0xf7, 0xff, 0xff, 0x89, 0x8d, 0x4c, 0xf7, 0xff, 0xff, 0xc7, 0x85, 0x48, 0xf7, 0xff, 0xff, 0x14
};
unsigned char t2[0x0010] =
{
0x07, 0x82, 0x19, 0xe8, 0x1f, 0xfe, 0xff, 0xff, 0x0f, 0xb6, 0xd0, 0x85, 0xd2, 0x74, 0x05, 0xe9
};
void getUn2(unsigned char* _un, unsigned char* _un2)
{
unsigned char* un = _un;
unsigned char* un2 = _un2;
for (size_t i = 0; i < 8; i++)
{
int a = (int)*(char*)(t+i);
int a2 = (int)*(char*)(un + i) ^ i;
un2[i] = a2 ^ a;
int b = (int)*(char*)(t2 + i);
int b2 = (int)*(char*)(un2 + i) ;
un2[i] = b2 ^ b;
}
}
int main()
{
std::cout << "Hello KeyGen!\n";
unsigned char uN[] = "XXXXXDbg";
unsigned char* uN2 = new unsigned char[sizeof(uN)];
getUn2(uN,uN2);
return 0;
}
a8 24 bc 3a ce f3 4c 40 // cd fd fd fd
A8 24 BC 3A CE F3 4C 40 // 00 00 00 00
可以看到结果完全正确,让我们继续还原。发现了一个未知的值,[EBP - 0x828],OD 往上翻翻,看看它的算法。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-25.png)
可以看到一个循环很多次的代码,结合刚才的经验,可以看出这个 constV 也是通过代码段的内容算出来的。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-26.png)
这个函数就不用还原了,取消所有软件断点,然后在 0x0401C53 下硬件执行断点,拿到 constV 即可。
constV = 0xCF238EE8;
int constV = 0xCF238EE8;
*(int*)((int*)uN2) ^= constV;
*(int*)((int*)uN2+1) ^= constV;
接下来又是一个循环,估计又有好多表要 CV 下来,哈哈,可以看到依旧循环 8 次。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-27.png)
继续单步~发现了三个 flag 将这段汇编还原成 C 代码~。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-28.png)
还原代码如下。
for (size_t i = 0; i < 8; i++)
{
flag1 = ((uN2[i] & 0xE0) + (((uN2[i] & 0xE0 )>> 24) & 0x1F)) >> 5;
flag2 = (uN2[i] & 0x1C) + (((uN2[i] & 0x1C) >> 24) & 3) >> 2;
flag3 = uN2[i] & 0x3;
int flag4 = i / 3;
int id = i >> 24;
if (id == 2)
{
// .......
}
if (id == 1)
{
// .......
}
if (id == 0)
{
// .......
}
}
继续单步跟踪,在这块代码中,我们发现几张 table 和新的 flag ,翻翻汇编代码看看 flag 有什么意义。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-29.png)
联系上文可以发现,它存储的就是正确的注册码 Serial。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-30.png)
将 table dump 出来将这个基本块还原成 C 代码。
unsigned char t3_834[0x0040] =
{
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x40, 0xed, 0x9f, 0xf5,
0x26, 0x3a, 0x6f, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned char t4_87c[0x00B0] =
{
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x6e, 0x69, 0xc9, 0x1b, 0x40, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x18, 0x00, 0xa0, 0xf0, 0x18, 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x00, 0x00, 0x40, 0xed, 0x9f, 0xf5, 0x26, 0x3a, 0x6f, 0x8f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned char t5_864[0x0090] =
{
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x18, 0x00, 0xa0, 0xf0, 0x18, 0x00,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x40, 0xed, 0x9f, 0xf5,
0x26, 0x3a, 0x6f, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
if (id == 0)
{
serial[i] = t3_834[flag2];
serial[i+1] = t4_87c[flag3];
serial[i+2] = t5_864[flag1];
}
好了,接下来我们继续还原其他分支,这是 id == 1 的情况。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-31.png)
还原代码如下。
if (id == 1)
{
serial[i] = t3_834[flag1];
serial[i + 1] = t4_87c[flag2];
serial[i + 2] = t5_864[flag3];
}
这是 id == 2 的情况,发现了俩张新的 table:t6、t7 。
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-32.png)
将新的 table dump 下来,还原 C 代码如下。
unsigned char t6_83c[0x001E] =
{
0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5A, 0x7F, 0x00, 0xF6, 0x9A, 0xE2, 0xF7, 0x9B, 0x50, 0x09, 0x80, 0x00, 0x00
};
unsigned char t7_874[0x003C] =
{
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x19, 0x00, 0xC9, 0x1B, 0x40, 0x00,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C
};
if (id == 2)
{
serial[i] = t5_864[flag3];
serial[i+ 1] = t6_83c[flag1];
serial[i + 2] = t7_874[flag2];
}
在测试的时候,发现 serial 对不上,原因是因为我忘记还原这条汇编指令了、flag4 和 id 的关系搞错了 、constV 提取错了。
IMUL EAX, EAX, 0x3
//int constV = 0xCF238EE8;
int constV = 0xE0698E5C;
int flag4 = i % 3;
int id = flag4;
最终输出结果如下。
XXXXXDbg
Vi7Vk21OvWk6Um21LxRj0Ui0
%E8%BD%AF%E4%BB%B6%E5%8A%A0%E5%AF%86%E4%B8%8E%E8%A7%A3%E5%AF%86-2-1-%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95%5BXDbg%5D-33.png)
看样子这个程序被巧妙的绕过了未知的 vm 保护,实际上经过我的测试,这个程序并无 vm 保护,有基于堆栈的花指令混淆。
这在学习第三章也就是下一章代码混淆时,会将其清除~
日后再补。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章