这题主要考察选手动态调试。扔进exeinfope,32位exe,无壳
image.png
main程序,点进sub_4017D0看
image.png
sub_4017D0,可以看出是检测进程名称。找到for之前的汇编,尝试在动调的时候修改eip直接jmp到CloseHandle,又或者是自己patch一下,防止return 1,从而反反调试。patch教程参考链接
(留意这里的dword_406120,之后会讲)
image.png
sub_401930是个SEH异常反调试,可以参考源码
image.png
直接查看汇编,参考刚刚上面的源码,反调试的E9刚好起到了花指令的作用,变成了jmp near ptr 8B762D3Ah。在jmp按一下U,在E9下面按c转为代码,然后再把E9 nop掉。
image.pngimage.png
没红色了那就F5一下,按两下进sub_401960(再留意一下dword_406120)
image.pngimage.png
返回到主函数的汇编,发现藏起来了一个函数sub_401FB0
image.png
又或者用shift+f12找到关键字符,对着Format按一下x键,找到调用的地方,然后再F5一下
image.png
找到了验证函数,先来看_Initialize_parallel_init_info函数
image.png
发现了NtSetInformationThread,这整个函数是隐藏线程防止调试用的,参考链接,直接eip修改过就行
(再留意一下dword_406120)
image.png
再看主函数里面的加密函数sub_401CD0,可看出来应该是做了加密的函数,解密后动态调用
image.pngimage.png
发现是用之前看到的dword_406120解密的,当反调试函数都过了,让dword_406120+=了21,21,14,即dword_406120(下面改成了unk_406120)为56时才解密成功,从而调用函数。
右键Jump in a new hex window ->找到15h右键Edit->改成38(即十进制56)再右键Apply changes。
image.pngimage.pngimage.png
成功解码之后再慢慢步入进加密函数,当中加了花指令,花指令可参考安全客
image.png
我出题的时候发现有时候jnz没跳转,所以又加了test eax, eax
所以整个花指令模式如下

  1. __asm{
  2. test eax, eax
  3. __emit(0x75) //jnz $+4
  4. __emit(0x02)
  5. __emit(0xE9) //干扰IDA
  6. __emit(0xED)
  7. }

这个花指令和上面是同一个,用的E9,参考上面的做法patch一下,把E9,ED nop掉
image.pngimage.png
在最上面的push ebp右键 create function,成功建立函数后就可以F5了,图片这个实际上就是异或运算,都是按位运算,试一下就会发现是异或
image.png
以此类推,发现运算分别为加减乘除异或和取余66666(十六进制0x1046A,小彩蛋),其中除1等于没效果,取余66666还是调用了rand,所以还原加密过程的时候还是要保留
image.pngimage.pngimage.pngimage.png
可以看出可以单字节爆破,还原一次加密过程,再爆破就得到flag了(导出加密的数据不细说了,shift+e)

  1. #include<stdlib.h>
  2. #include<stdio.h>
  3. #include<iostream>
  4. #define rand100 rand()%100+1
  5. const char enflag[] = {
  6. 0x0C,0x17,0x80,0x40,0x29,0x34,0x0C,0x29,0x28,0xA1,
  7. 0x3A,0x80,0x82,0x1D,0x00,0x18,0xC3,0xCA,0x10,0x2E,
  8. 0xD3,0x21,0x48,0xA5,0x3A,0x99,0xFB,0x46,0x0F,0xC6,
  9. 0x78
  10. };
  11. void gen_rand(){
  12. srand(0);
  13. for(int i=0;i<31;i++){
  14. if(i%2==0)printf("\n");
  15. for(int j=0;j<5;j++){
  16. printf("0x%02X,",rand100);
  17. }
  18. }
  19. }
  20. const char random_list[] = {
  21. 0x27,0x14,0x27,0x26,0x38,0x62,0x42,0x56,0x33,0x0D,
  22. 0x36,0x01,0x2B,0x52,0x26,0x16,0x2E,0x56,0x62,0x51,
  23. 0x4D,0x5C,0x38,0x07,0x3A,0x18,0x52,0x29,0x1A,0x4F,
  24. 0x2F,0x5B,0x29,0x58,0x08,0x26,0x0C,0x12,0x39,0x44,
  25. 0x22,0x4F,0x18,0x58,0x62,0x55,0x0D,0x0C,0x4F,0x43,
  26. 0x1E,0x05,0x50,0x06,0x59,0x32,0x1E,0x4D,0x20,0x41,
  27. 0x0F,0x25,0x1D,0x03,0x35,0x05,0x26,0x39,0x63,0x49,
  28. 0x62,0x0E,0x54,0x04,0x3D,0x2B,0x30,0x4C,0x48,0x05,
  29. 0x4A,0x35,0x14,0x05,0x28,0x57,0x05,0x26,0x18,0x24,
  30. 0x22,0x5E,0x15,0x4B,0x54,0x3E,0x19,0x42,0x46,0x1F,
  31. 0x44,0x25,0x32,0x25,0x14,0x1C,0x01,0x18,0x17,0x4B,
  32. 0x0C,0x3F,0x42,0x5C,0x14,0x30,0x33,0x15,0x23,0x45,
  33. 0x19,0x4E,0x2F,0x20,0x3B,0x49,0x1F,0x23,0x52,0x24,
  34. 0x44,0x3D,0x0F,0x2B,0x4D,0x1C,0x18,0x5F,0x45,0x2D,
  35. 0x19,0x16,0x08,0x61,0x1B,0x40,0x29,0x3F,0x30,0x51,
  36. 0x30,0x1D,0x0E,0x54,0x3C
  37. };
  38. char encode(int input,int i){
  39. input ^= random_list[i*5];
  40. input += random_list[i*5+1];
  41. input *= random_list[i*5+2];
  42. input -= random_list[i*5+3];
  43. input %= 66666;
  44. return input&0xFF;
  45. }
  46. void brute(){
  47. for(int i=0;i<31;i++){
  48. for(int j=33;j<125;j++){
  49. if(encode(j,i) == enflag[i]){
  50. printf("%c",j);
  51. //break;
  52. }
  53. }
  54. printf("x");
  55. }
  56. }
  57. int main(){
  58. brute();
  59. }

我出题出的比较烂,直接多解算了
image.png
MOCSCTF{cRa0k_M3_1s_s0_Ea3y!!!}
MOCSCTF{CRa0k_M3_1s_s0_Ea3y!!!}
MOCSCTF{CRA0k_M3_1s_s0_Ea3y!!!}
MOCSCTF{cRA0k_M3_1s_s0_Ea3y!!!}
image.png

后记

(更新于2021.03.16)做了一题VNCTF确实下次可以弄个md5限制一下多解,例如这题套个md5就能防止多解