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

本文作者: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;\0&#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;\0&#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!\n&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;\0&#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!\n&quot;);[/i]

[i]                return 0;[/i]

[i]        } [/i]

[i]        printf(&quot;%x,%x,%x\n&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,高位进CFb=a&lt;&lt;n
SHR逻辑右移:每位右移,低位进CF,高位补0b=a&gt;&gt;n
SAL算术左移:每位左移,低位补0,高位进CFb=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循环左移:高位到低位并送CFb=(a &gt;&gt; (32 – n)) | (a &lt;&lt; n)
ROR循环右移:低位到高位并送CFb=(a &lt;&lt; (32 – n)) | (a &gt;&gt; n)
RCL带进位循环左移:循环左移,进位值(CF)到低位,高位进CF未实现
RCR带进位循环右移:循环右移,进位值(CF)到高位,低位进CF未实现

 

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

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

 

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

 

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

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

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

 

转载于:https://www.cnblogs.com/ichunqiu/p/8119241.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值