网络验证就是将一些关键数据放到服务器上,软件必须从服务器取得这些数据才能运行。拦截这种验证的思路就是拦截服务器返回的数据包,分析程序是如何处理这些数据包的。
相关函数有:
send()函数:
int send(
SOCKET s, //套接字描述符
const char FAR *buf, //缓冲区
int len, //实际要发送数据的字节数
int flags //附加标志,一般为0
);
recv()函数:
int recv(
SOCKET s.
char FAR *buf,
int len,
int flags
)
这里我们攻击的是一个网络验证工具CrackMeNet.exe。
同时使用IDA和OD打开之。在OD界面在send函数处,设置中断。
Run起来,中断并回到当前程序领空:
查看data中的数据:
可以看到此处的Data已经经过了一定的运算实现了加密的效果。
基于IDA分析C函数的出色表现,我们这里使用IDA向前查看代码:
.text:0040150E push ecx ; char *
.text:0040150F call _strlen ; strlen(),获取输入的Name长度
.text:00401514 add esp, 4
.text:00401517 mov [ebp+var_240], eax
.text:0040151D lea edx, [ebp+var_234]
.text:00401523 push edx ; char *
.text:00401524 call _strlen ; 取输入的key的长度
.text:00401529 add esp, 4
.text:0040152C mov [ebp+var_1F8], eax
.text:00401532 push 0 ; time_t *
.text:00401534 call _time ; time(0)
.text:00401539 add esp, 4
.text:0040153C push eax ; struct CFrameWnd *
.text:0040153D call ?AFXSetTopLevelFrame@@YAXPAVCFrameWnd@@@Z ; AFXSetTopLevelFrame(CFrameWnd *)
.text:00401542 add esp, 4
.text:00401545 call _rand
.text:0040154A and eax, 800000FFh
.text:0040154F jns short loc_401558
.text:00401551 dec eax
.text:00401552 or eax, 0FFFFFF00h
.text:00401557 inc eax
.text:00401558
.text:00401558 loc_401558: ; CODE XREF: sub_4013BA+195↑j
.text:00401558 mov byte ptr [ebp+var_254], al
.text:0040155E mov al, byte ptr [ebp+var_240]
.text:00401564 mov [ebp+var_354], al
.text:0040156A mov cl, byte ptr [ebp+var_1F8]
.text:00401570 mov [ebp+var_353], cl
.text:00401576 mov dl, byte ptr [ebp+var_254]
.text:0040157C mov [ebp+var_352], dl
.text:00401582 mov eax, [ebp+var_240]
.text:00401588 push eax ; size_t
.text:00401589 lea ecx, [ebp+String]
.text:0040158F push ecx ; void *
.text:00401590 lea edx, [ebp+var_351]
.text:00401596 push edx ; void *
.text:00401597 call _memcpy
.text:0040159C add esp, 0Ch
.text:0040159F mov eax, [ebp+var_1F8]
.text:004015A5 push eax ; size_t
.text:004015A6 lea ecx, [ebp+var_234]
.text:004015AC push ecx ; void *
.text:004015AD mov edx, [ebp+var_240]
.text:004015B3 lea eax, [ebp+edx+var_351]
.text:004015BA push eax ; void *
.text:004015BB call _memcpy
.text:004015C0 add esp, 0Ch
.text:004015C3 mov ecx, [ebp+var_1F8]
.text:004015C9 mov edx, [ebp+var_240]
.text:004015CF lea eax, [edx+ecx+3]
.text:004015D3 mov [ebp+var_23C], eax
.text:004015D9 mov [ebp+var_238], 0
.text:004015E3 jmp short loc_4015F4
.text:004015E5 ; ---------------------------------------------------------------------------
.text:004015E5
.text:004015E5 loc_4015E5: ; CODE XREF: sub_4013BA+269↓j
.text:004015E5 mov ecx, [ebp+var_238]
.text:004015EB add ecx, 1
.text:004015EE mov [ebp+var_238], ecx
.text:004015F4
.text:004015F4 loc_4015F4: ; CODE XREF: sub_4013BA+229↑j
.text:004015F4 mov edx, [ebp+var_238]
.text:004015FA cmp edx, [ebp+var_23C]
.text:00401600 jge short loc_401625
.text:00401602 mov eax, [ebp+var_238]
.text:00401608 movsx ecx, [ebp+eax+var_354]
.text:00401610 xor ecx, 0A6h ; 对bufEncrypt[]进行异或加密运运算
.text:00401616 mov edx, [ebp+var_238]
.text:0040161C mov [ebp+edx+var_354], cl
.text:00401623 jmp short loc_4015E5;循环
由此,我们得知,客户端对用户输入的名字和key整理为(namelength,keylength,ran_K,name,key),进行异或加密运算,然后将数据发给服务器端。
接收数据报的程序段:
.text:00401657 push 1F4h ; len
.text:0040165C lea eax, [ebp+buf]
.text:00401662 push eax ; buf
.text:00401663 mov ecx, [ebp+s]
.text:00401669 push ecx ; s
.text:0040166A call recv
.text:0040166F mov [ebp+var_28C], eax
.text:00401675 mov [ebp+var_238], 0
.text:0040167F jmp short loc_401690
.text:00401681 ; ---------------------------------------------------------------------------
.text:00401681
.text:00401681 loc_401681: ; CODE XREF: sub_4013BA+302↓j
.text:00401681 mov edx, [ebp+var_238]
.text:00401687 add edx, 1
.text:0040168A mov [ebp+var_238], edx
.text:00401690
.text:00401690 loc_401690: ; CODE XREF: sub_4013BA+2C5↑j
.text:00401690 mov eax, [ebp+var_238]
.text:00401696 cmp eax, [ebp+var_28C]
.text:0040169C jge short loc_4016BE
.text:0040169E mov ecx, [ebp+var_238]
.text:004016A4 xor edx, edx
.text:004016A6 mov dl, [ebp+ecx+buf]
.text:004016AD xor edx, 6Eh ;异或解密
.text:004016B0 mov eax, [ebp+var_238]
.text:004016B6 mov byte_41AE68[eax], dl
.text:004016BC jmp short loc_401681 ;循环
倒数第二行可以看到,解密后的数据存档在41AE68H~41AECH:
data:0041AE68 byte_41AE68 db 14h ; DATA XREF: sub_4013BA+2FC↑w
.data:0041AE69 db 15h
.data:0041AE6A db 0
.data:0041AE6B db 0
.data:0041AE6C byte_41AE6C db 0D5h ; DATA XREF: sub_4013BA+306↑r
.data:0041AE6D db 7
.data:0041AE6E db 9
.data:0041AE6F db 0
.data:0041AE70 db 1
.data:0041AE71 db 0
.data:0041AE72 byte_41AE72 db 13h ; DATA XREF: sub_4013BA+32D↑r
.data:0041AE73 align 4
.data:0041AE74 db 3
.data:0041AE75 db 0
.data:0041AE76 byte_41AE76 db 6Dh ; DATA XREF: sub_4013BA+316↑r
.data:0041AE76 ; sub_40FAA8+7↑w
.data:0041AE77 align 4
.data:0041AE78 db 11h
.data:0041AE79 db 0
.data:0041AE7A db 0BBh
.data:0041AE7B db 0
.data:0041AE7C db 91h
.data:0041AE7D db 53h ; S
.data:0041AE7E db 1
.data:0041AE7F db 0
.data:0041AE80 db 21h ; !
.data:0041AE81 db 61h ; a
.data:0041AE82 db 0
.data:0041AE83 db 0
.data:0041AE84 db 1Eh
.data:0041AE85 db 0
.data:0041AE86 db 0C5h
.data:0041AE87 db 0Bh
.data:0041AE88 db 0C9h
.data:0041AE89 db 0Bh
.data:0041AE8A db 30h ; 0
.data:0041AE8B byte_41AE8B db 0BDh ; DATA XREF: sub_4013BA+33D↑r
.data:0041AE8C db 97h
.data:0041AE8D db 88h
.data:0041AE8E db 8Eh
.data:0041AE8F db 0
.data:0041AE90 db 0BEh
.data:0041AE91 db 19h
.data:0041AE92 db 0
.data:0041AE93 db 0
.data:0041AE94 db 0D4h
.data:0041AE95 db 12h
.data:0041AE96 db 0
.data:0041AE97 db 0
.data:0041AE98 db 6Fh ; o
.data:0041AE99 db 35h ; 5
.data:0041AE9A db 0E1h
.data:0041AE9B db 52h ; R
.data:0041AE9C db 51h ; Q
.data:0041AE9D db 0A4h
.data:0041AE9E db 0B7h
.data:0041AE9F db 7
.data:0041AEA0 db 76h ; v
.data:0041AEA1 db 0E7h
.data:0041AEA2 db 0D4h
.data:0041AEA3 db 0A1h
.data:0041AEA4 byte_41AEA4 db 43h ; DATA XREF: sub_4013BA+34D↑r
.data:0041AEA5 db 98h
.data:0041AEA6 db 88h
.data:0041AEA7 db 0D6h
.data:0041AEA8 db 45h ; E
.data:0041AEA9 db 0FFh
.data:0041AEAA byte_41AEAA db 0C6h ; DATA XREF: sub_4013BA+359↑r
.data:0041AEAB db 0B1h
.data:0041AEAC db 43h ; C
.data:0041AEAD db 66h ; f
.data:0041AEAE db 77h ; w
.data:0041AEAF db 98h
.data:0041AEB0 byte_41AEB0 db 77h ; DATA XREF: sub_4010B1+8C↑r
.data:0041AEB1 db 67h ; g
.data:0041AEB2 db 54h ; T
.data:0041AEB3 db 66h ; f
.data:0041AEB4 db 77h ; w
.data:0041AEB5 db 53h ; S
.data:0041AEB6 db 64h ; d
.data:0041AEB7 db 58h ; X
.data:0041AEB8 db 6Ch ; l
.data:0041AEB9 db 66h ; f
.data:0041AEBA db 5
.data:0041AEBB db 8
.data:0041AEBC db 60h ; `
.data:0041AEBD db 16h
.data:0041AEBE db 30h ; 0
.data:0041AEBF db 0B4h
.data:0041AEC0 db 0AAh
.data:0041AEC1 db 54h ; T
.data:0041AEC2 db 0
.data:0041AEC3 db 0
.data:0041AEC4 db 0
.data:0041AEC5 db 0
.data:0041AEC6 db 0
但是在此次试验环境中,对这个缓冲区的存放和读取是隐秘的。最有效率的方法是在整个代码里搜索访问41AE68h~41AEC1H,使用IDA运行如下python脚本:
#coding=utf-8
import os
import sys
def Getasm(ea_from,ea_to,range1,range2):
fp=open("code.txt","w")
ea=ea_from
while ea<ea_to:
cmd=GetMnem(ea)
if cmd == "mov" or cmd=="lea" :
opcode=Dword(NextNotTail(ea)-4)
if opcode<0:
opcode=(~opcode+1)
Message("-> %08X %0xX\n" % (ea,opcode))
if range1 <= opcode <= range2:
delta=opcode-range1
MakeComm(ea,"//+0x%04X" % delta)
fp.write("%08X %s\n" % (ea,GetDisasm(ea)))
ea=NextNotTail(ea)
fp.close()
Message("OK!")
Getasm(0x401000,0x40F951,0x41AE68,0x0041AEC1)
在软件同文件夹下,我们看到:
然后在编写一个服务器端,进行接收和发送。如果软件是域名登录服务器,可修改host文件,解析域名为本地即127.0.0.1。如果使用IP登录,可用inet_addr进行中断,将IP地址修改为本地IP,也可使用代理。
或者修改客户端程序,使用OD打开CrackMeNet.exe,将开始截取的正确数据粘贴到41AE68h~41AEC1H中;将发包功能删掉,再读随机数并将其放到0041AE76H处,代码为:
将recv()函数去除,跳过数据解密代码:
接着点击注册按钮时,跳出对话框,直接来到这个对话框的地址,修改代码:
由此,我们得知破解网络验证就是要把验证过程中的传输数据包给拦下分析,数据报分析工具有很多,如果出现加密就必须使用本例中的发送接收函数设置中断,跟踪程序的处理过程,针对性的寻找和破解。
常用断点设计技巧:
参考:《加密与解密》(第4版)