[网鼎杯 2020 青龙组]jocker分析与记录
前言:如您所见的是,图片似乎带有水印,但这并不意味着它是转载的,因为某站上的这篇帖子也是我写的......好不容易终于注册上这个大佬如云的地方的账号了,于是整理一下也发在这里了。另外一边已经完全弃用了。
自己的水平还不是很高,欢迎大佬们交流。如果您发现我的叙述存在任何问题,也请务必纠正!感谢!
正文:
无壳,IDA打开可以直接进入main函数:
https://img-blog.csdnimg.cn/20210630103900237.png
第12行调用VirtualProtect函数更改了offset encrypt处的访问保护权限
BOOL VirtualProtect(LPVOID lpAddress,SIZE_T dwSize,DWORDflNewProtect,PDWORD lpflOldProtect);
参见:Memory Protection Constants (WinNT.h) - Win32 apps | Microsoft Docs
该处数据为0x4:PAGE_READWRITE
Enables read-only or read/write access to the committed region of pages. If Data Execution Prevention is enabled, attempting to execute code in the committed region results in an access violation.
简单来说就是让这块数据能够被读写了(通常text段中只能拥有读/写中的一种)
继续往下读,发现输入值应该符合 24字节 的长度,然后遇到wrong 和 omg这两个函数
char *__cdecl wrong(char *a1)
{
char *result; // eax
int i; //
for ( i = 0; i <= 23; ++i )
{
result = &a1;
if ( (i & 1) != 0 )
a1 -= i;
else
a1 ^= i;
}
return result;
}
int __cdecl omg(char *a1)
{
int result; // eax
int v2; // BYREF
int i; //
int v4; //
v4 = 1;
qmemcpy(v2, &unk_4030C0, sizeof(v2));
for ( i = 0; i <= 23; ++i )
{
if ( a1 != v2 )
v4 = 0;
}
if ( v4 == 1 )
result = puts("hahahaha_do_you_find_me?");
else
result = puts("wrong ~~ But seems a little program");
return result;
}
wrong对输入值进行了一些加减或异或处理,然后将结果在omg中同unk_4030C0处数据进行对比;wrong的逆算法容易实现,照抄就行了
https://img-blog.csdnimg.cn/20210630105653286.png
(现在才知道能够通过导出窗口快捷的提取出数据,一直以来的手抄实在是太笨了)
unsigned int k = { 0x66,0x6b,0x63,0x64,0x7f,0x61,0x67,0x64,0x3b,0x56,0x6b,0x61,0x7b,0x26,0x3b,0x50,0x63,0x5f,0x4d,0x5a,0x71,0xc,0x37,0x66 };
for (int i = 0;i < 24; i++)
{
if ((i & 1) != 0)
{
k += i;
}
else
{
k ^= i;
}
cout << (char)k;
}
cout << endl;
得到结果flag{fak3_alw35_sp_me!!},提交发现错误;由于往下还有关键的encrypt段没分析,所以不用太怀疑flag是否算错,可以大胆的将它当作一个假的flag
再往下读for循环,发现它对offset encrypt进行了异或,判断其为代码段解密,可以用动调转到这个地方
https://img-blog.csdnimg.cn/20210630112117289.png
https://img-blog.csdnimg.cn/20210630112141318.png
IDA没能及时更新,需要我们手动修正为函数
选中00401500~0040152F,将其标为代码(Force)
https://img-blog.csdnimg.cn/2021063011284258.png
然后在00401502处创建函数,即可得到合适的结果
https://img-blog.csdnimg.cn/20210630113002264.png
https://img-blog.csdnimg.cn/20210630113010653.png
// positive sp value has been detected, the output may be wrong!
void __usercall __noreturn sub_401502(int a1@<ebp>)
{
unsigned __int32 v1; // eax
v1 = __indword(0x57u);
*(_DWORD *)(a1 - 32) = 1;
qmemcpy((void *)(a1 - 108), &unk_403040, 0x4Cu);
for ( *(_DWORD *)(a1 - 28) = 0; *(int *)(a1 - 28) <= 18; ++*(_DWORD *)(a1 - 28) )
{
if ( (char)(*(_BYTE *)(*(_DWORD *)(a1 - 28) + *(_DWORD *)(a1 + 8)) ^ Buffer[*(_DWORD *)(a1 - 28)]) != *(_DWORD *)(a1 + 4 * *(_DWORD *)(a1 - 28) - 108) )
{
puts("wrong ~");
*(_DWORD *)(a1 - 32) = 0;
exit(0);
}
}
if ( *(_DWORD *)(a1 - 32) == 1 )
puts("come here");
}
IDA分析得到的代码并不是那么易读,显然,它将一些索引给翻译错了,但并非无法理解的程度
首先,提取unk_403040处的数据放入(a1-108)处,以及循环中用到的Buffer
char Buffer[] = "hahahaha_do_you_find_me?";
unsigned int unk_403040 = {0x0E,0x0D ,0x09 ,0x06 ,0x13 ,0x05 ,0x58 ,0x56 ,0x3E ,0x06 ,0x0C ,0x3C ,0x1F ,0x57 ,0x14 ,0x6B ,0x57 ,0x59 ,0x0D };
*(a1-28)实际上是一个索引,指示了这个循环会执行19次;而(*(a1 - 28) + *(a1 + 8))相当于输入值指针加上一个偏移,其内容就是我们的输入值
这个输入值和Buffer异或后的结果应该等于(a1 - 108)的内容,也就是unk_403040处的数据,同样也容易写出解密代码
char key1[] = "hahahaha_do_you_find_me?";
unsigned int f = {0x0E,0x0D ,0x09 ,0x06 ,0x13 ,0x05 ,0x58 ,0x56 ,0x3E ,0x06 ,0x0C ,0x3C ,0x1F ,0x57 ,0x14 ,0x6B ,0x57 ,0x59 ,0x0D };
for (int i = 0; i < 19; i++)
{
f ^= key1;
cout << (char)f;
}
cout << endl;
得到flag{d07abccf8a410c
我们知道,flag应有24字节,但for循环只有19次,也就是缺少了5个字符;由于encrypt函数已经读完了,所以我们需要的结果应该在最后一个函数中,也就是finally函数
https://img-blog.csdnimg.cn/20210630114433932.png
将40159A~40159D处的数据全都转为代码,并将函数改为Undefine
https://img-blog.csdnimg.cn/20210630120139642.png
重新在40159A处创建函数,得到新函数finally:
https://img-blog.csdnimg.cn/20210630120254303.png
int __cdecl finally(char *a1)
{
unsigned int v1; // eax
int result; // eax
char v3; // BYREF
int v4; //
strcpy(v3, "%tp&:");
v1 = time(0);
srand(v1);
v4 = rand() % 100;
if ( (v3[*&v3] != a1[*&v3]) == v4 )
result = puts("Really??? Did you find it?OMG!!!");
else
result = puts("I hide the last part, you will not succeed!!!");
return result;
}
time(0)用以获取当前时间,第10行将其作为种子,第11行获取随机数;大概率我们是难以获取到出题人得到的种子的,因此,这个随机数若是必要的,应该只能通过预测得出
以及下面的if判断条件过于难以理解,不妨试着用OD去动调一下吧(个人觉得OD的动调会更好用一些,也好在这个函数没有被加密,OD还是能分析出来的,否则只能用IDA动调了,虽然没什么差别......)
https://img-blog.csdnimg.cn/20210630122059300.png
即便用OD动调也仍然不是很容易能够读懂其意义
关键的比较在401617处,如果相等的话,就说明flag输对了
大致就是取flag的第几位同“%tp&:"几位,相等即可;并且这正好是5个字节,很可能就是剩下的flag
但汇编代码中似乎也同样没有相应的加密过程,只能靠猜测它没有被复杂的加密
通过前半段的flag猜测最后一个字符应该为‘}’,将其与“%tp&:”的最后一个异或后得到 71,并由此得到最后结果
char key2[] = "%tp&:";
int v5 = '}' ^ key2;
for (int i = 0; i < 5; i++)
{
cout << (char)(key2 ^ v5);
}
//flag{d07abccf8a410cb37a}
我也试着将这个提交成功的flag输入进去,但它仍然不会输出成功的标识,可能是出题人的一点“恶意”吧......最后要靠猜测来得到结果,说实在的,有点难以释然,总觉得是不是自己看漏了什么重要内容...... 就差最后一部分想弄出来,看不到图片真的好难受.....我的汇编代码处就只有这部分,之后有一段很长的数据:
.text:0040159A
.text:0040159A
.text:0040159A ; _DWORD __cdecl finally(char *)
.text:0040159A public __Z7finallyPc
.text:0040159A __Z7finallyPc proc near ; CODE XREF: _main+121↓p
.text:0040159A 000 14 C8 adc al, 0C8h ; Add with Carry
.text:0040159C 000 A4 movsb ; Move Byte(s) from String to String
.text:0040159D 000 C2 AD 69 retn 69ADh ; Return Near from Procedure
.text:0040159D
.text:0040159D __Z7finallyPc endp
.text:0040159D
.text:0040159D ; ---------------------------------------------------------------------------
.text:004015A0 87 04 AA 64 87 04 AD 35 87 04 AC 31 87 04 AF 67+ dd 64AA0487h, 35AD0487h, 31AC0487h, 67AF0487h, 7BAE0487h
.text:004015A0 87 04 AE 7B 86 45 65 41 41 41 41 E8 68 13 00 00+ dd 41654586h, 0E8414141h, 1368h, 0E8240489h, 1368h, 136BE8h
.text:004015A0 89 04 24 E8 68 13 00 00 E8 6B 13 00 00 89 C1 BA+ dd 0BAC18900h, 51EB851Fh, 0EAF7C889h, 8905FAC1h, 1FF8C1C8h
.text:004015A0 1F 85 EB 51 89 C8 F7 EA C1 FA 05 89 C8 C1 F8 1F+ dd 0D089C229h, 8BF44589h, 0C06BF445h, 89C12964h, 0F44589C8h
.text:004015A0 29 C2 89 D0 89 45 F4 8B 45 F4 6B C0 64 29 C1 89+ dd 0F045C7h, 83000000h, 7F04F07Dh, 0EB558D3Eh, 1F0458Bh
.text:004015A0 C8 89 45 F4 C7 45 F0 00 00 00 00 83 7D F0 04 7F+ dd 10B60FD0h, 8BF04D8Bh, 0C8010845h, 3800B60Fh, 0C0950FC2h
.text:004015A0 3E 8D 55 EB 8B 45 F0 01 D0 0F B6 10 8B 4D F0 8B+ dd 3BC0B60Fh, 0E74F445h, 2C2404C7h, 0E8004040h, 1310h
.text:004015A0 45 08 01 C8 0F B6 00 38 C2 0F 95 C0 0F B6 C0 3B+ dd 4C70DEBh, 40405C24h, 1302E800h, 0C9900000h
.text:00401640 ; ---------------------------------------------------------------------------
.text:00401640 C3 retn ; Return Near from Procedure
有大佬知道这是为什么吗,IDA的版本是7.7:'(weeqw 本帖最后由 涛之雨 于 2021-11-18 18:07 编辑
一起加油!
提几个建议
1. 推荐试用md,bbcode真的有点不fashion了。
2. 自己的文章别的地方的水印无所谓,只要不违规都行(公众号,QQ空间啊什么之类的带联系方式的肯定不行)
3. 图片尽量上传原图,就算是从别的地方转发过来的最好也下载后上传,有些地方会检测盗链。
4. 吾爱挺好,我写教程说我违规侵权,然后那个下载站里到处都是乱七八糟的东西,不少人都转cnblog或者是自己搭小站了(我是懒。。。) 没看懂,这个题好难 涛之雨 发表于 2021-11-13 14:50
一起加油!
提几个建议
1. 推荐试用md,bbcode真的有点不fashion了。
感谢版主大大建议!
这篇文章当时在C某某N那里富文本写的,当时还并不会用markdown呢,现在再想转一遍又觉得有些麻烦,用md重写的话就更麻烦了(懒癌犯了)
图片以后会注意一下,尽量直接传原图的。
感谢大佬建议qwq 学习了,不错 学习了,谢谢分享 学习了,写的非常详细! 我给你编辑了下,看起来csdn水印通过链接参数加上去的,你其实可以把图片上传论坛本地。
页:
[1]
2