吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3854|回复: 3
收起左侧

[原创] 一个ReverseMe的算法分析

[复制链接]
zbnysjwsnd8 发表于 2017-9-28 19:27
前几天写到博客的 然后昨天写了一个和这个很相似的KeygenMe.
ReverseMe:https://www.52pojie.cn/thread-647291-1-1.html
KeygenMe:https://www.52pojie.cn/thread-647830-1-1.html

这个ReverseMe是我一年前写的,不过现在源代码丢了而且怎么写的也忘了。正好昨天逛一个论坛的时候看到了这个ReverseMe,就顺便下载玩了玩(也算是重温了一下),于是就有了这篇文章。
因为篇幅有限 我就写写关键的地方。
0x0 寻找算法地址
直接来到main函数(0x004019B2)处。
程序首先获取ntdll!ZwContinue函数的地址,然后保存到0x00417F7C处。

004019BE    68 DC504100     push    004150DC                         ; NtContinue
004019C3    68 E8504100     push    004150E8                         ; n
004019C8    33F6            xor     esi, esi
004019CA    33DB            xor     ebx, ebx
004019CC    C705 787F4100 0>mov     dword ptr [0x417F78], 00412000
004019D6    FF15 04304100   call    dword ptr [<&KERNEL32.GetModuleH>; kernel32.GetModuleHandleW
004019DC    50              push    eax
004019DD    FF15 0C304100   call    dword ptr [<&KERNEL32.GetProcAdd>; kernel32.GetProcAddress
004019E3    68 FC504100     push    004150FC                         ; E
004019E8    A3 7C7F4100     mov     dword ptr [0x417F7C], eax

继续往下走,发现程序设置了CONTEXT结构体然后调用ZwContinue函数。

00401ABB    8D05 3D1B4000   lea     eax, dword ptr [0x401B3D]        ; 返回地址
00401AC1    50              push    eax
00401AC2    9C              pushfd
00401AC3    8F45 FC         pop     dword ptr [ebp-0x4]
00401AC6    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401AC9    8945 F0         mov     dword ptr [ebp-0x10], eax
00401ACC    8965 FC         mov     dword ptr [ebp-0x4], esp
00401ACF    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401AD2    8945 F4         mov     dword ptr [ebp-0xC], eax
00401AD5    896D FC         mov     dword ptr [ebp-0x4], ebp
00401AD8    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401ADB    C785 30FFFFFF 0>mov     dword ptr [ebp-0xD0], 0x10007    ; CONTEXT_FULL
00401AE5    8945 E4         mov     dword ptr [ebp-0x1C], eax
00401AE8    A1 547F4100     mov     eax, dword ptr [0x417F54]
00401AED    8945 E8         mov     dword ptr [ebp-0x18], eax        ; Eip
00401AF0    16              push    ss
00401AF1    8F45 FC         pop     dword ptr [ebp-0x4]
00401AF4    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401AF7    8945 F8         mov     dword ptr [ebp-0x8], eax
00401AFA    0E              push    cs
00401AFB    8F45 FC         pop     dword ptr [ebp-0x4]
00401AFE    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401B01    8945 EC         mov     dword ptr [ebp-0x14], eax
00401B04    1E              push    ds
00401B05    8F45 FC         pop     dword ptr [ebp-0x4]
00401B08    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401B0B    8945 C8         mov     dword ptr [ebp-0x38], eax
00401B0E    06              push    es
00401B0F    8F45 FC         pop     dword ptr [ebp-0x4]
00401B12    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401B15    8945 C4         mov     dword ptr [ebp-0x3C], eax
00401B18    0FA0            push    fs
00401B1A    8F45 FC         pop     dword ptr [ebp-0x4]
00401B1D    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401B20    8945 C0         mov     dword ptr [ebp-0x40], eax
00401B23    0FA8            push    gs
00401B25    8F45 FC         pop     dword ptr [ebp-0x4]
00401B28    8B45 FC         mov     eax, dword ptr [ebp-0x4]
00401B2B    6A 00           push    0x0
00401B2D    8945 BC         mov     dword ptr [ebp-0x44], eax
00401B30    8D85 30FFFFFF   lea     eax, dword ptr [ebp-0xD0]
00401B36    50              push    eax
00401B37    FF15 7C7F4100   call    dword ptr [0x417F7C]             ; ntdll.ZwContinue
00401B3D    68 3C514100     push    0041513C                         ; pause
00401B42    E8 A9000000     call    00401BF0

在Eip(0x7781656D)下断,然后F9,分析后容易发现:这个函数也是一个跳转函数,其中配置CONTEXT结构体并转移的代码如下:

778165E3    A1 547F4100     mov     eax, dword ptr [0x417F54]
778165E8    0105 387B4100   add     dword ptr [0x417B38], eax
778165EE    16              push    ss
778165EF    8F05 4C7B4100   pop     dword ptr [0x417B4C]
778165F5    A1 4C7B4100     mov     eax, dword ptr [0x417B4C]
778165FA    A3 487B4100     mov     dword ptr [0x417B48], eax
778165FF    0E              push    cs
77816600    8F05 4C7B4100   pop     dword ptr [0x417B4C]
77816606    A1 4C7B4100     mov     eax, dword ptr [0x417B4C]
7781660B    A3 3C7B4100     mov     dword ptr [0x417B3C], eax
77816610    1E              push    ds
77816611    8F05 4C7B4100   pop     dword ptr [0x417B4C]
77816617    A1 4C7B4100     mov     eax, dword ptr [0x417B4C]
7781661C    A3 187B4100     mov     dword ptr [0x417B18], eax
77816621    06              push    es
77816622    8F05 4C7B4100   pop     dword ptr [0x417B4C]
77816628    A1 4C7B4100     mov     eax, dword ptr [0x417B4C]
7781662D    A3 147B4100     mov     dword ptr [0x417B14], eax
77816632    0FA0            push    fs
77816634    8F05 4C7B4100   pop     dword ptr [0x417B4C]
7781663A    A1 4C7B4100     mov     eax, dword ptr [0x417B4C]
7781663F    A3 107B4100     mov     dword ptr [0x417B10], eax
77816644    0FA8            push    gs
77816646    8F05 4C7B4100   pop     dword ptr [0x417B4C]
7781664C    A1 4C7B4100     mov     eax, dword ptr [0x417B4C]
77816651    6A 00           push    0x0
77816653    68 807A4100     push    0x417A80
77816658    A3 0C7B4100     mov     dword ptr [0x417B0C], eax
7781665D    FF15 7C7F4100   call    dword ptr [0x417F7C]             ; ntdll.ZwContinue
77816663    90              nop
77816664    FF05 507F4100   inc     dword ptr [0x417F50]

在0x7781665D处下断,拦截每次的Eip值。
拦截以后发现几个有用的函数:

0x00401B6D 接受用户名(存放在0x00417D50处)
0x00401B8B 接受注册码(存放在0x00417B50处)
0x7781668F(函数地址可变) 检查用户名是否是12个字节
0x778166BE(函数地址可变) 检查注册码是否是12个字节
0x00412057 算法部分
0x4123A9 返回结果

0x1 分析算法
来到0x00412057处,简单看看代码,发现一堆push call pop之类的指令,这里我使用IDA的F5插件来分析。
这里写图片描述
其中的EncryptData:
这里写图片描述

这里仅仅调用了两个函数:strlen和sub_401000。
我们目前需要做的就是分析出函数sub_401000是干什么的。
IDA进入401000处,发现代码很简短。
这里写图片描述
这段代码很简短,就是not not and,如果用一条指令来描述就是nor指令。
其中,有四个指令可以直接被nor模拟。

not(a) = nor(a,a)
and(a,b) = nor(nor(a,a),nor(b,b)) = nor(not(a),not(b))
or(a,b) = nor(nor(a,b),nor(a,b))
xor(a,b) = nor(nor(nor(a,a),nor(b,b)),nor(a,b)) = nor(and(a,b),nor(a,b))

根据这个关系 我们尝试将这段算法给改写成not and or xor的形式

HRESULT __stdcall Decrypt(PINFORMATIONCARD_CRYPTO_HANDLE hCrypto, BOOL fOAEP, DWORD cbInData, PBYTE pInData, DWORD *pcbOutData, PBYTE *ppOutData)
{
  HRESULT result; // eax@1
  unsigned int i; // esi@1
  int ByteOfSerial1; // ST28_4@2
  int ByteOfUsername; // ST28_4@2
  int ByteOfSerial; // ebx@2
  int ByteOfUsername1; // edi@2
  int v12; // ST18_4@2
  int v13; // ST14_4@2
  int v14; // eax@2
  int v15; // eax@2
  int v16; // ST18_4@2
  int v17; // ST14_4@2
  int v18; // ST10_4@2
  int v19; // eax@2
  int v20; // eax@2
  int v21; // eax@2
  int v22; // ST18_4@2
  int v23; // eax@2
  int v24; // eax@2
  int v25; // ST18_4@2
  int v26; // ST14_4@2
  int v27; // ST10_4@2
  int v28; // eax@2
  int v29; // eax@2
  int v30; // ST14_4@2
  int v31; // ST10_4@2
  int v32; // ST0C_4@2
  int v33; // eax@2
  int v34; // eax@2
  int v35; // eax@2
  int v36; // ST14_4@2
  int v37; // eax@2
  int v38; // eax@2
  int v39; // eax@2
  int v40; // ST18_4@2
  int v41; // ST14_4@2
  int v42; // ST10_4@2
  int v43; // eax@2
  int v44; // eax@2
  int v45; // ST14_4@2
  int v46; // ST10_4@2
  int v47; // ST0C_4@2
  int v48; // eax@2
  int v49; // eax@2
  int v50; // eax@2
  int v51; // ST14_4@2
  int v52; // eax@2
  int v53; // eax@2
  int v54; // ST14_4@2
  int v55; // ST10_4@2
  int v56; // ST0C_4@2
  int v57; // eax@2
  int v58; // eax@2
  int v59; // ST10_4@2
  int v60; // ST0C_4@2
  int v61; // ST08_4@2
  int v62; // eax@2
  int v63; // eax@2
  int v64; // eax@2
  int v65; // ST10_4@2
  int v66; // eax@2
  int v67; // eax@2
  int v68; // eax@2
  int v69; // eax@2
  char v70; // al@2
  int v71; // edi@2
  int v72; // ebx@2
  int v73; // ST18_4@2
  int v74; // ST14_4@2
  int v75; // eax@2
  int v76; // eax@2

  // 初始化加密数据
  *(_DWORD *)&SuccessfulData = EncryptData[0];
  *((_DWORD *)&SuccessfulData + 1) = EncryptData[1];
  *((_DWORD *)&SuccessfulData + 2) = EncryptData[2];
  *(&SuccessfulData + 12) = LOBYTE(EncryptData[3]);
  result = 0;
  i = 0;
  if ( strlen(UserName) != 0 )
  {
    do
    {
      ByteOfSerial1 = Serial[i];                // Serial[i]
      ByteOfUsername = UserName[i];             // UserName[i]
      ByteOfSerial = Serial[i];                 // Serial[i]
      ByteOfUsername1 = UserName[i];            // UserName[i]
      v12 = nor(ByteOfUsername, ByteOfSerial);
      v13 = nor(ByteOfSerial, ByteOfSerial);
      v14 = nor(ByteOfUsername1, ByteOfUsername1);
      v15 = nor(v14, v13);
      v16 = nor(v15, v12);                      // v10 = UserName[i] xor Serial[i]
      v17 = nor(UserName[i], Serial[i]);
      v18 = nor(ByteOfSerial, ByteOfSerial);
      v19 = nor(ByteOfUsername1, ByteOfUsername1);
      v20 = nor(v19, v18);
      v21 = nor(v20, v17);                      // v15 = UserName[i] xor Serial[i]
      v22 = nor(v21, v16);                      // v16 = not(UserName[i] xor Serial[i])
      v23 = nor(ByteOfUsername1, ByteOfUsername1);
      v24 = nor(v23, v22);                      // v18 = UserName[i] and v15
      v25 = nor(Serial[i], v24);                // v19 = nor(Serial[i],UserName[i] and (UserName[i] xor Serial[i]))
      v26 = nor(UserName[i], Serial[i]);
      v27 = nor(ByteOfSerial, ByteOfSerial);
      v28 = nor(ByteOfUsername1, ByteOfUsername1);
      v29 = nor(v28, v27);
      v30 = nor(v29, v26);                      // v24 = UserName[i] xor Serial[i]
      v31 = nor(UserName[i], Serial[i]);
      v32 = nor(ByteOfSerial, ByteOfSerial);
      v33 = nor(ByteOfUsername1, ByteOfUsername1);
      v34 = nor(v33, v32);
      v35 = nor(v34, v31);                      // v29 = UserName[i] xor Serial[i]
      v36 = nor(v35, v30);                      // v30 = not(UserName[i] xor Serial[i])
      v37 = nor(ByteOfUsername1, ByteOfUsername1);
      v38 = nor(v37, v36);                      // v32 = nor(not(UserName[i]),v30)
      v39 = nor(Serial[i], v38);                // v33 = nor(Serial[i],UserName[i] and (UserName[i] xor Serial[i]))
      v40 = nor(v39, v25);                      // v34 = not(v33)
      v41 = nor(UserName[i], Serial[i]);
      v42 = nor(ByteOfSerial, ByteOfSerial);
      v43 = nor(ByteOfUsername1, ByteOfUsername1);
      v44 = nor(v43, v42);
      v45 = nor(v44, v41);                      // v39 = UserName[i] xor Serial[i]
      v46 = nor(UserName[i], Serial[i]);
      v47 = nor(ByteOfSerial, ByteOfSerial);
      v48 = nor(ByteOfUsername1, ByteOfUsername1);
      v49 = nor(v48, v47);
      v50 = nor(v49, v46);                      // v44 = UserName[i] xor Serial[i]
      v51 = nor(v50, v45);                      // v45 = not(UserName[i] xor Serial[i])
      v52 = nor(ByteOfUsername1, ByteOfUsername1);
      v53 = nor(v52, v51);                      // v47 = UserName[i] and (UserName[i] xor Serial[i])
      v54 = nor(Serial[i], v53);                // v48 = nor(Serial[i],UserName[i] and (UserName[i] xor Serial[i]))
      v55 = nor(UserName[i], Serial[i]);
      v56 = nor(ByteOfSerial, ByteOfSerial);
      v57 = nor(ByteOfUsername1, ByteOfUsername1);
      v58 = nor(v57, v56);
      v59 = nor(v58, v55);                      // v53 = UserName[i] xor Serial[i]
      v60 = nor(UserName[i], Serial[i]);
      v61 = nor(ByteOfSerial, ByteOfSerial);
      v62 = nor(ByteOfUsername1, ByteOfUsername1);
      v63 = nor(v62, v61);
      v64 = nor(v63, v60);                      // v58 = UserName[i] xor Serial[i]
      v65 = nor(v64, v59);                      // v59 = not(v58)
      v66 = nor(ByteOfUsername1, ByteOfUsername1);
      v67 = nor(v66, v65);                      // v61 = UserName[i] and (UserName[i] xor Serial[i])
      v68 = nor(Serial[i], v67);                // v62 = nor(Serial[i],UserName[i] and (UserName[i] xor Serial[i]))
      v69 = nor(v68, v54);                      // v63 = not(v62)
      v70 = nor(v69, v40);                      // v64 = not(v63) -> v64 = v62
      UserName[i] = v70;                        // UserName[i] = v62
      v71 = v70;                                // v65 = v62
      v72 = (unsigned __int8)*(&SuccessfulData + i);// v66 = SuccessfulData[i]
      v73 = nor(v72, v70);
      v74 = nor(v71, v71);
      v75 = nor(v72, v72);
      v76 = nor(v75, v74);                      // v70 = SuccessfulData[i] and v62
      *(&SuccessfulData + i) = nor(v76, v73);   // SuccessfulData[i] = SuccessfulDara[i] xor v62
      result = 0;
      ++i;
    }
    while ( i < strlen(UserName) );
  }
  return result;
}

总结一下算法:

设:用户名为UserName,注册码为Serial,提示信息为SuccessfulData,用户名和注册码的每个字节的关系为x。
则有:
x = nor(Serial[i],UserName[i] and (UserName[i] xor Serial[i]))
SuccessfulData[i] = SuccessfulData[i] xor x

0x2 注册机的编写
知道了算法,这样就可以写一个注册机了。
不过因为算法本身的原因,这个注册机编写起来还是有一定难度的。
因为不是所有的用户名所对应的注册码都能被输入进去,不过又因为算法的关系,导致了x并不是只有一个结果。

根据这个ReverseMe的成功图片,成功会输出"Hello world!",正好是SuccessfulData的长度。
那么将字符串"Hello world!"和SuccessfulData逐位异或,得到新的SuccessfulData如下:

char SuccessfulData[] = {0x80,0x90,0x9A,0x8A,0x8A,0x92,0x80,0xCD,0xCE,0xC8,0x80,0xA0};

注册机的代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define and &
#define xor ^
#define not ~
int nor(int a,int b)
{
    return not a and not b;
}
int main(void)
{
    char SuccessfulData[] = {0x80,0x90,0x9A,0x8A,0x8A,0x92,0x80,0xCD,0xCE,0xC8,0x80,0xA0};
    char UserName[512];
    char Serial[13] = {0};
    scanf("%s",UserName);
    if(strlen(UserName) != 12)
        return 0;
    for(int i = 0;i < 12;i++)
    {
        if(!(UserName[i] >= 0x21 && UserName[i] <= 0x7F))
            return 0;
    }
    for(int i = 0;i < 12;i++)
    {
ContinueWhile:
        for(Serial[i] = 0x21;Serial[i] <= 0x7E;Serial[i]++) //scanf函数接受字符串输入时遇到空格截断.
        {
            if(nor(Serial[i],UserName[i] and (UserName[i] xor Serial[i])) == SuccessfulData[i])
                goto Next;
        }
        /* 当前用户名没有对应的可显示的注册码,尝试更改用户名 */
        if(UserName[i] == 0x7E)
            UserName[i] -= (Serial[i] - 0x21);
        else
            UserName[i]++;
        if(UserName[i] == 0x20) //空格截断
            UserName[i]++;
        goto ContinueWhile;
Next:
        _asm nop
    }
    printf("------------------------\nUserName:[%s]\n",UserName);
    printf("Serial:[%s]\n",Serial);
    system("pause");
    return 0;
}

运行结果如图所示:
这里写图片描述
ReverseMe的下载链接:http://pan.baidu.com/s/1gf5YC2B 密码:y093

全文完。

免费评分

参与人数 7威望 +1 吾爱币 +12 热心值 +7 收起 理由
悲伤的杜甫 + 1 用心讨论,共获提升!
siuhoapdou + 1 + 1 谢谢@Thanks!
2864095098 + 1 + 1 热心回复!
keyu + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Three_fish + 1 + 1 谢谢@Thanks!
我是小白123 + 1 + 1 已答复!
Sound + 1 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

仙梦 发表于 2017-9-28 21:07 来自手机
帮忙顶一下666
keyu 发表于 2017-9-28 22:49
2864095098 发表于 2017-9-28 23:46
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 03:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表