小菜鸟和小远不得不说的故事二:隐藏的right!之探秘Base64编码

6627 3
小菜鸟一枚 2022-4-12 20:53:59 | 显示全部楼层 |阅读模式
人物介绍:
小菜鸟:是个萌新
大佬远:是个黑阔

今天,小菜鸟苦思冥想,觉得肯定是因为字符串是直接写的,被大老远发现了,于是去学习了base64编码,首先利用万能的百度搜索相关知识,得到如下知识:
1Base64使用A--Z,a--z,0--9,+,/ 64个字符.
2)编码原理:3个字节转换成4个字节( (3 X 8) = 24 = (4 X 6) )先读入3个字节,每读一个字节,左移8,再右移四次,每次6,这样就有4个字节了.
3)解码原理:4个字节转换成3个字节.先读入46(用或运算),每次左移6,再右移3,每次8.这样就还原了.
于是小菜鸟又开始了编写程序。
1)桌面新建文件,还是命名为xcn,打开vscode,打开xcn文件夹,然后新建一个main.cpp文件。
2)然后新建一个json文件,运行配置用的,一会要在cmd窗口输入数据,点添加配置,按模板自动生成,然后根据情况进行修改:
3)接下来苦思冥想编写代码:
#include <iostream>
#include <cstring>

using namespace std;

int encodeBase64(const char *str, int strLen, char *cpystr, int cpystrLen)
{
        //读取3个字节zxc,转换为二进制01111010 01111000 01100011
        //转换为4个6位字节,011110 100111 100001 100011
        //不足8位在前补0,变成00011110 00100111 00100001 00100011
        //再将4个字节转换为十进制,30 39 33 35
        //通过下标找到码表中对应的,e n h j
        //zxc加密后的密文就为,enhj
        //接下来第二轮加密,只剩1个字母v,转换成二进制01110110
        //转换成2个6位字节,011101 100000(10不足6位末尾补0)
        //不足8位在前补0,变成 00011101 00100000
        //转换成十进制,29 32
        //通过下标找到码表中对应的d g,还缺两个才构成四字节,这时补等于号即可
        //v加密后的密文为dg==
        //最后整个明文被base64加密完成,得到密文enhjdg==
        char base64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//定义base64码表
        int i = 0;//明文下标
        int j = 0;//密文下标
        while (i < strLen)//判断是否加密完毕
        {
                if (j < cpystrLen - 1)//判断下标是否超过密文可存储范围
                {
                        if (i + 3 <= strLen)//读取到3个字节
                        {
                                cpystr[j] = base64[str >> 2];//取第1个字节的前6位,将其加密为base64[]码表中对应的字符
                                cpystr[j + 1] = base64[(str & 3) << 4 | (str[i + 1] >> 4)];//取第一个字节的后2位再加上第二个字节的前4位,将其加密为base64[]码表中对应的字符
                                cpystr[j + 2] = base64[(str[i + 1] & 15) << 2 | (str[i + 2] >> 6)];//取第二个字节的后4位再加上第三个字节的前2位,将其加密为base64[]码表中对应的字符
                                cpystr[j + 3] = base64[str[i + 2] & 63];//取第三个字节的后6位,将其加密为base64[]码表中对应的字符
                        }
                        else if (i + 2 <= strLen)//读取到2个字节
                        {
                                cpystr[j] = base64[str >> 2];//取第1个字节的前6位,将其加密为base64[]码表中对应的字符
                                cpystr[j + 1] = base64[(str & 3) << 4 | (str[i + 1] >> 4)];//取第一个字节的后2位再加上第二个字节的前4位,将其加密为base64[]码表中对应的字符
                                cpystr[j + 2] = base64[(str[i + 1] & 15) << 2];//取第二个字节的后4位再加上第三个字节的前2位,将其加密为base64[]码表中对应的字符
                                                                                                                                                                   //末尾补一个等于号
                                cpystr[j + 3] = '=';
                        }
                        else if (i + 1 <= strLen)//只读取到1个字节
                        {
                                cpystr[j] = base64[str >> 2];//取第1个字节的前6位,将其加密为base64[]码表中对应的字符
                                cpystr[j + 1] = base64[(str & 3) << 4];//取第一个字节的后2位再加上第二个字节的前4位,将其加密为base64[]码表中对应的字符
                                                                                                                                                          //末尾补两个等于号
                                cpystr[j + 2] = '=';
                                cpystr[j + 3] = '=';
                        }
                        else
                        {
                                cout << "为什么循环还在执行,程序逻辑错误!!!" << endl;
                                return 1;
                        }
                        i += 3;//明文每次循环向后移动3位
                        j += 4;//密文每次循环向后移动4位
                }
                else
                {
                        cout << "存储密文的内存不足,请重新分配!!!" << endl;
                        return 2;
                }
        }
        cpystr[j] = '\0';//补一个字符串结束符
        return 0;
}

int decodeBase64(const char *cpystr, int cpystrLen, char *str, int strLen)
{
        //解密就是将加密的过程倒过来即可!

        char base64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//定义base64码表

        int i = 0;//密文下标
        int j = 0;//明文下标

        while (i < cpystrLen)//判断是否解密完毕
        {
                int index = 0;//码表下标
                int temp1 = -1;//保存第一个密文下标
                int temp2 = -1;//保存第二个密文下标
                int temp3 = -1;//保存第三个密文下标
                int temp4 = -1;//保存第四个密文下标

                if (j < strLen - 1)//判断下标是否超过明文可存储范围
                {
                        //通过码表反查密文对应的下标,然后分别获得十进制数字
                        for (; index < 64; index++) {
                                if (cpystr == base64[index])
                                {
                                        temp1 = index;
                                }
                                if (cpystr[i + 1] == base64[index])
                                {
                                        temp2 = index;
                                }
                                if (cpystr[i + 2] == base64[index])
                                {
                                        temp3 = index;
                                }
                                if (cpystr[i + 3] == base64[index])//一轮密文4个字节已取出,退出循环
                                {
                                        temp4 = index;
                                }

                                //如果已经查到四个下标就退出循环!
                                if (temp1 != -1 && temp2 != -1 && temp3 != -1 && temp4 != -1) break;
                        }

                        if (temp3 != -1 && temp4 != -1)//完整的读取到了4个字节,直接解密
                        {
                                str[j] = (temp1 << 2) | (temp2 >> 4);//取第一个密文6个字符再加上第二个密文前2个字符
                                str[j + 1] = ((temp2 & 15) << 4) | (temp3 >> 2);//取第二个密文后4个字符再加上第三个密文前4个字符
                                str[j + 2] = ((temp3 & 3) << 6) | temp4;//取第三个密文后2个字符再加上第四个密文6个字符
                        }
                        else if (temp3 == -1)//只取到了2个字节
                        {
                                str[j] = (temp1 << 2) | (temp2 >> 4);//取第一个密文6个字符再加上第二个密文前2个字符
                                j -= 2;//下标前移2位,不然补‘\0’的位置不对
                        }
                        else if (temp4 == -1)//只取到了3个字节
                        {
                                str[j] = (temp1 << 2) | (temp2 >> 4);//取第一个密文6个字符再加上第二个密文前2个字符
                                str[j + 1] = ((temp2 & 15) << 4 | (temp3 >> 2));//取第二个密文后4个字符第三个密文前4个字符
                                j--;//下标前移1位,不然补‘\0’的位置不对
                        }
                        else
                        {
                                cout << "下标取值不对,程序逻辑错误!!!" << endl;
                                return 1;
                        }

                        i += 4;//密文每次循环向后移动4位
                        j += 3;//明文每次循环向后移动3位
                }
                else
                {
                        cout << "存储明文的内存不足,请重新分配!!!" << endl;
                        return 2;
                }
        }

        str[j] = '\0';//补一个字符串结束符
        return 0;
}

int main(int argc, char *argv[])
{
    /* 小菜鸟的第一个 C++ 程序 cmlnaHQh*/
    char cpystr[20];
    char str[20];
    int cpystrLen = 0;
    int strLen = 20;

    cout<<"Please input a password:"<<endl;
    cin>>cpystr;

    cpystrLen = strlen(cpystr);

    decodeBase64(cpystr, cpystrLen, str, strLen);

    cout<<str<<endl;

    return 0;

}

4)按下Ctrl+Shift+B生成exe程序,按下Ctrl+F5运行程序,编译通过。
5)输入cmlnaHQh,输出right!,嘿嘿,成功了!


小菜鸟拿着程序找到大佬远,看,我写的程序,输入密码就能输出right!,厉害不。这次你要是不知道密码,连right!都输出不了,有本事你再来试试。大佬远翻翻白眼,就这,等着,于是几分钟以后小菜鸟傻眼了,大佬远轻轻松松的得到了密码。
大佬远干了这事:
1)将程序拖进吾爱破解OD工具,下载地址:https://down.52pojie.cn/Tools/Debuggers/%E5%90%BE%E7%88%B1%E7%A0%B4%E8%A7%A3%E4%B8%93%E7%94%A8%E7%89%88Ollydbg.rar
2)然后在主界面右键-》中文搜索引擎-》智能搜索
3)看到了一行字符串ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/,会心一笑,原来是base64啊,先不轻举妄动,在提示输入密码的那一行回车打上断点,输入abcd
4)可以看到拿到abcd字符串后进行了一个长度判断,接着进行了一系列的赋值操作,接着调用了4016E0函数,没有看到push参数传递,那么就肯定不是堆栈传参而是寄存器传参了,利用esp寄存器传参,F7进去这个call,可以看到先初始化了base64码表,接着不断的左移,右移运算,典型base64的编码的特征:
004016EC  |.  BB 98604000   mov ebx,main.00406098                             ;  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

0040182C  |.  C1E0 02       |shl eax,0x2

00401834  |.  C1F8 04       |sar eax,0x4

0040184A  |.  C1E0 04       |shl eax,0x4

00401852  |.  C1F8 02       |sar eax,0x2


5)执行完这段代码后,回来发现是i?说明这可能是解码函数,也就是将输入的字符串进行base64解码,最终得到right,本来到这里拿着字符串加解密网站base64编码就得到密码了,但是我们会看中文搜索的时候有两个地方出现了base64码表,是不是一个编码一个解码用的呢?
6)重新加载程序,输入right!,在调用call main.004016E0的时候将它改成00401410,让他执行完这个函数,我们再来看看,是不是得到了cmlnaHQh密码呢。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
shadow虚空幻影 2022-6-27 20:15:34 来自手机 | 显示全部楼层
这程序。。是一个刚百度的人能掌握的吗?(挑刺)
一个普通人 2022-6-27 21:32:07 来自手机 | 显示全部楼层
这故事怎么感觉有点奇怪?这是想表达什么
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行