# IcthyoDescription:
Long before stegosaurus roamed the earth, another [species](icthyo) prowled the sea; here is an artist's [rendition](out.png).
Type `strings icthyo` we saw some interesting things```...%s must be 256 x 256%s must be 8 bit depth%s must be RGBmessage (less than 256 bytes): USAGE: %s in.png out.png...```We guess it will hide some message in the `out.png` using `in.png`
We created a `white.png` with size of 256 x 256 to test the function```bash./icthyo white.png sample.pngmessage (less than 256 bytes): hello```It just prompt for the message to hide and exit
We open this in Ghidra and decompile it, the main function:```cint main(int iParm1,undefined8 *puParm2)
{  time_t tVar1;   if (iParm1 != 3) {    printf("USAGE: %s in.png out.png\n",*puParm2);                    /* WARNING: Subroutine does not return */    exit(1);  }  tVar1 = time((time_t *)0x0);  srand((uint)tVar1);  read_file(puParm2[1]);  encode();  write_file(puParm2[2]);  return 0;}```Look at the encode function looks complicated:```cvoid encode(void)
{  char cVar1;  int iVar2;  byte *pbVar3;  long lVar4;  undefined8 *puVar5;  long in_FS_OFFSET;  int rowValue;  int colValue;  int counter;  undefined8 local_118 [33];  long local_10;  long currentRow;   local_10 = *(long *)(in_FS_OFFSET + 0x28);  lVar4 = 0x20;  puVar5 = local_118;  while (lVar4 != 0) {    lVar4 = lVar4 + -1;    *puVar5 = 0;    puVar5 = puVar5 + 1;  }  printf("message (less than 256 bytes): ");  fgets((char *)local_118,0x100,stdin);  rowValue = 0;  while (rowValue < 0x100) {    currentRow = *(long *)(rows + (long)rowValue * 8);    colValue = 0;    while (colValue < 0x100) {      pbVar3 = (byte *)(currentRow + (long)(colValue * 3));      iVar2 = rand();      *pbVar3 = (byte)iVar2 & 1 ^ *pbVar3;      iVar2 = rand();      pbVar3[1] = pbVar3[1] ^ (byte)iVar2 & 1;      iVar2 = rand();      pbVar3[2] = pbVar3[2] ^ (byte)iVar2 & 1;      colValue = colValue + 1;    }    counter = 0;    while (counter < 8) {      pbVar3 = (byte *)(currentRow + (long)(counter * 0x60));      cVar1 = *(char *)((long)local_118 + (long)rowValue);      if ((pbVar3[2] & 1) != 0) {        pbVar3[2] = pbVar3[2] ^ 1;      }      pbVar3[2] = pbVar3[2] |                  (byte)((int)cVar1 >> ((byte)counter & 0x1f)) & 1 ^ (pbVar3[1] ^ *pbVar3) & 1;      counter = counter + 1;    }    rowValue = rowValue + 1;  }  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {                    /* WARNING: Subroutine does not return */    __stack_chk_fail();  }  return;}```Basically it prompt the message to hide:```cprintf("message (less than 256 bytes): ");fgets((char *)local_118,0x100,stdin);```And add 1 or minus 1 to every column in every row:```cwhile (colValue < 0x100) {      pbVar3 = (byte *)(currentRow + (long)(colValue * 3));      iVar2 = rand();      *pbVar3 = (byte)iVar2 & 1 ^ *pbVar3;      iVar2 = rand();      pbVar3[1] = pbVar3[1] ^ (byte)iVar2 & 1;      iVar2 = rand();      pbVar3[2] = pbVar3[2] ^ (byte)iVar2 & 1;      colValue = colValue + 1;}```And hide the message in specific coordinates in the output image:```ccounter = 0;while (counter < 8) {  pbVar3 = (byte *)(currentRow + (long)(counter * 0x60));  cVar1 = *(char *)((long)local_118 + (long)rowValue);  if ((pbVar3[2] & 1) != 0) {    pbVar3[2] = pbVar3[2] ^ 1;  }  pbVar3[2] = pbVar3[2] |              (byte)((int)cVar1 >> ((byte)counter & 0x1f)) & 1 ^ (pbVar3[1] ^ *pbVar3) & 1;  counter = counter + 1;}```After we do some research for how C reads the image,## Reference[A simple libpng example program](http://zarb.org/~gc/html/libpng.html)
We realize `pbVar3` is the RGB value in the pixel which means:```cpbVar3[0] //is RpbVar3[1] //is GpbVar3[2] //is B```According to the code:```cif ((pbVar3[2] & 1) != 0) {		//if B is not even     pbVar3[2] = pbVar3[2] ^ 1;	//make it even}```And the most complicated line here:```cpbVar3[2] = pbVar3[2] | (cVar1 >> (counter & 0x1f)) & 1 ^ (pbVar3[1] ^ pbVar3) & 1;````cVar1` is the message char follow the current `rowValue````ccVar1 = *(char *)((long)local_118 + (long)rowValue);```And the `cVar1` shift right number of `counter`:```c// If cVar1 is 'h'// h in binary is 01101000// shift right 101101000 -> 00110100// shift right 201101000 -> 00011010```The `counter` start with `0` and loop until `7`
That means the `(cVar1 >> (counter & 0x1f))` is equal to `cVar1 >> counter`
Because `counter` doing bitwise to 0x1f has no effect if the value is 0 to 7
So the process looks like this:```c// If cVar1 is 'h'// h in binary is 01101000cVar1 >> 0	-> 01101000cVar1 >> 1	->  0110100cVar1 >> 2	->   011010cVar1 >> 3	->    01101cVar1 >> 4	->     0110cVar1 >> 5	->      011cVar1 >> 6	->       01cVar1 >> 7	->        0```After that it perform bitwise AND for 1
Means if the last bit is `1` the result is `1`, if `0` the result is `0````ccVar1 >> 0	-> 01101000 & 1 = 0cVar1 >> 1	->  0110100 & 1 = 0cVar1 >> 2	->   011010 & 1 = 0cVar1 >> 3	->    01101 & 1 = 1cVar1 >> 4	->     0110 & 1 = 0cVar1 >> 5	->      011 & 1 = 1cVar1 >> 6	->       01 & 1 = 1cVar1 >> 7	->        0 & 1 = 0```If you arrange the result, you will see is exactly the same as the binary:```	       0	      0	     0	    1	   0	  1	 1+	0------------	01101000------------```But after that, the result will XOR with `(pbVar3[1] ^ pbVar3) & 1`
Which means if `(pbVar3[1] ^ pbVar3)` last bit is `1` and result is `1` then `1 XOR 1 = 0````c1 ^ 1 = 0	//if cVar1 >> counter = 1 and (pbVar3[1] ^ pbVar3) = 11 ^ 0 = 1	//if cVar1 >> counter = 1 and (pbVar3[1] ^ pbVar3) = 00 ^ 1 = 1	//if cVar1 >> counter = 0 and (pbVar3[1] ^ pbVar3) = 10 ^ 0 = 0	//if cVar1 >> counter = 0 and (pbVar3[1] ^ pbVar3) = 0```Lastly it perform OR bitwise with the result:```c//if pbVar3[2] (B) is 255if ((pbVar3[2] & 1) != 0) {		//if B last bit is 1    pbVar3[2] = pbVar3[2] ^ 1;	//B last bit become 0, 255 - 1 = 254}pbVar3[2] = 254 | 0 = 254pbVar3[2] = 254 | 1 = 255```Summarize the equation:```cB = B | message & 1 ^ (G ^ R) & 1```It got four possible:```c1 & 1 ^ 1 & 1 = B add 0 means B is even or last bit is 01 & 1 ^ 0 & 1 = B add 1 means B is odd or last bit is 10 & 1 ^ 1 & 1 = B add 1 means B is odd or last bit is 10 & 1 ^ 0 & 1 = B add 0 means B is even or last bit is 0```Our target is to find message
Simplify the equation:```cB = message ^ (G ^ R)message = B ^ (G ^ R)``` So if we know the RGB value, we can find the message!
I wrote a [python script](solve.py) that uses PIL to get all the pixel RGB value
And we get the flag!!!```bashpython solve.py actf{lurking_in_the_depths_of_random_bits}```
## Flag> actf{lurking_in_the_depths_of_random_bits}