逆向
逆向实战演示
实现目标:
贪吃蛇999999分
1. exeinfo:
简单的查壳软件
2. IDA:
IDA是一款交互式反汇编工具,其功能十分强大,支持多操作系统、多处理器下的二进制程序的反汇编分析,并且可以和使用者进行交互来提升处理效率。
3. Cheat Engine(ce):
CheatEngine一款内存修改软件,功能十分强大,可以轻松实现对单机游戏的数据进行修改.
首先在网上随便下一个贪吃蛇程序:
游戏界面:
在记录这个选项中.能看见最近几次的最高分数
那如何得到我们想要的分数呢,(懒狗是不可能手打的)
首先打开Cheat Engine这个工具:
点击左上角的小电脑图标,选择贪吃蛇游戏的进程
点击Open
接下来开始游戏
简单获得一分
接下来打开Cheat Engine 进行搜索,在Value处输入获得的分数,然后点击First scan,第一次搜索
能够看见一共有超级多的搜索结果,接下来继续返回游戏,
再玩一次游戏,玩到两分:
这个时候返回Cheat Engine再搜索:
这个时候就神奇的发现,只剩一个结果了,这个结果就是我们想要的分数:
对这个结果双击,结果就会添加到下方的方框中
这个时候双击红色方框中的2,就可以进行修改值:
修改为999999
修改完成,回到游戏查看:
分数就修改成功了.
接下来使用以往新生赛的一道题来演示在CTF中如何得到flag:
首先看看这个游戏是怎么个玩法:
WASD控制,Enter开始游戏,只要得到60分就可以得到flag
使用exeinfo查看程序的位数:
接下来就可以32位IDA进行反汇编:
在左侧的函数框里,能一眼看见main函数,双击main函数:
很清晰的两个函数:开始菜单函数,游戏循环函数.
双击循环函数:
int gameMainLoop()
{
void *v0; // eax
int v1; // edx
int v2; // ecx
int v3; // eax
int v4; // eax
int v5; // eax
int v6; // eax
int v7; // eax
char v8; // cl
char v9; // cl
int result; // eax
char Buffer[1247]; // [esp+2Dh] [ebp-ECBh] BYREF
int v12; // [esp+50Ch] [ebp-9ECh]
int v13[625]; // [esp+510h] [ebp-9E8h] BYREF
char v14; // [esp+ED7h] [ebp-21h]
int j; // [esp+ED8h] [ebp-20h]
char v16; // [esp+EDFh] [ebp-19h]
int i; // [esp+EE0h] [ebp-18h]
int v18; // [esp+EE4h] [ebp-14h]
int v19; // [esp+EE8h] [ebp-10h]
int v20; // [esp+EECh] [ebp-Ch]
gameBegin();
v0 = malloc(1u);
srand((unsigned int)v0);
do
{
v20 = 12;
v19 = 12;
v18 = 4;
memset(v13, 0, sizeof(v13));
for ( i = 0; i <= 24; i += 2 )
{
v1 = 624 - i;
v2 = 25 * i;
v3 = 25 * (i + 1) - 1;
v13[v3] = -2;
v13[v2] = v13[v3];
v13[v1] = v13[v2];
v13[i] = v13[v1];
}
v16 = 100;
v14 = 3;
*(_DWORD *)Buffer = 0;
v12 = 0;
memset(&Buffer[3], 0, 4 * (((Buffer - &Buffer[3] + 1251) & 0xFFFFFFFC) >> 2));
sprintf_s(Buffer, 0x20u, "mode con: cols=%d lines=%d", 50, 25);
system(Buffer);
for ( j = 2; j; --j )
{
do
i = rand() % 625;
while ( v13[i] );
v13[i] = -1;
}
system("title GluttonousSnake");
while ( 1 )
{
if ( _kbhit() )
{
v14 = _getch();
if ( v14 )
{
switch ( v14 )
{
case 27:
exit(0);
case 32:
_getch();
break;
case 65:
case 75:
case 97:
if ( v16 != 100 )
v16 = 97;
break;
case 68:
case 77:
case 100:
if ( v16 != 97 )
v16 = 100;
break;
case 72:
case 87:
case 119:
if ( v16 != 115 )
v16 = 119;
break;
case 80:
case 83:
case 115:
if ( v16 != 119 )
v16 = 115;
break;
default:
break;
}
}
}
if ( v16 == 100 )
{
v5 = v20 > 23 ? -24 : 1;
v20 += v5;
}
else if ( v16 > 100 )
{
if ( v16 == 115 )
{
v6 = v19 > 23 ? -24 : 1;
v19 += v6;
}
else if ( v16 == 119 )
{
if ( v19 <= 0 )
v7 = -24;
else
v7 = 1;
v19 -= v7;
}
}
else if ( v16 == 97 )
{
if ( v20 <= 0 )
v4 = -24;
else
v4 = 1;
v20 -= v4;
}
if ( v13[25 * v19 + v20] > 1 || v13[25 * v19 + v20] == -2 )
break;
if ( v13[25 * v19 + v20] == -1 )
{
++v18;
do
i = rand() % 625;
while ( v13[i] );
v13[i] = -1;
sprintf_s(Buffer, 0x20u, "title score:%d", v18 - 4);
system(Buffer);
}
else
{
for ( i = 0; i <= 624; ++i )
{
if ( v13[i] > 0 )
--v13[i];
}
}
v13[25 * v19 + v20] = v18;
for ( i = 0; i <= 1249; ++i )
{
if ( v13[i / 2] )
{
if ( v13[i / 2] <= 0 )
{
if ( v13[i / 2] == -2 )
{
if ( (i & 1) != 0 )
v9 = 93;
else
v9 = 91;
Buffer[i] = v9;
}
else
{
Buffer[i] = 48;
}
}
else
{
if ( (i & 1) != 0 )
v8 = 41;
else
v8 = 40;
Buffer[i] = v8;
}
}
else
{
Buffer[i] = 32;
}
}
system("cls");
printf(Buffer);
sub_4020C0(v18);
Sleep(0x64u);
}
if ( isFlag )
{
decode_flag(&t_flag);
strcat(s_flag, &t_flag);
gameEndmenu(v18, s_flag);
}
else
{
gameEndmenu(v18, flag);
}
result = _getch();
}
while ( result == 32 );
return result;
}
在这一段中:
判断分数是否达到了60,如果达到了isFlag的值为真,然后解密flag并输出flag,如果没有达到,就会输出"so you cant get flag QWQ"
这个时候,如果我们可以修改if的判断条件,让他在判断的时候如果分数没有达到60就跳到解密输出flag中去,就可以轻松得到flag
做法如下:
对着函数的函数名右键:
点击Copy to assembly
就会跳转至图形界面中
在其中找到isFlag所在的位置:
判断时,是通过jz进行判断的:
于是就知道了达到60分isFlag不为0,没有达到60分就为0
那反过来没有达到60分就不为0,就会执行解密flag的操作:
只需将jz 修改成jnz即可:
接下来保存修改:
点击ok,保存成功
接下来再次运行游戏:
分数是0分但是获得了flag