FCSC 2023 : Des ptits trous
Task
You are given a bunch of IBM029 perforated cards in the form of a jpeg like this one. And we know that out of the 79 cards and only the first 53 are in the right order.
Solve
To solve this, we first have to decode it into a readable form according to the documentation of those punched cards.
Image to binary format
We have to figure out a way to determine for each (x,y) if it is punched or not. To do so we determine the delta between each row and column and then measure the value at that point of the image :
X_START = 55
Y_START = 56
fname = './cards/'+f'{5:0>10}.jpg'
img = Image.open(fname).convert('L')
pixels = img.load()
for x in np.arange(X_START,X_START+29*31,10.88):
for y in np.arange(Y_START,Y_START+31*12,31.1):
pixels[int(x),int(y)] = 50
img.show()
It works ! We notice upon execution that every sampled pixel is aligned with the punching emplacements so we can now move onto the decoding.
Binary format to text
The data is read column by column and in each of them is encoded a character. So we create a dictionary of character to column association and a function that enables to do the conversion :
punchchar = {'001000000000':'0',
'000100000000':'1',
'000010000000':'2',
'000001000000':'3',
...
'000001000010':'#',
'010000000000':'-',
'100000000000':'&',
'001100000000':'/',}
def getchar(input):
try :
#print(f'{input}=>{punchchar[input]}')
return punchchar[input]
except :
#print(f'{input}')
return ' '
Now that we have that we can iterate through all the images and all the columns to convert into text:
data = ""
for i in range(1,80):
fname = './cards/'+f'{i:0>10}.jpg'
img = Image.open(fname).convert('L')
pixels = img.load()
for x in np.arange(X_START,X_START+29*30,10.88):
b = ""
for y in np.arange(Y_START,Y_START+31*12,31.1):
b += "0" if pixels[x,y] < 128 else "1"
data += getchar(b)
data += '\n'
print(data)
The result of the decoding is this :
--------------------------------------------------
MODULE HELLO_1
BEGIN S1,S2,H,SS DEFINITION
--------------------------------------------------
INTEGER :: S1(256) = (/ 96,172,121,222,15,140,53,104,39,145,51,250,217, &
27,32,127,70,179,21,46,236,189,143,133,77,171,208,223,113,139,158,54,203,227,&
160, &
95,99,155,85,169,103,130,238,31,226,41,52,90,152,183,8,173,72,131,229,231,243,&
184, &
199,146,134,6,249,4,117,84,151,201,47,25,180,33,79,230,166,142,10,161,233,7,&
112, &
255,126,19,138,193,83,125,168,106,24,48,198,177,209,2,56,185,108,16,200,65, &
186, &
225,12,167,137,105,98,43,62,150,147,38,149,251,49,234,119,212,29,86,129,110,&
93, &
204,9,34,74,73,176,120,195,67,205,123,196,244,175,241,102,245,162,248,218, &
232,219, &
59,28,197,87,170,221,57,92,214,37,247,116,100,26,239,107,216,188,148,22,&
60,192, &
13,23,80,91,44,66,42,153,18,40,76,165,220,206,144,115,82,114,20,253,202,&
174,215, &
163,211,78,124,228,11,0,1,97,58,35,128,61,14,159,17,132,94,178,252,182,&
240,111, &
141,71,187,235,213,154,63,190,3,45,75,191,135,207,101,88,118,181,89,164,&
50,36,55, &
246,81,254,242,30,210,194,237,157,136,224,69,109,156,122,64,5,68 /)
INTEGER :: S2(256) = (/ 216,182,122,143,69,3,117,72,42,134,53,75,179, &
49,27,189,26,30,254,78,83,139,194,237,60,93,70,105,109,240,178,46,158,210,&
193,24, &
172,141,23,156,234,31,220,62,145,127,4,51,19,176,247,255,111,81,55,135,71,&
0,177, &
38,211,155,166,90,181,224,202,195,137,221,43,170,201,159,230,44,108,196,&
133,132, &
183,144,225,120,129,147,121,164,136,45,157,1,186,115,13,206,68,217,252,&
251,233, &
95,59,184,187,241,37,113,98,25,162,235,118,47,79,35,80,50,89,250,192,229,&
110,56, &
58,185,40,150,253,205,191,87,231,124,223,198,173,160,142,222,239,226,190,&
168,167, &
174,207,21,140,76,28,52,152,100,14,16,48,163,92,33,197,154,238,161,15,84,&
116,11, &
12,73,232,128,215,67,204,8,209,20,96,17,200,188,9,214,104,131,91,64,99,18,&
61,249, &
203,153,138,36,103,5,39,22,151,227,41,165,219,246,101,74,236,65,218,180,148,&
123, &
242,85,57,29,169,63,54,146,245,7,77,106,32,208,126,149,175,94,212,130,114,6,&
125, &
213,112,119,66,244,102,82,10,97,248,86,107,243,34,228,171,2,199,88 /)
INTEGER :: H(30) = (/ 17,10,1,18,25,28,27,14,22,20,4,8,15,5,26,19,6,12, &
7,21,3,29,13,23,9,24,0,16,11,2 /)
INTEGER :: SS(30) = (/ 68,180,51,31,68,20,206,229,56,160,219,251,169, &
184,56,229,206,66,160,186,51,153,83,68,56,157,160,68,56,187 /)
END MODULE
END S1,S2,H,SS DEFINITION (END HELLO_1)
--------------------------------------------------
901 FORMAT (99A)
DO I = 0,29,1
- - - - - -
DO I = 0,29,1
CHARACTER :: SSSS(0:255)
INTEGER :: SSS(0:255)
END DO
- - - - - -
END PROGRAM
PROGRAM HELLO
SSSS(I:I) = CHAR(SSS(I:I))
SSS(I:I) = S2(SS(I+1)+1:SS(I+1)+1)
INTEGER :: I
USE HELLO_1
END DO
--------------------------------------------------
WRITE (*,901,ADVANCE=\YES\) \\
STOP 0
DO I = 0,29,1
IMPLICIT NONE
DO I = 0,29,1
SS(H(I+1)+1:H(I+1)+1) = SSS(I:I)
WRITE (*,901,ADVANCE=\NO\) SSSS(I:I)
SSS(I:I) = S1(SS(I+1)+1:SS(I+1)+1)
END DO
END DO
Solving the fortran puzzle
The code is complete but as it was said in the beginning it is in part not in the right order. And it appears that it starts being scrambled right after the HELLO_1 module. This part :
901 FORMAT(99A)
DO I=0,29,1
DO I=0,29,1
CHARACTER::SSSS(0:255)
INTEGER::SSS(0:255)
END DO
END PROGRAM
PROGRAM HELLO
SSSS(I:I)=CHAR(SSS(I:I))
SSS(I:I)=S2(SS(I+1)+1:SS(I+1)+1)
INTEGER::I
USE HELLO_1
END DO
WRITE(*,901,ADVANCE=\YES\)\\
STOP 0
DO I=0,29,1
IMPLICIT NONE
DO I=0,29,1
SS(H(I+1)+1:H(I+1)+1)=SSS(I:I)
WRITE(*,901,ADVANCE=\NO\) SSSS(I:I)
SSS(I:I)=S1(SS(I+1)+1:SS(I+1)+1)
END DO
END DO
There are 4 for loops and 4 assignements. So from this, through logic we can know what comes first in what order. For example, we know fore sure that the assignment of SSSS cannot come before SSS. And we can build the code back piece by piece this way just like a jigsaw puzzle.
I threw the module out the window and made it into one program but you should get this :
PROGRAM HELLO
IMPLICIT NONE
INTEGER::S1(256)=(/96,172,121,222,15,140,53,104,39,145,51,250,217,&
27,32,127,70,179,21,46,236,189,143,133,77,171,208,223,113,139,158,54,203,227,&
160,&
95,99,155,85,169,103,130,238,31,226,41,52,90,152,183,8,173,72,131,229,231,243,&
184,&
199,146,134,6,249,4,117,84,151,201,47,25,180,33,79,230,166,142,10,161,233,7,&
112,&
255,126,19,138,193,83,125,168,106,24,48,198,177,209,2,56,185,108,16,200,65,&
186,&
225,12,167,137,105,98,43,62,150,147,38,149,251,49,234,119,212,29,86,129,110,&
93,&
204,9,34,74,73,176,120,195,67,205,123,196,244,175,241,102,245,162,248,218,&
232,219,&
59,28,197,87,170,221,57,92,214,37,247,116,100,26,239,107,216,188,148,22,&
60,192,&
13,23,80,91,44,66,42,153,18,40,76,165,220,206,144,115,82,114,20,253,202,&
174,215,&
163,211,78,124,228,11,0,1,97,58,35,128,61,14,159,17,132,94,178,252,182,&
240,111,&
141,71,187,235,213,154,63,190,3,45,75,191,135,207,101,88,118,181,89,164,&
50,36,55,&
246,81,254,242,30,210,194,237,157,136,224,69,109,156,122,64,5,68/)
INTEGER::S2(256)=(/216,182,122,143,69,3,117,72,42,134,53,75,179,&
49,27,189,26,30,254,78,83,139,194,237,60,93,70,105,109,240,178,46,158,210,&
193,24,&
172,141,23,156,234,31,220,62,145,127,4,51,19,176,247,255,111,81,55,135,71,&
0,177,&
38,211,155,166,90,181,224,202,195,137,221,43,170,201,159,230,44,108,196,&
133,132,&
183,144,225,120,129,147,121,164,136,45,157,1,186,115,13,206,68,217,252,&
251,233,&
95,59,184,187,241,37,113,98,25,162,235,118,47,79,35,80,50,89,250,192,229,&
110,56,&
58,185,40,150,253,205,191,87,231,124,223,198,173,160,142,222,239,226,190,&
168,167,&
174,207,21,140,76,28,52,152,100,14,16,48,163,92,33,197,154,238,161,15,84,&
116,11,&
12,73,232,128,215,67,204,8,209,20,96,17,200,188,9,214,104,131,91,64,99,18,&
61,249,&
203,153,138,36,103,5,39,22,151,227,41,165,219,246,101,74,236,65,218,180,148,&
123,&
242,85,57,29,169,63,54,146,245,7,77,106,32,208,126,149,175,94,212,130,114,6,&
125,&
213,112,119,66,244,102,82,10,97,248,86,107,243,34,228,171,2,199,88/)
INTEGER::H(30)=(/17,10,1,18,25,28,27,14,22,20,4,8,15,5,26,19,6,12,&
7,21,3,29,13,23,9,24,0,16,11,2/)
INTEGER::SS(30)=(/68,180,51,31,68,20,206,229,56,160,219,251,169,&
184,56,229,206,66,160,186,51,153,83,68,56,157,160,68,56,187/)
CHARACTER::SSSS(0:255)
INTEGER::SSS(0:255)
INTEGER::I
DO I=0,29,1
SSS(I:I)=S1(SS(I+1)+1:SS(I+1)+1)
END DO
DO I=0,29,1
SS(H(I+1)+1:H(I+1)+1)=SSS(I:I)
END DO
WRITE(*,901,ADVANCE="YES")
DO I=0,29,1
SSS(I:I)=S2(SS(I+1)+1:SS(I+1)+1)
END DO
DO I=0,29,1
SSSS(I:I)=CHAR(SSS(I:I))
WRITE(*,901,ADVANCE="NO") SSSS(I:I)
END DO
STOP 0
901 FORMAT(99A)
END PROGRAM
Try it with an online fortran compiler and finally here we go it works. we get the flag.
FCSC{#!F0RTR4N_1337_FOR3V3R!}STOP 0
...Program finished with exit code 0
Press ENTER to exit console.
And voilà ! It was a very fun challenge it felt like being an archeologue.