# Phân tích phần mềm chấm thi FineLinux

<p>FineLinux là phần mềm chấm thi môn "Linux và phần mềm mã nguồn mở" dành cho sinh viên đại học Thuỷ Lợi</p><p>Hôm nay trong buổi test thử phần mềm để chuẩn bị cho bài thi online cuối môn, tôi đã có dịp phân tích thử phần mềm này xem mức độ bảo mật của nó tới đâu, liệu có thể bị sinh viên gian lận trong quá trình thi không.</p><p>Đầu tiên sau khi tải phần mềm về và giải nén, tôi nhận được 2 file như sau:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628827998/13212122-3334-4383-9503-cb5ee411ee97.png" class="kg-image" alt loading="lazy" width="620" height="82" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image.png 600w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628827998/13212122-3334-4383-9503-cb5ee411ee97.png 620w"></figure><p>File exe chỉ có 40kb, trông hơi khả nghi, dù sao thì tôi cũng sẽ vứt nó vào Sandboxie kèm với Fiddler proxy debugger xem liệu có capture được http request nào thú vị hay không.</p><p>Ban đầu, phần mềm hiển thị 1 form đăng nhập như sau:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628830321/3e37a2d7-c9e4-4378-9664-f368582c18d3.png" class="kg-image" alt loading="lazy" width="500" height="232"></figure><p>Sau khi điền thông tin và bấm đăng nhập, tôi nhận được kết quả như sau (thông tin sinh viên chỉ mang tính chất tham khảo, không phải thông tin thật)</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628832565/63fee3bd-9cf7-427e-afdc-5453e6089a58.png" class="kg-image" alt loading="lazy" width="705" height="435" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-4.png 600w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628832565/63fee3bd-9cf7-427e-afdc-5453e6089a58.png 705w"></figure><p>Request nhận được ở Fiddler:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628834884/a8ce7de9-15f6-4882-85cf-a74e18dd54e7.png" class="kg-image" alt loading="lazy" width="730" height="592" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-5.png 600w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628834884/a8ce7de9-15f6-4882-85cf-a74e18dd54e7.png 730w"></figure><p>Tôi nhấn thử "Xem đề trắc nghiệm", phần mềm hiển thị 1 form như sau:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628836590/472ad562-e5e1-4d2c-a58e-f81da4354016.png" class="kg-image" alt loading="lazy" width="798" height="471" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-6.png 600w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628836590/472ad562-e5e1-4d2c-a58e-f81da4354016.png 798w"></figure><p>Vì mục đích thử nghiệm nên tôi đánh thử vài câu rồi chọn "Chấm trắc nghiệm"</p><p>Phần mềm gửi lên 1 request như sau:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628839046/e7ff9e56-3413-492d-a5e2-910a5e43f421.png" class="kg-image" alt loading="lazy" width="675" height="288" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-7.png 600w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628839046/e7ff9e56-3413-492d-a5e2-910a5e43f421.png 675w"></figure><p>Con số 163 trông rất bí ẩn, vì vậy tôi quyết định decompile phần mềm này. Vì vừa mở lên có thể nhận thấy đấy là phần mềm viết bằng C# Winform nên tôi đã mở nó bằng phần mềm JustDecompile.</p><p>Sau một hồi tìm quanh thì tôi thấy vài điều khá thú vị</p><p>Đầu tiên là giải thích cho con số 163, đây là hàm gửi request:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628840896/9a7ed54e-ccc6-4216-8b12-39b420dd97ce.png" class="kg-image" alt loading="lazy" width="1334" height="571" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-8.png 600w, __GHOST_URL__/content/images/size/w1000/2021/06/image-8.png 1000w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628840896/9a7ed54e-ccc6-4216-8b12-39b420dd97ce.png 1334w" sizes="(min-width: 1200px) 1200px"></figure><p>Vậy con số 163 này được tạo thành bởi giá trị làm tròn của <strong>mark * heso</strong>, là 2 biến double được truyền vào hàm.</p><p>Trace theo lời gọi hàm, ta thấy hàm này được truyền vào các tham số sau:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628843394/0d44b293-1a16-4457-98e2-5f61542e29f1.png" class="kg-image" alt loading="lazy" width="1421" height="389" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-9.png 600w, __GHOST_URL__/content/images/size/w1000/2021/06/image-9.png 1000w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628843394/0d44b293-1a16-4457-98e2-5f61542e29f1.png 1421w" sizes="(min-width: 1200px) 1200px"></figure><p>Có vẻ <strong>heso</strong> là 1 giá trị tĩnh nào đấy, tìm phần khai báo của nó ta được kết quả sau:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628845843/00b1af08-53a8-4321-9cc0-ea2d17127037.png" class="kg-image" alt loading="lazy" width="790" height="42" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-10.png 600w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628845843/00b1af08-53a8-4321-9cc0-ea2d17127037.png 790w"></figure><p>heso[1] = 7</p><p>heso[2] = 3</p><p>(về sau này tôi nhận ra rằng đây là hệ số của phần trắc nghiệm và điền khuyết, trắc nghiệm chiếm 7 phần và điền khuyết chiếm 3 phần trong tổng số điểm)</p><p>Tiếp theo là biến <strong>num</strong>, nhìn lại lời gọi hàm ở trên ta thấy biến <strong>num</strong> được tính bằng hàm <strong>qf.Score</strong>. Tiếp tục trace hàm này:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628848130/238f0a38-d209-4218-823a-cb3288fa250f.png" class="kg-image" alt loading="lazy" width="530" height="161"></figure><p>Ta thấy hàm for qua các câu hỏi (0 -&gt; <strong>answer.Length</strong>, mảng này chứa các checkbox đáp án của mỗi câu) và gọi hàm <strong>CheckAnswer2</strong>. Trace tiếp hàm này:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628850441/467d6aef-306e-476d-b386-a3824e1b6b53.png" class="kg-image" alt loading="lazy" width="517" height="470"></figure><p>Chỗ này mặc dù tôi có nháp ra và đã hiểu nhưng tôi nghĩ nó hơi khó giải thích, đại loại việc nó làm là như sau:</p><p>- For qua các checkbox của câu hỏi thứ k</p><p>- Nếu checkbox thứ <strong>num1</strong> được chọn, check xem trong đáp án có phương án này hay không, bằng cách convert số <strong>num1</strong> ra thành chữ cái tương ứng với checkbox (<strong>"ABCDEF"[num1]</strong>) rồi  check index. Nếu có thì <strong>num += 1</strong>. Nếu không có =&gt; sv chọn sai, 0 điểm (return <strong>length = 0</strong>)</p><p>- Cuối cùng gán lại length = Số lựa chọn đúng / Tổng số đáp án đúng (<strong>num / num / (double)this.QB[k][0].Length</strong>)</p><p>- Trả về length là điểm thành phần của câu đó</p><p>Có thể đọc xong cái giải thích cái hàm <strong>CheckAnswer2</strong> này bạn vẫn chả hiểu gì hết. Vậy thôi, với mục đích gian lận thì ta có một cách nghĩ đơn giản hơn. Cùng nhìn lại hàm Score:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628852328/65c872fd-c7ca-42d4-a86a-9a7311c2dd93.png" class="kg-image" alt loading="lazy" width="530" height="161"></figure><p><strong>double length = num * 100 / (double)((int)this.answer.Length);</strong></p><p><strong>- num</strong> là tổng số điểm thành phần. Trả lời đúng mỗi câu bạn sẽ được 1 điểm</p><p><strong>- this.answer.Length</strong> là số câu hỏi</p><p>Nghĩa là tổng số điểm bài trắc nghiệm của bạn sẽ là <strong>tổng số điểm thành phần / số câu hỏi * 100</strong> =&gt; Max là 100 điểm</p><p>Hệ số điểm của bài trắc nghiệm là 7 =&gt; Điểm gửi lên server sẽ là <strong>tổng số điểm * 7</strong>.</p><p>Quay lại với con số 163. Ta chỉ cần lấy <strong>163 / 7</strong> sẽ biết được số điểm tổng = <strong>23.28</strong> ~ <strong>23.3 trên 100</strong></p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628854829/cb5ed855-d15c-487c-b8c8-533af856e32b.png" class="kg-image" alt loading="lazy" width="706" height="431" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-16.png 600w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628854829/cb5ed855-d15c-487c-b8c8-533af856e32b.png 706w"></figure><p>Vậy để được max số điểm, ta chỉ cần sửa con số 163 này thành 700</p><p>Tương tự với phần điền khuyết, ta sửa params thành part2=300 để được số điểm max</p><p>Ngoài ra, ta còn có thể xem giá trị biến <strong>QB </strong>để lấy dữ liệu ngân hàng đề và đáp án, cách này đơn giản và hiệu quả, không cần phân tích nhiều:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674628856753/422eb035-3c77-424e-8c18-ad0758c1edff.png" class="kg-image" alt loading="lazy" width="1562" height="283" srcset="__GHOST_URL__/content/images/size/w600/2021/06/image-17.png 600w, __GHOST_URL__/content/images/size/w1000/2021/06/image-17.png 1000w, https://cdn.hashnode.com/res/hashnode/image/upload/v1674628856753/422eb035-3c77-424e-8c18-ad0758c1edff.png 1562w" sizes="(min-width: 1200px) 1200px"></figure>
