Brad Soblesky.2 逆向分析&注册机编写

admin 2024年7月3日07:49:22评论0 views字数 5299阅读17分39秒阅读模式

这次用OD分析Brad Soblesky.2的算法分析,并一步步写对应的注册机。

先不考虑有无壳的问题,着重在分析思路。

0x01 信息收集

先简单运行程序试试功能,直接输入1/2

获得提示信息1:user name must have at least 5 characters.

Brad Soblesky.2 逆向分析&注册机编写

再输入正常点的用户名密码

获得提示信息2:incorrect!!, try again.

Brad Soblesky.2 逆向分析&注册机编写

得出结论程序的功能为:

  • 用户名长度至少要五个字符

  • 用户名和注册码有对应关系(后续分析发现注册码并不是写死,而是通过算法计算出来)

0x02 程序分析

先找到主要的处理逻辑,提示信息可以用OD本身的功能或插件直接搜索

查找->所有参考文本字串  //OD自带Ultra String Reference->Find ASCII //插件查找

Brad Soblesky.2 逆向分析&注册机编写

  • 双击跳转到对应汇编代码,F2下断点,F9运行输入1/2 执行,到断点处

Brad Soblesky.2 逆向分析&注册机编写

在 00401575 处看到有 cmp 5,提示词又是用户名要5个字符以上,猜测此处是做用户名长度判断,[ebp-1C] 是用户名长度

在 00401579 处跳转到 004015BE,跳过了用户名长度不够的提示信息,猜测跳转后才是真正的处理逻辑,即 004015BE 处的后续才是具体的注册码处理逻辑。

  • 在 004015BE 处下断点输入 admin/123456 执行,满足用户名长度后被断下来,继续分析:

Brad Soblesky.2 逆向分析&注册机编写

来捋一捋这一段在干嘛,边执行边猜功能。

  1. 有个很明显的红箭头跳转,从 00401618 处跳转到前面 004015C7,猜测是个循环,中间还有个 jge 跳转到循环外的地址,猜测是break作用

  2. [ebp-20] 处初始值为0,然后第一次循环从 004015C5 跳转到 004015D0,不执行加1。后续每次循环 [ebp-20] 都加1

  3. jge 是基于 [ebp-1c] 和 [ebp-20] 的对比,查看内存 [ebp-1c] 值为5,猜测是用户名长度,因此此处的循环的次数是用户名的长度

那我们接下去看看每次循环都干了什么。执行到 004015DC ,查看内存 [ebp-14]对应的值

Brad Soblesky.2 逆向分析&注册机编写

看起来存的是个地址,继续看存的什么

Brad Soblesky.2 逆向分析&注册机编写

这不就是我们的用户名吗,因此 [ebp-14] 存的就是用户名的地址

Brad Soblesky.2 逆向分析&注册机编写

在 004015E4 处有个 call,直接看汇编代码看不太出干嘛的,但执行完 call 后,ECX是输入的用户名,下一行代码为 movsx edx, al,而当前 al 的值为 61(此处是十六进制,61H对应的字符就是a),猜测此处这个 call,就是根据从0开始的序号取出对应用户名的字符值。

  • 此处其实就当前的信息继续分析,看不出所以然,也不知道什么时候对应用户名的注册码才出来,先继续往下走

Brad Soblesky.2 逆向分析&注册机编写

跳出那多次循环后往下执行,执行后 00401627 这个 call 后,发现 EAX 出现了一串值,猜测这就是序列号

Brad Soblesky.2 逆向分析&注册机编写

猜测正确,此处的函数调用 push了EAX和%lu,后者是用于格式化字符串,即当时的EAX是通过用户名计算出来的序列号值,而EAX是刚结束循环后的 [ebp-10] 。那可以大胆猜测,上面的循环计算,最后就是把序列号放到 [ebp-10],那我们再重新看下循环内的 [ebp-10] 。

  • 断点下在循环初始处,执行到 [ebp-10] 

Brad Soblesky.2 逆向分析&注册机编写

经过循环和多次执行发现,每次循环刚开始,[ebp-10] 的值都是81276345,猜测这是一个种子值。然后在每次循环过程中经过各种计算,[ebp-10] 的值一直在变化,到循环结束后[ebp-10] 的值就是序列号的十六进制,再转换成十进制就是我们应该输入的序列号。

0x03 注册机的编写(一)

通过上面分析可知,算法就是这个循环,循环完后序列号的十六进制已经出来,后续只是做个格式转换。

此处投机取巧,直接用汇编写注册机,就不用理解算法再重新实现了。但随之问题也来了,要分析那些 ebp对应的值是什么,提前初始化好,根据前面的分析,汇总如下:

[ebp-14] : 用户名地址[ebp-10] : 初始化种子值为81276345[ebp-1C] : 用户名长度[ebp-20] : 索引/序号,从0开始到len(user)-1

利用插件把这个循环的代码复制出来,这里用的是 Asm2Clipboard 插件

需要复制的是这段汇编代码

Brad Soblesky.2 逆向分析&注册机编写

利用插件复制出来是这样的

  mov     dword ptr [ebp-20], 0  jmp L005L002:  mov     edx, dword ptr [ebp-20]  add     edx, 1  mov     dword ptr [ebp-20], edxL005:  mov     eax, dword ptr [ebp-20]  cmp     eax, dword ptr [ebp-1C]  jge     short 0040161A  mov     ecx, dword ptr [ebp-20]  push    ecx  lea     ecx, dword ptr [ebp-14]  call    00401900  movsx   edx, al  mov     eax, dword ptr [ebp-10]  add     eax, edx  mov     dword ptr [ebp-10], eax  mov     ecx, dword ptr [ebp-20]  shl     ecx, 8  mov     edx, dword ptr [ebp-10]  xor     edx, ecx  mov     dword ptr [ebp-10], edx  mov     eax, dword ptr [ebp-20]  add     eax, 1  mov     ecx, dword ptr [ebp-1C]  imul    ecx, dword ptr [ebp-20]  not     ecx  imul    eax, ecx  mov     edx, dword ptr [ebp-10]  imul    edx, eax  mov     dword ptr [ebp-10], edx  jmp L002

可以看到,其中 [ebp-10],[ebp-14],[ebp-1C] 的值需要我们去定义,这部分前面已经写过,直接替代

[ebp-14] : 用户名地址[ebp-10] : 初始化种子值为81276345[ebp-1C] : 用户名长度[ebp-20] : 索引/序号,从0开始到len(user)-1

用汇编写个简易的窗口程序,源码如下

;Register.Asm.386.model flat, stdcall  ;32 bit memory modeloption casemap :none  ;case sensitiveinclude Register.inc.data  g_szCode db 256 dup(0) ;用于存储注册码  g_szFmt db "%lu", 0    ;用于格式化字符串.codestart:  invoke GetModuleHandle,NULL  mov  hInstance,eax    invoke InitCommonControls  invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL  invoke ExitProcess,0;########################################################################OnBtnRegister proc hWin:HWND  LOCAL @dwUserLen:DWORD  LOCAL @dwIdx:DWORD  LOCAL @szBuff[256]:BYTE  LOCAL @dwSeed:DWORD  ;getuser  invoke GetDlgItemText, hWin, EDT_USER, addr @szBuff, 256  mov @dwUserLen, eax   ;此处eax为DialogBoxParam返回的用户名长度  mov   @dwSeed, 81276345h  ;注册序列号初始种子值  mov     @dwIdx, 0    jmp L005L002:    mov     edx, @dwIdx  ;index    add     edx, 1    mov     @dwIdx, edxL005:  mov     eax, @dwIdx  cmp     eax, @dwUserLen  jge     LEND  ;此处代码里为函数调用,进入函数摘取关键操作  ;mov     ecx, dword ptr [ebp-20]    ;push    ecx    ;lea     ecx, dword ptr [ebp-14]    ;call    00401900  mov     ecx, @dwIdx  ;index  lea      eax, @szBuff  mov      al, [eax+ecx];getchar  movsx   edx, al  mov     eax, @dwSeed  add     eax, edx  mov     @dwSeed, eax  mov     ecx, @dwIdx  shl     ecx, 8  mov     edx, @dwSeed  xor     edx, ecx  mov     @dwSeed, edx  mov     eax, @dwIdx  add     eax, 1  mov     ecx, @dwUserLen  imul    ecx, @dwIdx  not     ecx  imul    eax, ecx  mov     edx, @dwSeed  imul    edx, eax  mov     @dwSeed, edx  jmp L002LEND:  invoke wsprintf, offset g_szCode, offset g_szFmt, @dwSeed  invoke SetDlgItemText, hWin, EDT_CODE, offset g_szCode ;return value in code  retOnBtnRegister endpDlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM  mov  eax,uMsg  .if eax==WM_INITDIALOG  .elseif eax==WM_COMMAND    mov eax, wParam    .if ax == BTN_REGISTER      invoke OnBtnRegister, hWin    .endif  .elseif eax==WM_CLOSE    invoke EndDialog,hWin,0  .else    mov  eax,FALSE    ret  .endif  mov  eax,TRUE  retDlgProc endpend start
;Register.Incinclude windows.incinclude kernel32.incinclude user32.incinclude Comctl32.incinclude shell32.incincludelib kernel32.libincludelib user32.libincludelib Comctl32.libincludelib shell32.libDlgProc      PROTO  :HWND,:UINT,:WPARAM,:LPARAM.constIDD_DIALOG1      equ 101EDT_USER        equ 1002EDT_CODE        equ 1003BTN_REGISTER        equ 1001;#########################################################################.data?hInstance      dd ?;#########################################################################

执行试试,随便输入用户名

Brad Soblesky.2 逆向分析&注册机编写

Brad Soblesky.2 逆向分析&注册机编写

0x04 注册机的编写(二)

python实现注册机

本人拙劣,直接照着算法的一步步操作复刻,汇编里的循环算法图已经在上边就不贴了。

说下编写过程中的几个点:

  1. 内存中是以二进制进行操作,位与、异或、取反等操作,代码中操作时,操作数是十进制或二进制结果相同,直接操作就行,输出时默认转换为十进制

  2. 内存中负数的表示是补码,代码中计算出来的是值,所以如果是负值要转换为对应的补码。

def limit(user_name):    if len(user_name) < 5:        print("user name must have at least 5 characters.")        exit()#操作数转换成十进制/十六进制进行操作结果相同def pojie(user_name):    seed = 0x81276345 #int    length = len(user_name)    for i in range(length):        seed = hex(seed + ord(user_name[i]))        tmp_index = i << 8        seed = int(seed,16) ^ tmp_index        tmp_index = i + 1        tmp = i * length        tmp = ~tmp & 0xFFFFFFFF  # 取反并按需截断至32位        tmp_seed = tmp_index * tmp        seed = (seed * tmp_seed) & 0xFFFFFFFF  # 取反并按需截断至32位,实现imul        #seed = hex(seed * hex(tmp_length))    return seedif __name__ == '__main__':    user_input = str(input("请输入用户名:"))    limit(user_input)    register_code = pojie(user_input)    print("用户名: "+user_input+",对应的注册是", register_code)

Brad Soblesky.2 逆向分析&注册机编写

Brad Soblesky.2 逆向分析&注册机编写

声明:该公众号大部分文章来自作者日常学习笔记,也有部分文章是经过作者授权和其他公众号白名单转载,未经授权,严禁转载,如需转载,联系开白名单。

请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与本公众号无关。

原文始发于微信公众号(白帽学子):Brad Soblesky.2 逆向分析&注册机编写

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月3日07:49:22
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Brad Soblesky.2 逆向分析&注册机编写http://cn-sec.com/archives/2903480.html

发表评论

匿名网友 填写信息