经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
CatFly【汇编代码还原】
来源:cnblogs  作者:yuanyy720  时间:2023/12/22 16:20:21  对本文有异议

CatFly【难度:1】

题目界面

下载附件,发现是dll文件,放到linux中运行一下,运行界面如图所示:

从上图中可以看到两处字符串,上面的字符串不断滚动,下方字符串在次数上不断累加,猜测上方字符串与flag相关。

静态调试

  • 打开IDA,找到main函数

    方便分析,此处只粘贴关键部分代码(源代码的后半部分)

    1. time(&timer);
    2. v13 = 1;
    3. v24 = 0LL;
    4. v23 = 0;
    5. v22 = 0;
    6. v12 = off_FA88;
    7. while ( v13 )
    8. {
    9. if ( dword_E104 )
    10. printf("\x1B[H");
    11. else
    12. printf("\x1B[u");
    13. for ( k = dword_E1EC; k < dword_E1F0; ++k )
    14. {
    15. for ( m = dword_E1F4; m < dword_E1F8; ++m )
    16. {
    17. if ( k <= 23 || k > 42 || m >= 0 )
    18. {
    19. if ( m >= 0 && (unsigned int)k <= 0x3F && m <= 63 )
    20. {
    21. v19 = off_FA20[v24][k][m];
    22. off_FA88 = sub_6314((unsigned int)v24, k, m, (__int64)v12);
    23. }
    24. else
    25. {
    26. v19 = 44;
    27. }
    28. }
    29. else
    30. {
    31. v18 = (2 - m) % 16 / 8;
    32. if ( ((v24 >> 1) & 1) != 0 )
    33. v18 = 1 - v18;
    34. s[128] = (__int64)",,>>&&&+++###==;;;,,";
    35. v19 = asc_BFE3[v18 - 23 + k];
    36. if ( !v19 )
    37. v19 = 44;
    38. }
    39. if ( v25 )
    40. {
    41. printf("%s", *((const char **)&unk_FCC0 + v19));
    42. }
    43. else if ( v19 == v22 || !*((_QWORD *)&unk_FCC0 + v19) )
    44. {
    45. printf("%s", off_FA88);
    46. }
    47. else
    48. {
    49. v22 = v19;
    50. printf("%s%s", *((const char **)&unk_FCC0 + v19), off_FA88);
    51. }
    52. }
    53. sub_65E2(1LL);
    54. }
    55. if ( dword_E100 )
    56. {
    57. time(&time1);
    58. v11 = difftime(time1, timer);
    59. v10 = sub_63FF((unsigned int)(int)v11);
    60. for ( n = (dword_E1FC - 29 - v10) / 2; n > 0; --n )
    61. putchar(32);
    62. dword_E1E8 += printf("\x1B[1;37mYou have nyaned for %d times!\x1B[J\x1B[0m", (unsigned int)++dword_108E0);
    63. }
    64. v22 = 0;
    65. ++v23;
    66. if ( dword_104C4 && v23 == dword_104C4 )
    67. sub_6471();
    68. if ( !off_FA20[++v24] )
    69. v24 = 0LL;
    70. usleep(1000 * v27);
    71. }
    72. return 0LL;

    因为flag可能与屏幕上滚动的字符串有关,所以此处需要格外关注printf函数。从上述关键代码中,可以看到off_FA88最可能与flag有关,因为其他的printf函数打印的基本是一个确定的值。

  • 查看目标的交叉引用路径

    确定了off_FA88后,开始查找与其相关的函数。

    • 第一处是469行中的对off_FA88的赋值操作

      off_FA88的值是sub_6314()函数的返回值。

    • 接下来进入sub_6314()函数

      函数伪代码如下图所示:

      其功能为:当a2!=18,a3≤4||a3>54时off_FA88不变(v12的值就是off_FA88),所以此处相当于是k=18(一次)且m∈(4,53)时会执行sub_6314函数,即循环执行50次。该函数的返回值是byte_104C8的地址,byte_104C8的值与dword_E120[a3-5],dword_E120[a3-5]的值又与sub_62B5()函数的返回值有关。这里还有个sub_62E3作为逻辑判断的条件。将此处逻辑进行还原,还原代码如下:

      1. for (int i = 0; i < 50; i++) {
      2. dword_E120[i] ^= sub_62B5();
      3. }
    • 查看sub_62B5()函数

      函数伪代码如下图所示:

      可以看到该函数的返回值与dword_E1E8相关,查看dword_E1E8的交叉引用,可以看到只在main函数中有一次自增操作:

    • 总结整个流程

  • 还原关键汇编代码

    此处使用大佬wp给的代码进行分析,基本都给了解析:

    1. #include<stdio.h>
    2. #include<string.h>
    3. //在ida里面点开dword_E1E8会发现初始值为0x1106
    4. int dword_E1E8 = 0x1106;
    5. //同理,ida里也能看到dword_E120的初始值
    6. int dword_E120[50] = { 0x27fb, 0x27a4, 0x464e, 0x0e36, 0x7b70, 0x5e7a, 0x1a4a, 0x45c1, 0x2bdf, 0x23bd, 0x3a15, 0x5b83, 0x1e15, 0x5367, 0x50b8, 0x20ca, 0x41f5, 0x57d1, 0x7750, 0x2adf, 0x11f8, 0x09bb, 0x5724, 0x7374, 0x3ce6, 0x646e, 0x010c, 0x6e10, 0x64f4, 0x3263, 0x3137, 0x00b8, 0x229c, 0x7bcd, 0x73bd, 0x480c, 0x14db, 0x68b9, 0x5c8a, 0x1b61, 0x6c59, 0x5707, 0x09e6, 0x1fb9, 0x2ad3, 0x76d4, 0x3113, 0x7c7e, 0x11e0, 0x6c70 };
    7. //原封不动抄下来就好了
    8. int sub_62B5()
    9. {
    10. dword_E1E8 = 1103515245 * dword_E1E8 + 12345;
    11. return (dword_E1E8 >> 10) & 0x7FFF;
    12. }
    13. //这块是拿来算输出数字n需要多少个字符的。
    14. //因为main函数中的dword_E1E8需要接收printf函数的返回值
    15. //而printf函数的返回值是打印的字符长度
    16. int llog(int n) {
    17. int a = 0;
    18. while (n /= 10)a++;
    19. return a;
    20. }
    21. //这个函数我没有特别放出来,反正这里也是照抄的
    22. int sub_62E3(char a1)
    23. {
    24. int result; // rax
    25. if ((a1 & 0x7Fu) <= 0x7E)
    26. result = (a1 & 0x7Fu) > 0x20;
    27. else
    28. result = 0LL;
    29. return result;
    30. }
    31. int main() {
    32. //count代表那个不停自增的dword_108E0
    33. int count = 0;
    34. while (1) {
    35. for (int i = 0; i < 50; i++) {
    36. dword_E120[i] ^= sub_62B5();
    37. }
    38. count++;
    39. //计算printf的返回值,更改dword_E1E8,每10倍增加一个位数
    40. dword_E1E8 += 42 + llog(count);
    41. if (count % 1000000 == 0) {
    42. printf("Count:%d\n", count);
    43. }
    44. //flag代表off_FA88
    45. unsigned char flag[51] = { 0 };
    46. for (int i = 0; i < 50; i++) {
    47. //根据出题人所说,出题时循环次数为705980581,但是线性同余随机数算法出现了循环导致在100427942就出现了flag,若只考虑数组的最低字节,能在100001958得到flag
    48. // Loop: 100427942
    49. // if((dword_E120[i] & 0xff00)){
    50. // break;
    51. // }
    52. // Loop: 100001958
    53. if (!sub_62E3(dword_E120[i])) {
    54. break;
    55. }
    56. flag[i] = dword_E120[i] & 0xff;
    57. }
    58. if (memcmp("CatCTF", flag, 6) == 0) {
    59. puts(flag);
    60. printf("Count:%d\n", count);
    61. break;
    62. }
    63. }
    64. }
  • 运行代码,得到flag

原文链接:https://www.cnblogs.com/yuanyy/p/17921456.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号