当前位置: 代码迷 >> 综合 >> BugKuCtf-逆向-Mountain climbing
  详细解决方案

BugKuCtf-逆向-Mountain climbing

热度:38   发布时间:2023-12-04 06:09:56.0

PEID查询有壳

使用UPX脱壳机DLL 脱壳

使用IDA打开

shit+F12 查看字符串

看到error字样,交叉引用到相关函数。

反编译 查看到该函数的如下代码

int sub_411B70()
{int v0; // edx@1int v1; // ecx@1int v2; // edx@5int v3; // ecx@5signed int v4; // eax@5int v5; // edx@8int v6; // ecx@8int v7; // edx@15int v8; // ecx@15int v9; // edx@17int v10; // ecx@17int v11; // edx@18int v12; // ecx@18char v14; // [sp+Ch] [bp-154h]@1int v15; // [sp+D0h] [bp-90h]@9int j; // [sp+DCh] [bp-84h]@3int i; // [sp+E8h] [bp-78h]@1char flag[104]; // [sp+F4h] [bp-6Ch]@7unsigned int v19; // [sp+15Ch] [bp-4h]@1int savedregs; // [sp+160h] [bp+0h]@1memset(&v14, 0xCCu, 0x154u);v19 = (unsigned int)&savedregs ^ __security_cookie;srand(0xCu);sub_411127(v1, v0);j_memset(&unk_423D80, 0, 0x9C40u);for ( i = 1; i <= 20; ++i ){for ( j = 1; j <= i; ++j ){rand();v4 = sub_411127(v3, v2);*(&dword_41A138[100 * i] + j) = v4 % 100000;}}print("input your key with your operation can get the maximum:");sub_411249("%s", flag);if ( j_strlen(flag) == 19 )                   // flag长度为19{sub_41114F((int)flag);                      // 第一个函数处理v15 = 0;j = 1;i = 1;dword_423D78 += dword_41A138[101];while ( v15 < 19 ){if ( flag[v15] == 'L' ){dword_423D78 += *(&dword_41A138[100 * ++i] + j);}else{if ( flag[v15] != 'R' ){print("error\n");system("pause");sub_411127(v8, v7);goto LABEL_18;}dword_423D78 += *(&dword_41A138[100 * ++i] + ++j);}++v15;}print("your operation can get %d points\n", dword_423D78);system("pause");sub_411127(v10, v9);}else{print("error\n");system("pause");sub_411127(v6, v5);}
LABEL_18:sub_411280(&savedregs, dword_411DF8);sub_411294();return sub_411127(v12, v11);
}

乍一看很简单,输入长度为19的字符串,字符串里的L与R作为每次的抉择,然后得到某个与i j有关的值,i每次+1,j每次可以++或者不+。值是用随机数生成的,但随机数种子固定,所以每次生成的是一样的。

尝试输入LLLLLLLLLLLLLLLLLLL,却提示error,难道输入的不是L或R吗?

那应该与其他几个函数有关,尤其注意sub41114F

使用OD进行动态调试

在IDA中找到该函数地址 00411C8B                 call    sub_41114F

在OD中对该地址下断点,跟随进入

直到在堆栈或者寄存器出现我们输入的字符串被修改的字样,再停止并进行观察

对该循环运行几遍,即可知道它将双数字符进行了与4异或。所以我们只要将每个双数字符与4异或一下即可变成正常的值

知道H与4异或是L,V与4异或是R

所以尝试输入LHLHLHLHLHLHLHLHLHL

得到该值,然后与一开始的函数中的 得到最大分数提示知道,就是不同的LR得到的分数不同,要得到最高的分数。

所存的数组在这里

for ( i = 1; i <= 20; ++i ){for ( j = 1; j <= i; ++j ){rand();v4 = sub_411127(v3, v2);*(&dword_41A138[100 * i] + j) = v4 % 100000;}}while ( v15 < 19 ){if ( flag[v15] == 'L' ){dword_423D78 += *(&dword_41A138[100 * ++i] + j);

在堆栈里观察了一下,因为是dword,间隔100就是间隔400字节。所以数据比较多。

但是在下面的while循环观察一下,循环最后一次i的值是19*100,所以只需要i从1到19的数据即可。

修改for循环里的100*i为19*i 。即0x190改为0x4c 因为dword每个数字占据4字节

然后从内存里拷贝数据出来使用Python脚本处理

import re
file_name='reverse.txt'
a=[]
b=[]
with open(file_name) as file_obj:for line in file_obj:line=line.rstrip().split(" ")[-1]#以空格为间隔 分割字符串line=line[4:]#取分割后的第四个往后的line='0x'+line#便于转化成16进制a.append(line)
del a[-1]#去除后面两个不正常的项
del a[-1]for i in a:b.append(int(i,16))
print (b)

再就是算法问题,使用C++递归写

#include<iostream>
#include <string>
using namespace std;
string opera;
int max=0;
int num[500]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5628, 6232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29052, 1558, 26150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12947, 29926, 11981, 22371, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4078, 28629, 4665, 2229, 24699, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27370, 3081, 18012, 24965, 2064, 26890, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21054, 5225, 11777, 29853, 2956, 22439, 3341, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31337, 14755, 5689, 24855, 4173, 32304, 292, 5344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15512, 12952, 1868, 10888, 19581, 13463, 32652, 3409, 28353, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26151, 14598, 12455, 26295, 25763, 26040, 8285, 27502, 15148, 4945, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26170, 1833, 5196, 9794, 26804, 2831, 11993, 2839, 9979, 27428, 6684, 0, 0, 0, 0, 0, 0, 0, 0, 4616, 30265, 5752, 32051, 10443, 9240, 8095, 28084, 26285, 8838, 18784, 6547, 0, 0, 0, 0, 0, 0, 0, 7905, 8373, 19377, 18502, 27928, 13669, 25828, 30502, 28754, 32357, 2843, 5401, 10227, 0, 0, 0, 0, 0, 0, 22871, 20993, 8558, 10009, 6581, 22716, 12808, 4653, 24593, 21533, 9407, 6840, 30369, 2330, 0, 0, 0, 0, 0, 3, 28024, 22266, 19327, 18114, 18100, 15644, 21728, 17292, 8396, 27567, 2002, 3830, 12564, 1420, 0, 0, 0, 0, 29531, 21820, 9954, 8319, 10918, 7978, 24806, 30027, 17659, 8764, 3258, 20719, 6639, 23556, 25786, 11048, 0, 0, 0, 3544, 31948, 22, 1591, 644, 25981, 26918, 31716, 16427, 15551, 28157, 7107, 27297, 24418, 24384, 32438, 22224, 0, 0, 12285, 12601, 13235, 21606, 2516, 13095, 27080, 16331, 23295, 20696, 31580, 28758, 10697, 4730, 16055, 22208, 2391, 20143, 0, 16325, 24537, 16778, 17119, 18198, 28537, 11813, 1490, 21034, 1978, 6451, 2174, 24812, 28772, 5283, 6429, 15484, 29353, 5942};
int dfs(int i,int j,int sum,string str){if(i==20){if(sum>max){max=sum;opera=str;}return 1;}int sum1=sum+num[i*19+j];dfs(i+1,j,sum1,str+'L');int sum2=sum+num[i*19+j];dfs(i+1,j+1,sum2,str+'R');
}int main(){dfs(1,1,0,"");cout<<max;cout<<opera;return 1;
}

运行得到结果

将双数位置的字符 'L'改成'H','R'改成'V'即可。