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

2024-07-06 284 0

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

程序可以自行网上找。这次先不考虑有无壳的问题,着重在分析思路。

1、信息收集

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

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

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

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

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

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

得出结论程序的功能为:

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

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

2、程序分析

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

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

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

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

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

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

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

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

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

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

  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 逆向分析&注册机编写插图5

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

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

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

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

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

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

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

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

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

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

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

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

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

注册机的编写(一)

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

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

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

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

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

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

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

mov     dword ptr [ebp-20], 0
  jmp L005
L002:
  mov     edx, dword ptr [ebp-20]
  add     edx, 1
  mov     dword ptr [ebp-20], edx
L005:
  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 model
option casemap :none  ;case sensitive

include Register.inc

.data
	g_szCode db 256 dup(0) ;用于存储注册码
	g_szFmt db "%lu", 0    ;用于格式化字符串

.code

start:
	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 L005
L002:
  	mov     edx, @dwIdx  ;index
  	add     edx, 1
  	mov     @dwIdx, edx
L005:
	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 L002
LEND:
	invoke wsprintf, offset g_szCode, offset g_szFmt, @dwSeed
	
	invoke SetDlgItemText, hWin, EDT_CODE, offset g_szCode ;return value in code
	
	ret

OnBtnRegister endp

DlgProc 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
	ret

DlgProc endp

end start
;Register.Inc
include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include shell32.inc

includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib shell32.lib

DlgProc			PROTO	:HWND,:UINT,:WPARAM,:LPARAM

.const

IDD_DIALOG1			equ 101
EDT_USER    		equ 1002
EDT_CODE    		equ 1003
BTN_REGISTER        equ 1001
;#########################################################################
.data?
hInstance			dd ?
;#########################################################################

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

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

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

注册机的编写(二)

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 seed

if __name__ == '__main__':
    user_input = str(input("请输入用户名:"))
    limit(user_input)
    register_code = pojie(user_input)
    print("用户名: "+user_input+",对应的注册是", register_code)

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


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

webpack打包站点,js文件名批量获取思路
加密对抗靶场enctypt——labs通关
【论文速读】| 注意力是实现基于大语言模型的代码漏洞定位的关键
蓝队技术——Sysmon识别检测宏病毒
内网渗透学习|powershell上线cs
LLM attack中的API调用安全问题及靶场实践

发布评论