这里是ctf逆向小白学习之路的第一站
Section1 两个逆向工具的作用:
1:IDA pro7.0是完整版本 7.2只有64 f5可以查看源码,6.8 pro的有些文件grapf view不能分析,作用:静态调试,32位的程序用32位的ida分析
2:OllyDBG,作用:动态调试,只能调试32位的程序
Section2 ida伪代码分析
下面展示一些 内联代码片
。
unsigned __int64 Decry()
{ char v1; // [rsp+Fh] [rbp-51h]
int v2; // [rsp+10h] [rbp-50h]
int v3; // [rsp+14h] [rbp-4Ch]
int i; // [rsp+18h] [rbp-48h]
int v5; // [rsp+1Ch] [rbp-44h]
char src[8]; // [rsp+20h] [rbp-40h]
__int64 v7; // [rsp+28h] [rbp-38h]
int v8; // [rsp+30h] [rbp-30h]
__int64 v9; // [rsp+40h] [rbp-20h]
__int64 v10; // [rsp+48h] [rbp-18h]
int v11; // [rsp+50h] [rbp-10h]
unsigned __int64 v12; // [rsp+58h] [rbp-8h]
v12 = __readfsqword(0x28u);
*(_QWORD *)src = 357761762382LL;
v7 = '\0';
v8 = 0;
v9 = 512969957736LL;
v10 = '\0';
v11 = 0;
text = (char *)join(key3, &v9);
strcpy(key, key1);
strcat(key, src);
v2 = 0;
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > '@' && key[v3 % v5] <= 'Z' )
key[i] = key[v3 % v5] + 32;
++v3;
}
printf("Please input your flag:");
while ( 1 )
{
v1 = getchar();
if ( v1 == '\n' )
break; 输入回车程序终止
if ( v1 == ' ' )
{
++v2;
}
else
{
if ( v1 <= 96 || v1 > 122 )
{
if ( v1 > 64 && v1 <= 90 )
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
else
{
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
if ( !(v3 % v5) )
putchar(' ');
++v2;
}
}
if ( !strcmp(text, str2)
puts("Congratulation!\n");
else
puts("Try again!\n");
return __readfsqword(0x28u) ^ v12;
}
//u:usigned
//_QWORD:Bit,Byte,Word,Dword,Qword. 8个比特(Bit)=一个字节(Byte),两个字节称为一个字(Word),两个字称为一个双字(Dword),两个双字称为一个四字(Qword)。
可以把_WORD理解为int,_Dword、 _QWORD理解为long int,这里src是一个强制类型转化为int类型的指针,char本质上也是int
//357761762382LL:涉及大小端序存储的问题
大端序(Big-endian):高位字节存入低地址,低位字节存入高地址;小端序(Little-endian):低位字节存入低地址,高位字节存入高地址,
这里可以看到两个十六进制是大端序,但是数据在内存中都是小端序,所以要将其反转一下。鼠标选中16进制按r键得到字符串,再反转为’NDCLS’和’hadow’
得到:
text= "killshadow"11个字符
key= "ADSFKNDCLS”11个字符
//Join:将key3和&v9两个字符串连接起来
逆向编写脚本:
思路1:
11次外循环:依次将正确的v1值存储至flag[]中
内循环:找到正确的v1值使得str2[v2]值与text中的一个字符能够比对相等,v1值可以用循环129次,ASCII码0-128依次试,因为输入的数在ASCII码是不确定的,所以倒推可以把所有的数都试一遍
思路2:
已知flag变换后要比对的字符串
用已知的字符串经过已知的变换式子倒推未知的变量
str[i]=(v1-39-key[i]+97)%26+97= text[i];
text[i]、key[i] 已知,反写式子求v1
总结:
1:大端序和小端序存储
2:Word,Dword,Qword数据类型
3:Join函数的作用
4:逆向编写脚本,
首先从最后一步比较是否相等的字符串倒推
Section3 OllyDbg的基本操作
背景知识:pe文件用OllyDbg打开后,首先看到的是EP (EntryPoint)入口点代码,是执行程序最先执行的代码,依赖于CPU,之后是编译器为保证程序正常运行自动添加的启动函数(不同编译器产生的启动函数都不同),而逆向分析首先是找到pe文件的main函数。
1、用OllyDbg寻找主函数
以hello word.exe为例
这里先了解一下hello word.exe(双击会出现一个box框显示“hello word!”)
main函数
方法一:代码执行法(只适用于代码量不大,功能明确的程序)
导入OD后,不断执行F8(单步步过,遇到call指令仅执行函数,不进入函数内部),当执行到main函数时,如果main函数功能是调用对话框显示信息,则在这一步程序会暂停,并执行mian函数功能,此时的地址就是main函数的地址,但如果main函数功能是比较两数的大小,那么f8会执行完整个程序,而不会在main函数处暂停
方法二:根据运行程序,程序给出的提示寻找main函数:
1)根据字符串找main函数
Cpu窗口右击查找-所有参考文本地段,双击即可进入cpu窗口对应字符串
2)查看被调用的api找main函数
推测main函数中使用的api—> 查看所有被调用的api(Cpu窗口右击查找-所有模块间的调用)—>双击进入函数
3)查看程序所有api找main函数(当程序被压缩保护后,OD就不能列出api调用列表了)
推测main函数中使用的api—>查看所有被加载的dll中的api(Cpu窗口右击查找-所有模块中的名称)—>定位到messagebox函数—>双击查看该函数的地址空间(我的理解是查看这个函数的具体实现方法)—>右侧寄存器窗口ESP的值即进程栈的地址,这里下一个断点,F9运行,程序停在了断点处,说明此处确实是mian函数的执行处—>查看栈窗口,看到call调用指令,说明0040100E处的指令调用了messagebox函数,go to(ctrl+g)跳转至0040100E,这里就是main函数的所在处
2、用OllyDbg修改pe文件(打补丁/破解)
以hello word.exe为例
1)直接修改字符串缓冲区(数据区的hex 数据窗口)
Go to跳转至4092a0,ctrl+e编辑,选中修改的ascii码右击复制到可执行文件,右击保存文件
2)在其他内存区新建字符串后,直接修改汇编码栏处被调用命令中的地址(在cpu窗口,将被传递给消息函数)
找到一个空闲的内存区,ctrl e编辑,修改cpu区push地址
懂得修改cpu窗口处的信息很重要,针对一些绕过,比如程序的功能是比较两个信息是否一样(用到JE命令:条件跳转指令,分为有条件跳转和无条件跳转),我们就可以修改JE命令为无条件跳转,就可以直接跳转到最后的结果了
3、总结:
1:跳转到某一地址处(go to):ctrl+g
2:编辑hex数据:ctrl+e
3:修改cpu区指令/地址:空格或直接双击
4:Push 00409FA0:将00409FA0地址处的数据压入栈,F8执行该命令后会发现ESP的值减小
//压栈和出栈与ESP寄存器(栈指针寄存器)有关,一个进程中,ESP初始状态指向栈底,push将数据压入栈,ESP上移,向低地址移动,pop将数据弹出栈,ESP下移,向高地址移动,栈为空,ESP指向栈底