[BUUCTF][网鼎杯 2020 青龙组]jocker 分析与记录
无壳,IDA打开可以直接进入main函数:
第12行调用VirtualProtect函数更改了offset encrypt处的访问保护权限
BOOL VirtualProtect(LPVOID lpAddress,SIZE_T dwSize,DWORD flNewProtect,PDWORD lpflOldProtect
);
参见:https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
该处数据为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; // eaxint i; // [esp+Ch] [ebp-4h]for ( i = 0; i <= 23; ++i ){result = &a1[i];if ( (i & 1) != 0 )a1[i] -= i;elsea1[i] ^= i;}return result;
}
int __cdecl omg(char *a1)
{int result; // eaxint v2[24]; // [esp+18h] [ebp-80h] BYREFint i; // [esp+78h] [ebp-20h]int v4; // [esp+7Ch] [ebp-1Ch]v4 = 1;qmemcpy(v2, &unk_4030C0, sizeof(v2));for ( i = 0; i <= 23; ++i ){if ( a1[i] != v2[i] )v4 = 0;}if ( v4 == 1 )result = puts("hahahaha_do_you_find_me?");elseresult = puts("wrong ~~ But seems a little program");return result;
}
wrong对输入值进行了一些加减或异或处理,然后将结果在omg中同unk_4030C0处数据进行对比;wrong的逆算法容易实现,照抄就行了
(现在才知道能够通过导出窗口快捷的提取出数据,一直以来的手抄实在是太笨了)
unsigned int k[24] = { 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] += i;}else{k[i] ^= i;}cout << (char)k[i];}cout << endl;
得到结果flag{fak3_alw35_sp_me!!},提交发现错误;由于往下还有关键的encrypt段没分析,所以不用太怀疑flag是否算错,可以大胆的将它当作一个假的flag
再往下读for循环,发现它对offset encrypt进行了异或,判断其为代码段解密,可以用动调转到这个地方
IDA没能及时更新,需要我们手动修正为函数
选中00401500~0040152F,将其标为代码(Force)
然后在00401502处创建函数,即可得到合适的结果
// positive sp value has been detected, the output may be wrong!
void __usercall __noreturn sub_401502(int a1@<ebp>)
{unsigned __int32 v1; // eaxv1 = __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[19] = {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[19] = {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[i] ^= key1[i];cout << (char)f[i];}cout << endl;
得到flag{d07abccf8a410c
我们知道,flag应有24字节,但for循环只有19次,也就是缺少了5个字符;由于encrypt函数已经读完了,所以我们需要的结果应该在最后一个函数中,也就是finally函数
将40159A~40159D处的数据全都转为代码,并将函数改为Undefine
重新在40159A处创建函数,得到新函数finally:
int __cdecl finally(char *a1)
{unsigned int v1; // eaxint result; // eaxchar v3[9]; // [esp+13h] [ebp-15h] BYREFint v4; // [esp+1Ch] [ebp-Ch]strcpy(v3, "%tp&:");v1 = time(0);srand(v1);v4 = rand() % 100;if ( (v3[*&v3[5]] != a1[*&v3[5]]) == v4 )result = puts("Really??? Did you find it?OMG!!!");elseresult = puts("I hide the last part, you will not succeed!!!");return result;
}
time(0)用以获取当前时间,第10行将其作为种子,第11行获取随机数;大概率我们是难以获取到出题人得到的种子的,因此,这个随机数若是必要的,应该只能通过预测得出
以及下面的if判断条件过于难以理解,不妨试着用OD去动调一下吧(个人觉得OD的动调会更好用一些,也好在这个函数没有被加密,OD还是能分析出来的,否则只能用IDA动调了,虽然没什么差别......)
即便用OD动调也仍然不是很容易能够读懂其意义
关键的比较在401617处,如果相等的话,就说明flag输对了
大致就是取flag的第几位同“%tp&:"几位,相等即可;并且这正好是5个字节,很可能就是剩下的flag
但汇编代码中似乎也同样没有相应的加密过程,只能靠猜测它没有被复杂的加密
通过前半段的flag猜测最后一个字符应该为‘}’,将其与“%tp&:”的最后一个异或后得到 71,并由此得到最后结果
char key2[] = "%tp&:";int v5 = '}' ^ key2[4];for (int i = 0; i < 5; i++){cout << (char)(key2[i] ^ v5);}
//flag{d07abccf8a410cb37a}
我也试着将这个提交成功的flag输入进去,但它仍然不会输出成功的标识,可能是出题人的一点“恶意”吧......最后要靠猜测来得到结果,说实在的,有点难以释然,总觉得是不是自己看漏了什么重要内容......
[BUUCTF][网鼎杯 2020 青龙组]jocker 分析与记录相关推荐
- Buuctf [网鼎杯 2020 青龙组]jocker 题解
目录 一.主函数逻辑 二.wrong函数和omg函数--假flag 1.wrong函数 2.omg函数 3.假flag 三.encrypt和finally函数--真flag 1.打开sp指针偏移显示 ...
- re -25 buuctf [网鼎杯 2020 青龙组]jocker
[网鼎杯 2020 青龙组]jocker 前话:ida7.6设置栏内没有general,可以通过ctrl+shift+p打开命令面板,搜索option打开设置选项,于Disassembly设置堆栈显示 ...
- BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker
BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker 先看下文件信息,没有加壳,32位程序 运行一下,又是一道字符串比较的题目 用IDA32位打开,分析一下 // positive ...
- [网鼎杯 2020 青龙组]jocker
[网鼎杯 2020 青龙组]jocker SMC(self-Modifying Code): 自修改代码,程序在执行某段代码的过程中会对程序的代码进行修改,只有在修改后的代码才是可汇编,可执行的.在程 ...
- Buuctf[网鼎杯 2020 青龙组]AreUSerialz
[网鼎杯 2020 青龙组]AreUSerialz 打开题目仔细阅读源码 <?phpinclude("flag.php"); highlight_file(__FILE__) ...
- [BUUCTF]Reverse——[网鼎杯 2020 青龙组]jocker
网上大部分是动调,我尝试IDC解一下, 无壳,32位,放入IDApro,查看main函数 查看wrong和str函数,借出假的flag a=[0x66,0x6B,0x63,0x64,0x7F,0x61 ...
- buuctf刷题记录21 [网鼎杯 2020 青龙组]jocker
今天挑战一下,结果最后还是看了别人的wp才写出来的 无壳,ida查看发现不能f5,原因堆栈不平衡 进行栈指针修改 修改出错的地方的栈指针偏移,快捷键alt+k,值改为0 然后就能f5了, 逻辑也不难, ...
- BUUCTF·[网鼎杯 2020 青龙组]boom·WP
BUUCTF在线评测 (buuoj.cn) 附件 是一个可运行的文件 回车出现: 分析 猜测是C编写的,拉到编译器,发现提示是二进制文件 那就用010Editor打开 查找了一下flag,这不就是跟附 ...
- [网鼎杯 2020 青龙组]jocker(详解)
首先我们查看一下信息,32位程序,无壳: 然后用IDA打开,提示// positive sp value has been detected, the output may be wrong! 出现了 ...
最新文章
- 《分布式服务架构:原理、设计与实战》第二章彻底解决分布式系统一致性的问题...
- 装运点确认失败的排查
- 2020全国大学生数学建模竞赛【论文格式、时间节点及作品提交要求、竞赛题目下载、评分要点】【微信公众号:校苑数模】
- java 数据截断_java – 数据截断:第1行的列’标志’的数据太长
- CF611F. New Year and Cleaning
- linux shell if -a 到-z参数含义
- 品质标签分几种颜色_常见标签种类分析
- Docker学习总结(28)——Docker 容器健康检查机制
- git学习(三)分支管理
- Linux kernel中网络设备的管理
- Navicat安装问题及解决办法
- layabox flash转html5,FlashBuilder中第一个H5程序“Hello Layabox”
- python 生成 exe
- 好玩好用的网站,不能错过!
- ios WKWebView之视频无法播放
- 【数字图像处理】【Matlab】【实验一】图像处理基本操作
- 疯狂的世界,疯狂的人!
- 中国尼龙搭扣市场趋势报告、技术动态创新及市场预测
- 小米手机安装推特后频繁闪退
- 在Ubuntu PHP中基于wxsqlite3扩展使用加密的Sqlite数据库