# TSGCTF2020 ONNXRev Writeup
Netron is not useful in this problem.
First of all, we need a way to edit onnx file in order to exploit its internal neural network. Onnx files are saved in protobuf format, so we can use a general way to edit protobuf files. The module `google.protobuf.json_format` supports conversions from/to JSON format.
to JSON:```:import onnximport google.protobuf.json_format as json_format
model = onnx.load("problem.onnx")str = json_format.MessageToJson(model)print(str)```
from JSON:```:import onnximport google.protobuf.json_format as json_format
with open("modified.json") as f:    str = f.read()
model = json_format.Parse(str, model)onnx.save(model, "problem_mod.onnx")```
Now we can look into the internal graphs of `model.onnx`. Reading carefully, we can see what the model actually does. It's something like the following pseudo code:
```:coeff_matrix[41][41] = **embedded**target_coeffs[41] = **embedded**ok = truefor i from 0 until 41:	accum = 0	for j from 0 until 41:		chara = trim_image_at_nth_char(input, i)		id = run_nn_to_get_char_id(chara)		accum += id * coeff_matrix[i][mod(i - j, 41)]	ok = ok && accum == target_coeffs[i]
return ok ? "Correct" : "Wrong"```
It's just a matrix multiplication! By extracting matrix elements (don't forget to shear `coeff_matrix`), we can guess the return values of `run_nn_to_get_char_id` for each character. And this is it:
```71, 54, 32, 39, 71, 5, 91, 17, 40, 12, 33, 22, 31, 27, 22, 9, 22, 90, 52, 12, 73, 22, 56, 65, 22, 51, 42, 43, 75, 9, 40, 86, 22, 10, 22, 7, 14, 52, 40, 90, 77
T = 71S = 54G = 32C = 39T = 71F = 5{ = 91? = 17? = 40? = 12? = 33? = 22? = 31? = 27? = 22? = 9? = 22? = 90? = 52? = 12? = 73? = 22? = 56? = 65? = 22? = 51? = 42? = 43? = 75? = 9? = 40? = 86? = 22? = 10? = 22? = 7? = 14? = 52? = 40? = 90} = 77```
This is **not** character codes, it's just character class ids to which the neural network classify characters. And what is worse, it is scrambled. So the only way to restore the flag is to run the neural network actually for each ascii character.
Now we need to edit the model. We modified JSON so that the model writes the last input character's class id:
```:coeff_matrix[41][41] = **embedded**target_coeffs[41] = **embedded**for i from 0 until 41:	accum = 0	for j from 0 until 41:		chara = trim_image_at_nth_char(input, i)		id = run_nn_to_get_char_id(chara)		accum = id	result = accum
return result```
Running this model several times, we finally get the class id table:
```87 = !64 = "50 = #0  = $11 = %92 = &37 = '23 = (6  = )18 = *53 = +15 = ,34 = -19 = .66 = /56 = 031 = 124 = 244 = 39  = 442 = 584 = 67  = 769 = 845 = 921 = :21 = ;63 = <62 = =47 = >1  = ?83 = @25 = A79 = B39 = C61 = D89 = E5  = F32 = G78 = H10 = I72 = J70 = K75 = L4  = M12 = N17 = O58 = P60 = Q68 = R54 = S71 = T85 = U57 = V8  = W74 = X46 = Y41 = Z55 = [76 = \35 = ]38 = ^22 = _88 = `30 = a81 = b82 = c73 = d51 = e65 = f86 = g14 = h52 = i16 = j90 = k28 = l80 = m40 = n43 = o2  = p36 = q48 = r27 = s26 = t49 = u67 = v29 = w33 = x3  = y59 = z91 = {20 = |77 = }```
Now it's easy to restore the flag: `TSGCTF{OnNx_1s_4_kiNd_0f_e5oL4ng_I_7hink}`