Outils d'utilisateurs

Outils du Site


pwnium2k14_reverse2

Ceci est une ancienne révision du document !


Cette épreuve est constituée d'un exécutable livré avec SDL.dll

Quand on lance l'exécutable, on nous propose de jouer à un clone du jeu Snake :


En reversant l'application avec IDA, on localise le main en 0x402A98 grâce aux appels de l'API de SDL servant à initialiser la librairie ainsi que la fenêtre SDL :

  SDL_Init(33);
  v9 = SDL_SetVideoMode(336, 240, 0, 1073741825);
  SDL_WM_SetCaption("Special Pwnium Game", 0);

On trouve plus loin dans le main la boucle de jeu :

while (1)
{
	if (!SDL_PollEvent(&v11)) {
		// Affichage [...]
	}
	if (v11 == 12)
		goto LABEL_17;
	if (v11 == 2)
		break;
 
	LABEL_6: // Gestion des inputs
		sub_4074EC(v4, (int)&v11);
}

On peut se référer au code source de SDL pour comprendre les codes renvoyés par SDL_PollEvent :

  SDL_KEYDOWN = 2
  SDL_QUIT = 12

Dans la fonction 0x4074EC utilisée pour gérer les inputs, des opérations sont effectuées en fonction de l'input choisi. Beaucoup de code est utilisé pour faire avancer notre snake, mais on remarque aussi que selon l'input, on écrit dans une structure de 4000 octets allouée en début de fonction.

switch (v12) // key
{
	[...]
	case 276:
		*(_BYTE *)(*(_DWORD *)(a1 + 16) + 54016) |= 4u;
	break;
	case 275:
		*(_BYTE *)(*(_DWORD *)(a1 + 16) + 54016) |= 8u;
	break;
	case 274:
		*(_BYTE *)(*(_DWORD *)(a1 + 16) + 54016) |= 2u;
	break;
	case 273:
		*(_BYTE *)(*(_DWORD *)(a1 + 16) + 54016) |= 1u;
	break;
	[...]
}
 
[...]
v3 = *(_BYTE *)(*(_DWORD *)(a1 + 16) + 54016);
if ( (_BYTE)v3 != -1 )
{
    v4 = dword_414024;
    *(_DWORD *)(dword_414024 + 4 * dword_414020) = v3;
    v5 = dword_414020 + 1;
    dword_414020 = v5;
    dword_414BE0 = 0;
}

Juste à la suite, on remarque une cascade de if imbriqué qui lit notre structure. En connaissant l'état de la structure attendue, on peut reconstruire le tableau de bytes qui est écrit :

if ( v5 > 0 )
{
 v7 = v4 + 28;
 v6 = 0;
 do
 {
  if ( *(_DWORD *)(v7 - 28) == 254 )
  {
   if ( *(_DWORD *)(v7 - 24) == 254 )
   {
    if ( *(_DWORD *)(v7 - 20) == 251 )
    {
     if ( *(_DWORD *)(v7 - 16) == 254 )
     {
      if ( *(_DWORD *)(v7 - 12) == 253 )
      {
       if ( *(_DWORD *)(v7 - 8) == 247 )
       {
        if ( *(_DWORD *)(v7 - 4) == 253 )
        {
         if ( *(_DWORD *)v7 == 251 )
         {
          if ( *(_DWORD *)(v7 + 4) == 247 )
          {
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15108) = 71; // 71
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15109) = *(_BYTE *)(v7 - 24) + 81; // 254 + 81
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15110) = 12048 / *(_DWORD *)(v7 - 20); // 12048 / 251
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15111) = *(_DWORD *)(v7 - 8) ^ 0xB3; // 247 ^ 0xB3
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15112) = 23845 / *(_DWORD *)v7; // 23845 / 251
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15113) = *(_BYTE *)(v7 + 4) + 83; // 253 + 83
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15114) = 48; // 48
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15115) = *(_BYTE *)(v7 - 4) + 64 - *(_BYTE *)v7; // 253 + 64 - 251
           *(_BYTE *)(*(_DWORD *)(a1 + 16) + 15116) = *(_BYTE *)(v7 + 4) ^ 0xD6; // 247 ^ 0xD6
          }
         }
        }
       }
      }
     }
    }
   }
  }
  ++v6;
  dword_414BE0 = v6;
  v7 += 4;
 }
while ( v5 != v6 );
}

On peut écrire un bout de code pour le décodage :

unsigned char buffer[1024] = {0};
buffer[0] = 71; // 71
buffer[1] = 254 + 81; // 254 + 81
buffer[2] = 12048 / 251; // 12048 / 251
buffer[3] = 247 ^ 0xB3; // 247 ^ 0xB3
buffer[4] = 23845 / 251; // 23845 / 251
buffer[5] = 253 + 83; // 253 + 83
buffer[6] = 48; // 48
buffer[7] = 253 + 64 - 251; // 253 + 64 - 251
buffer[8] = 247 ^ 0xD6; // 247 ^ 0xD6
printf("buffer = %s\n", buffer);

Qui nous donne :

  buffer = GO0D_P0B!

Ce flag ne validant pas (?), on reconnaît néanmoins la similitude avec le flag GO0D_J0B! qui lui permet de valider l'épreuve.

pwnium2k14_reverse2.1404656209.txt.gz · Dernière modification: 2017/04/09 15:33 (modification externe)