• 算法逆向第一篇——简单算法逆向


    本文作者:icq5f7a075d

    Music起~

    “啊门 啊前 一棵葡萄树,啊嫩 啊嫩 绿地刚发芽,蜗牛背着那重重地壳呀,一步一步地往上爬~”

    上周分析勒索病毒(GlobeImposter),笔者感觉自己算法的逆向能力很弱,于是找了些题目练手。本文逆向程序主要来源于《加密与解密实战攻略》,算法将用Python/C++实现。

    既然有了算法逆向1,自然会有2、3、4……叶问说“我要打十个!”,笔者说“我要写十篇!”,甭管行不行,先立Flag!——>文末有福利

    1. 简单的算法逆向

    预计时间:2h

    1.1. 定位关键位置

    先随便输入,弹出了错误提示:

    1.png

    搜索字符串:Invalid!此时已经找到了关键位置。

    2.png

    上下翻找一番,0×00404167就是关键跳转,如果是crackme,那么直接把这里修改为jmp就可以了。而我们要进行算法逆向,就要追踪分析ebx和esi这两个值怎么来的。

    1.2. 算法逆向

    往上回溯,ebx的值来自于下图,过程还是比较简单,对输入name字符串进行计算,得到一个值,以这个值为编号,在表0x4050B8中取值:

    3.png

    往上回溯,esi的值来自于此,也比较简单对输入密码字符串进行计算:

    4.png

    算法逆向源码(python):

    calc_table=[0x19791126,0x19791007,0x11261979,0x10071979,

                0x12345678,0x9abcdef0,0x12123434,0x78787878,

                0xccc6ccc6,0xcc00cc00,0xffefefff,0xddcc5555,

                0x67678787,0xcececbcc,0x778899ab,0x44337766]

    def calc_name(name):

        sum_name=0

        for i  in range(0,len(name)):

            sum_name=sum_name+i*ord(name[i])

        #print hex(sum_name)

        calc_name1=(sum_name&0xFFFF)+((((2*len(name)+0×63)&0xFFFF)<<0×10))

        #print hex(calc_name1)

        calc_name2=calc_name1&0xF

        return calc_name2

    def calc_pass(password):

        str_pass=&#39;0&#39;+password

        edx=0

        for i in range(0,len(str_pass)):

            edx=edx*2

            edx=edx+edx*4

            edx=edx+ord(str_pass[i])-0×30

        return edx

    name_in = raw_input(&quot;Input your name:&quot;)

    pass_in=raw_input(&quot;Input your pass:&quot;)

    print hex(calc_table[calc_name(name_in)])

    print hex(calc_pass(pass_in))

    2. 解方程算法逆向

    预计时间:4h

    本程序虽然叫解方程算法,但是解方程算法只是本程序算法中的一小部分。


    2.1. 定位关键算法

    运行程序,随便输入name和serial,点击check没有任何反应。直接拖入IDA,程序的函数很少,一眼就可以看到,我们要逆向的算法在DialogFunc和sub_401141中:

    5.png

    DialogFunc中流程很简单,检测到Check按钮点击事件就会进入如下的处理过程:

    6.png

    第一次调用GetDlgItemTextA获取输入的Serial,第二次调用GetDlgItemTextA获取输入的Name,对Name的长度进行判断,当Name长度大于5并小于32,则调用sub_401141进行计算,计算结果正确则弹出正确对话框,否则,重新等待用户输入。

    要分析的算法处于sub_401141,第一次参数是Name的长度,第二次参数是Serial的长度,0×403084存放输入的Serial,0×403044存放输入的Name。

    2.2. 算法分析

    (本小节中的代码尽量遵循汇编语句,所以比较凌乱,下小节有整合后的源码)

    首先是一个先循环,倒序取Name中的字符进行计算,结果存放在dword_4030C4中:

    7.png

    这段代码有循环移位操作,需要使用自定义函数实现,在下文中会对移位指令进行介绍,

    本部分源码(C++):

    unsigned ebx=0×1;

    unsigned d_4030c4=0×0;

    char cl;

    for(int i=len_name;i>0;){

            i–;

            unsigned edx=name[i];

            edx=edx ^ ebx;

            edx=edx * ebx;

            ebx+=0×5;

            d_4030c4=d_4030c4 ^ edx;

            d_4030c4=leftrot(d_4030c4,5);

    }

    d_4030c4=0-d_4030c4-1;//这里必须要减去1

    d_4030c4=right_rot(d_4030c4,len_name);

    char b_4030c8=&#39;&#39;;

    对Name的计算完毕之后,对Serial进行计算,但是计算Serial计算是将其分为三部分:&#39;-&#39;字符、&#39;-&#39;字符前的字符,&#39;-&#39;字符后的字符,下图是计算是否存在&#39;-&#39;字符,并取&#39;-&#39;字符前的字符:

    8.png

    这部分源码(C++):

    for(int eax=0;eax!=len_serial;){

            char edx_c=serial[eax];

            eax++;

            if(edx_c==&#39;-&#39;){//Serial中必须有&#39;-&#39;字符

                char al=(char)eax;

                    b_4030c8=–al;

                    eax=len_serial;}

    }

    if(b_4030c8!=0){

            cl=&#39;&#39;;

            cl= b_4030c8;

    }

    else{

            return -1;

    }

    &#39;-&#39;字符前的字符的字符ASCII区间(0x3F,0x5B)

    9.png

    这部分源码(C++):

    int cl_i=(int)cl;

    for(;cl_i!=0;)

    {

            cl_i=cl_i-1;

            char edx_c=serial[cl_i];

            if(edx_c<=0x3F || edx_c>=0x5B)

                    {

                    return -1;}

    }

    计算’-’前的字符,判断得到的值和最初计算Name得到的值是否相同,且Serial中’-’后的字符个数是3

    10.png

    这部分源码(C++):

    cl=b_4030c8;

    cl_i=(int)cl;

    int ebx_2=0;

    for(;cl_i!=0;)

    {

            cl_i=cl_i-1;

            char edx_c=serial[cl_i];

            ebx_2*=0x1A;

            edx_c=serial[cl_i];

            edx_c=edx_c-&#39;x41&#39;;

            ebx_2=ebx_2+int(edx_c);

    }

    if(ebx_2!=d_4030c4)

            {return -1;}        

    int eax=len_serial;

    eax=(eax/0×100)*0×100+(eax%0×100-(int)b_4030c8)%0×100;

    if(eax!=4){

            return -1;

    }

    计算’-’后的3个字符

    11.png

    12.png

    这部分源码(C++):

    int j=(int)b_4030c8;

        char b_4030c9=serial[j+1];

        char b_4030ca=serial[j+2];

        char b_4030cb=serial[j+3];

        eax=(int)b_4030c9;

        eax*=3;

        unsigned d_4030cc=eax;

        eax=0;

        ebx=(int)b_4030c9;

        ebx*=7;

        unsigned d_4030d0=eax-ebx;

        unsigned d_4030d4=(int)b_4030c9;

        d_4030cc=d_4030cc-(int)b_4030ca;

        d_4030d0=(int)b_4030ca*2+d_4030d0;

        d_4030d4=d_4030d4+(int)b_4030ca;

        d_4030cc=d_4030cc+(int)b_4030cb*5;

        d_4030d0=d_4030d0+(int)b_4030cb*7;

        d_4030d4=d_4030d4-(int)b_4030cb*2;

        if(d_4030cc==0×204 && d_4030d0==0×19 &&d_4030d4==0x0D)

        {

                printf(&quot;OK! &quot;);

                return 0;

        }

    2.3. 源码整合

    [size=18.6667px]源码(C++)

    #include <iostream>[/i]

    [i]#include <string.h>[/i]

    [i]using namespace std;[/i]

    [i]//elfbin:0x8501675c[/i]

    [i]char* str_n=&quot;elfbin&quot;;//test [/i]

    [i]unsigned len_n=strlen(str_n);[/i]

    [i]char* str_s=&quot;YWSCVFH-RAC&quot;;//test[/i]

    [i]unsigned len_s=strlen(str_s);[/i]

    [i]unsigned leftrot(unsigned a,int n) //RCL[/i]

    [i]{[/i]

    [i]        unsigned b;[/i]

    [i]        b=(a >> (32 – n)) | (a << n);[/i]

    [i]        return b;[/i]

    [i]}[/i]

    [i]unsigned rightrot(unsigned a,int n)[/i]

    [i]{[/i]

    [i]        unsigned b;[/i]

    [i]        b=(a << (32 – n)) | (a >> n);[/i]

    [i]        return b;[/i]

    [i]}[/i]

    [i]int main(){[/i]

    [i]        //计算name,得到32位数值; [/i]

    [i]        unsigned f_n=0×0;[/i]

    [i]        char cl;[/i]

    [i]        int i=len_n;[/i]

    [i]        int j=1;[/i]

    [i]        for(;i>0;){[/i]

    [i]                i–;[/i]

    [i]                f_n^=(str_n^j)*j;[/i]

    [i]                j+=5;[/i]

    [i]                f_n=leftrot(f_n,5);[/i]

    [i]        }[/i]

    [i]        f_n=0-f_n-1;//这里必须要减去1 ,取反 [/i]

    [i]        f_n=rightrot(f_n,len_n);[/i]

    [i]        //Serial中必须有&#39;-&#39;字符,第一个 字符不能是&#39;-&#39;字符,&#39;-&#39;字符前的字符应该是大写或@ [/i]

    [i]        char l_s=&#39;&#39;;        [/i]

    [i]        for(i=0;i!=len_s;i){[/i]

    [i]                if(str_s==&#39;-&#39;){[/i]

    [i]                        l_s=(char)i;[/i]

    [i]                        i=len_s;[/i]

    [i]                        }[/i]

    [i]                        else[/i]

    [i]                          i++;[/i]

    [i]        }[/i]

    [i]        if(l_s==0){[/i]

    [i]                return -1;[/i]

    [i]        }[/i]

    [i]        for(i=(int)l_s;i!=0;)[/i]

    [i]        {[/i]

    [i]                i–;[/i]

    [i]                if(str_s<=0x3F || str_s>=0x5B)[/i]

    [i]                        {return -1;} [/i]

    [i]        } [/i]

    [i]        //计算Serial, 倒序取&quot;-&quot;字符前的字符,其在倒序字符串中的索引*在字母表中的位置 求和 [/i]

    [i]        int f_s=0;        [/i]

    [i]        for(i=(int)l_s;i!=0;)[/i]

    [i]        {[/i]

    [i]                i–;[/i]

    [i]                f_s*=0x1A;//=26[/i]

    [i]                f_s=f_s+int(str_s)-&#39;x41&#39;;//int(serial)-&#39;x41&#39;,在字母表中的位置 [/i]

    [i]                 } [/i]

    [i]        if(f_s!=f_n) // 计算Serial得到的值等于计算Name得到的值 [/i]

    [i]                {;return -1;} [/i]

    [i]        //serial &#39;-&#39;字符后面还有3个字符 [/i]

    [i]        if((len_s-(int)l_s)!=4){[/i]

    [i]                return -1;[/i]

    [i]        }[/i]

    [i]        //计算Serial后三位 [/i]

    [i]        unsigned A,B,C;[/i]

    [i]        char a=str_s[(int)l_s+1];[/i]

    [i]        char b=str_s[(int)l_s+2];[/i]

    [i]        char c=str_s[(int)l_s+3];[/i]

    [i]        A=(int)a*3;[/i]

    [i]        B=-(int)a*7;[/i]

    [i]        C=(int)a;[/i]

    [i]        A=A-(int)b;[/i]

    [i]        B=(int)b*2+B;[/i]

    [i]        C=C+(int)b;[/i]

    [i]        A=A+(int)c*5;[/i]

    [i]        B=B+(int)c*7;[/i]

    [i]        C=C-(int)c*2;[/i]

    [i]        //0×204=a*3-b+c*5 [/i]

    [i]        //0×19=a*7+b*2+c*7[/i]

    [i]        //0x0D=a+b-c*2[/i]

    [i]        if(A==0×204 && B==0×19 &&C==0x0D)[/i]

    [i]        {[/i]

    [i]                printf(&quot;OK! &quot;);[/i]

    [i]                return 0;[/i]

    [i]        } [/i]

    [i]        printf(&quot;%x,%x,%x &quot;,A,B,C);[/i]

    [i]        return -1;[/i]

    [i]} [/i]

    [i]

    2.4. 注册机

    13.png

    计算Serial最后三字节才是真正的解方程算法,方程组如下:

    3a-b+5c=0×204

    7a+2b+7c=0×19

    a+b-2c=0x0D

    注册机源码(C++)

    #include <iostream>

    #include <string.h>

    using namespace std;

    unsigned leftrot(unsigned a,int n)

    {

            unsigned b;

            b=(a >> (32 – n)) | (a << n);

            return b;

    }

    unsigned rightrot(unsigned a,int n)

    {

            unsigned b;

            b=(a << (32 – n)) | (a >> n);

            return b;

    }

    int main(){

            

            char str_n[30];//test

            char str_s[30];

            unsigned len_n;

            cout<<&quot;Name:&quot;;

            cin>>str_n;

            len_n=strlen(str_n);

            //计算name,得到32位数值;

            unsigned f_n=0×0;

            char cl;

            int i=len_n;

            int j=1;

            for(;i>0;){

                    i–;

                    f_n^=(str_n[i]^j)*j;

                    j+=5;

                    f_n=leftrot(f_n,5);

            }

            f_n=0-f_n-1;

            f_n=rightrot(f_n,len_n);

            //计算str_s

            for(i=0;f_n>0x1a;i++){

                    str_s[i]=(char)(f_n%0x1a+0×41);

                    f_n/=0x1a;

            }

            str_s[i]=(char)(f_n+0×41);

            str_s[i+1]=&#39;-&#39;;

            str_s[i+2]=&#39;R&#39;;

            str_s[i+3]=&#39;A&#39;;

            str_s[i+4]=&#39;C&#39;;       

            cout<<&quot;Serial:&quot;<<str_s<<endl;

            return 0;

    }

    2.5. 移位运算

    算是本节的彩蛋吧,在本算法逆向过程中,移位指令花费了笔者大量时间,于是就总结出了下面这些东西:

    移位指令解释C++实现
    SHL 逻辑左移:每位左移,低位补0,高位进CF b=a&lt;&lt;n
    SHR 逻辑右移:每位右移,低位进CF,高位补0 b=a&gt;&gt;n
    SAL 算术左移:每位左移,低位补0,高位进CF b=a&lt;&lt;n
    SAR 算术右移动:每位右移,低位进CF,高位不变 c=0;for(i=n;i&gt;0;i–){         c=c|((a&gt;&gt;(i-1))/(0xFFFFFFFF&gt;&gt;(i)));}b=c|(a&gt;&gt;n);
    ROL 循环左移:高位到低位并送CF b=(a &gt;&gt; (32 – n)) | (a &lt;&lt; n)
    ROR 循环右移:低位到高位并送CF b=(a &lt;&lt; (32 – n)) | (a &gt;&gt; n)
    RCL 带进位循环左移:循环左移,进位值(CF)到低位,高位进CF 未实现
    RCR 带进位循环右移:循环右移,进位值(CF)到高位,低位进CF 未实现

    本文如果出现错误,欢迎指出,感激不尽!

    本文中的所有程序请在虚拟机中运行。

    夜深人静,未完待续…..

     

    解方程算法程序>>>>>戳我回原文下载

    简单算法程序>>>>>戳我回原文下载

    >>>>>>黑客入门必备技能   带你入坑,和逗比表哥们一起聊聊黑客的事儿,他们说高精尖的技术比农药都好玩!

  • 相关阅读:
    jmeter中生成html测试报告
    jmeter插件管理
    Jmeter参数化
    jmeter测试实例
    jmeter调用时间函数
    Jmeter参数关联
    JMeter的使用流程
    Jmeter 响应结果乱码解决
    使用BadBoy录制JMeter脚本
    jemter安装
  • 原文地址:https://www.cnblogs.com/ichunqiu/p/8119241.html
Copyright © 2020-2023  润新知