刮开有奖

刮开有奖

用 ida 打开,shift + F12 查看字符串列表,看到有一个字符串 “U g3t 1T!” ,有点像 “u got it!” 的感觉,可能是需要的信息,点进去,然后选中,Ctrl + x 交叉引用列表,f5 来到源代码

img

自下而上分析:
(1)输出 “U g3t 1T!” 的话,要满足 if 判断,if 判断里面,要求 String[0]、String[1]、String[2]、v4 和 v5 同时满足对应条件
(2)其中 v5 是数组 v18 经过 sub_401000 函数处理后的值,v18 的值又是分别等于 String[3]、String[2]、String[4]
(3)同时 v4 也是数组 v18 经过 sub_401000 函数处理后的值,这时 v18 的值又是分别等于 String[5]、String[7]、String[6]
(4)然后看到是对 v7 的处理,通过函数 sub_4010F0,在往上是对一系列变量的赋值

总体来说,就是通过 GetDlgItemTextA 获取了某个值之后,如果 String 长度为 8 时候,进行 (4)、(3)、(2) 的操作,然后如果通过了 (1) 的判断,最后输出 “U g3t 1T!”

下面先看对 v7 的处理,v7 的值为 ‘ZJ’,调用了 sub_4010F0(v7, 0, 10);,进去发现是这样的代码,如下

img

(_DWORD *)是强制类型转化,将值转换为指向 DWORD 的指针

经常看汇编的会觉得比较熟悉,尝试将其变得更易于理解

int __cdecl sub_4010F0(int a1[], int a2, int a3) // v7, 0, 10
//int __cdecl sub_4010F0(int a1, int a2, int a3)
{
    int result; // eax
    int i; // esi
    int v5; // ecx
    int v6; // edx

    result = a3; // result=10
    for ( i = a2; i <= a3; a2 = i ) // i=0, i<=10
    {
        v5 = i;//v5 = 4 * i;
        v6 = a1[i];//v6 = *(_DWORD *)(4 * i + a1);
        if ( a2 < result && i < result )
        {
            do
            {
                if ( v6 > a1[result] )//if ( v6 > *(_DWORD *)(a1 + 4 * result) )
                {
                    if ( i >= result )
                        break;
                    ++i;
                    a1[v5] = a1[result];// *(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + 4 * result);
                    if ( i >= result )
                        break;
                    while ( a1[i] <= v6 )// while ( *(_DWORD *)(a1 + 4 * i) <= v6 )
                    {
                        if ( ++i >= result )
                            goto LABEL_13;
                    }
                    if ( i >= result )
                        break;
                    v5 = i;//v5 = 4 * i;
                    a1[result] = a1[i];// *(_DWORD *)(a1 + 4 * result) = *(_DWORD *)(4 * i + a1);
                }
                --result;
            }
            while ( i < result );
        }
LABEL_13:
        a1[result] = v6;//*(_DWORD *)(a1 + 4 * result) = v6;
        sub_4010F0(a1, a2, i - 1);
        result = a3;
        ++i;
    }
    return result;
}

改完之后,可以发现一个问题,函数调用时候传参是 sub_4010F0(v7, 0, 10);,因此 v7 这个地址传入之后,它 v7[result] 寻址很明显超了 v7 的正常数组大小。
这个时候观察原本的 v7 - v16,发现它们的地址是连续的,因此可以判断,其实整个 sub_4010F0(v7, 0, 10) 的调用,是对 v7 - v16 整体进行操作,只不过可能 ida 在反编译时候,没有将这个整体识别成一个数组,我们可以用快捷键 Y 自行修改,这里就懒得改了。

虽然这个 sub_4010F0() 的操作没有看懂,但是影响不大,直接跑一轮看看结果

#include <iostream>
using namespace std;
int __cdecl sub_4010F0(int a1[11], int a2, int a3)
{
    int result; // eax
    int i; // esi
    int v5; // ecx
    int v6; // edx

    result = a3; // result=10
    for (i = a2; i <= a3; a2 = i) // i=0, i<=10
    {
        v5 = i;//v5 = 4 * i;
        v6 = a1[i];//v6 = *(_DWORD *)(4 * i + a1);
        if (a2 < result && i < result)
        {
            do
            {
                if (v6 > a1[result])//if ( v6 > *(_DWORD *)(a1 + 4 * result) )
                {
                    if (i >= result)
                        break;
                    ++i;
                    a1[v5] = a1[result];// *(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + 4 * result);
                    if (i >= result)
                        break;
                    while (a1[i] <= v6)// while ( *(_DWORD *)(a1 + 4 * i) <= v6 )
                    {
                        if (++i >= result)
                            goto LABEL_13;
                    }
                    if (i >= result)
                        break;
                    v5 = i;//v5 = 4 * i;
                    a1[result] = a1[i];// *(_DWORD *)(a1 + 4 * result) = *(_DWORD *)(4 * i + a1);
                }
                --result;
            } while (i < result);
        }
    LABEL_13:
        a1[result] = v6;//*(_DWORD *)(a1 + 4 * result) = v6;
        sub_4010F0(a1, a2, i - 1);
        result = a3;
        ++i;
    }
    return result;
}
int main()
{
    int v7[11];
    v7[0] = 90;
    v7[1] = 74;
    v7[2] = 83;
    v7[3] = 69;
    v7[4] = 67;
    v7[5] = 97;
    v7[6] = 78;
    v7[7] = 72;
    v7[8] = 51;
    v7[9] = 110;
    v7[10] = 103;
    for (int i = 0; i < 11; i++)
    {
        printf("%c", v7[i]);
    }
    printf("\n");
    sub_4010F0(v7, 0, 10);
    for (int i = 0; i < 11; i++)
    {
        printf("%c", v7[i]);
    }
    printf("\n");
    return 0;
}

结果如下
ZJSECaNH3ng
3CEHJNSZagn

下面看 sub_401000 函数,点进去之后如下

img

乍一看挺复杂的,但是观察一开始的处理,这里参数 v2 是字符的 len 长度,先判断 %3 是否为 0,从这个点上面,可能可以联想到 base64 的 %3 的操作,因为 base64 处理时候就是按照 3 bite 进行分组的。
往后看的话,在 byte_407830 这个数组里面,确实是找到了 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/这个特征,所以直接猜测 sub_401000 就是一个 base64 的处理操作

所以把 “ak1w”、“V1Ax” 解一下得到 jMp、WP1

现在回来看这个if判断

if ( String[0] == v7[0] + 34
        && String[1] == v10
        && 4 * String[2] - 141 == 3 * v8
        && String[3] / 4 == 2 * (v13 / 9)
        && !strcmp(v4, "ak1w")
        && !strcmp(v5, "V1Ax") )

结合

        v9[0] = String[5];
        v9[2] = String[7];
        v9[1] = String[6];
        v4 = (const char *)sub_401000(v9, strlen(v9));
        memset(v9, 0, 0xFFFFu);
        v9[1] = String[3];
        v9[0] = String[2];
        v9[2] = String[4];

那么每个 String 都可以解出来了

编写解密脚本

#include <iostream>
using namespace std;

int main()
{
    string v7_s = "3CEHJNSZagn";
    int v7[11] = {0};
    for (int i = 0; i < v7_s.length(); i++)
    {
        v7[i] = int(v7_s[i]);
    }

    int String[8];
    String[0] = v7[0] + 34;
    String[1] = v7[4];
    String[2] = (3 * v7[2] + 141) / 4;
    String[3] = 2 * (v7[7] / 9) * 4;
    string v9 = "WP1";
    String[4] = int(v9[2]);
    v9 = "jMp";
    String[5] = int(v9[0]);
    String[6] = int(v9[1]);
    String[7] = int(v9[2]);
    for (int i = 0; i < 8; i++)
    {
        printf("%c", String[i]);

    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值