程序下载地址:
http://pan.baidu.com/s/1ntHRiyt
首先PEID查壳是没有壳,看区段大家不难发现其实是有壳的,OD能正常调试,所以我们就不脱壳了。
在这里点注册,注册码是绑定账号,不绑定电脑的,所以根据账号应该有个算法计算注册码的。
从点注册的地方开始分析,跟了一段就到了一大堆跳转里面去了,分析不出个头绪。
换个思路,运行后dump出来查一下看有没有什么算法,如图
账号长度是不确定的,作者给的key是256位的,DES的话不会改变长度,那么算法很明显是RSA了。256位16进制数就是二进制1024位,应该是标准的RSA1024。
易语言的RSA有什么特征呢,易的作者一般不会自己去写这种算法,估计有模块。
上易的论坛看了看,数据操作支持库里面有加密相关的操作,
安装了个易语言,然后发现有个RSACheck
自己写了个简单的程序调用这个函数,然后OD里面调,可以定位出这个函数的特征码:
B8 34 20 00 00 E8 ?? ?? ?? ?? 53 55 56 8B B4 24 ?? ?? ?? ?? 8D 44 24 10 57 8B 4E 0C 33 DB 50 51 89 5C 24 24 89 5C 24 20 89 5C 24 18 89 5C 24 1C
OD里面CTRL+B搜索,果然有,那就是这个函数无疑了!
观察这个函数的参数可以发现那么只要找到公钥和模数就可以了。
先调试了一会自己那个简单的程序,知道了那2个参数在什么位置传入的,
然后调试了一下那个程序,发一个很可疑的字符串,1032B估计就是公钥了,后面那个256位的字符串是模数。
然后自己把参数带入自己的程序测试,生成的注册码一模一样了,成功了!
然后写注册机
我又测试了另外几个号,结果输入了注册码还是注册不成功,看来还有暗桩。
我是自己算的号,没有爆破,他怎么设置暗桩呢?估计还有网络验证了。
下断点bp ws2_32.send
发现访问了一个网易博客
里面的内容是一串乱七八糟的字符,
{orochigg1dz}
8QuRsPpQwAiDlA6TkAiRlToPiRjTsPlRvTkAZR2RlT3PvR7E2PlR8TlA2R7T1PvR8TlApRYEpAPRmEmAoCYEpACRtT2AnCCTvAoC8ErPjR2TmPBR1TmA5P2RsE5PrCrEoA6RpEpAwC5T3PsC6TmAmCrE1PnCt
EnAuBoD6UnB4SpF3Q0S7FZQrDZU6QZSZUmB0SwD0UmBrD2U4Q4S3UvBsDqF6QwDpFrBnD6UrBxD2UvBZSmFrBwDnF3Q5SoF2QnDZUlB8StU5Q{orochi2001}
{orochizzdh}fgJeIeuJeeqKOddfOOqdxK4axIGfJOnIdfmaSdrfvaFdvK8KROvIIfAOzIyfJOCZHfIOJdTfdOXddfPOxIGfra2IGfxaiC7EkAiC7E{orochi2001}
{orochiggy}ra7LScuJNc2awJncjSpUtQrSBUkBiDuU8QkStUsBnDmFnBnDqFpBwD8FtQ1SiU2Q1SkS8F1QkS7UkBoSZUZQpS5U8QjDlUtQmS{orochi2001}
{orochihmd}zEmFoFsEuBmFpBlD{orochi2001}
{orochigxrz}
tJjcvKBg7LzJ8R7U7SqFrBjDqFnBjDsFnBjDqF6FecxezLOcPeNgSc5JJgaclDAgVcKergsbaeKgAYMeegsbaeBgIclDAgjbSejLEcxJJgbORdtKiADdiIPfwOtI8KROvIPfwOuIlKcOIdbfxavCvKEOwIafy
aGZdfPODdJfRO1drKCOddTfROrIdfjaNdUfBO8aed7Ksa6dwKBOTdhfiASdrfCOsIKfraAdA1MOGd{orochi2001}
{orochixzdz}kTpUuRtUvD8EjCsT0PkR7E1P2RlT3PvR7E2PlR8TlApR8EnAkRoTRPIRlTxPuR{orochi2001}
{orochixzdz2}{orochi2001}
{orochixzdzrz}
ybYdvICcIePOgehOrKhOddtK4ayIiRoTsPqRATlAjCZTkPiCqTlP2R7TkPiC6TnPnR8E2PoCiTqAjRnE7P2RsEpA2IiK7PuRoToPuC8ElAoP2RiTkAZR4T8P5RpTkA0RjTlPjCvTlApCiTsPTROT8PzRoT
{orochi2001}
{orochizcmd}
xDmEtEmDmEnCqEsAmC5ErApCsEtAsCyEsArCoEiApCpEmArCzEoApCpEtAkCtEuArCyEpAwCmEpApC5EvAmCzEsAmAoCzEiAxCsEvAsCqEmAmC5EqAoCmEuAwCpErAwCoEiApCsEsAnCzEsAxCpEnAsC5ErAm
CoEqAnCyEmEnAsC5EpAnBnDoFqBqDrFuBsD6F{orochi2001}
{orochiybmd}
kCtCsCoCtEsFpAtBoFjBoDoFvBtDrFoBqDsFqBqBlDpFnBrDzFrBmD6FmBrDtFnAqCqEmAqCqEiAnCsEuAmCtEsAtCoEnAkCsEqAxCqEqAwCtEoAtC5EvAuAwCnEpAwCyEuAwC5EtAqCpEvAnCoEmAxCsEiAn
CnEnAqCyEnAqCqEnAkCyErAsCyEuArCoEmApC5EmEpAtCoEoAsCqEpAmC5EoAmCyEvAoCnEvAmCqE6FrBmDyFuBnDzFuBoD6FrBsDmFrBoDnFmBoDsFjBpBtDzFtBoDzFoBsDnFrBlDpFoBsDyFsBxDoFpBnD
6F{orochi2001}
{orochibb}72{orochi2001}
估计这个就是白名单了,前面看到的DES和BASE64什么的算法可能就在这里。
在OD里面调了一下这个解密过程,从一个JMP之后又是一段很乱的反汇编代码
00D7B21D 51 push ecx
00D7B21E 881C24 mov byte ptr ss:[esp],bl
00D7B221 60 pushad
00D7B222 C74424 20 2B7DC>mov dword ptr ss:[esp+0x20],0xADC77D2B
00D7B22A 68 ED4B8312 push 0x12834BED
00D7B22F E8 028EF2FF call Q宠迷你?00CA4036
00D7B234 DFEF fucomip st,st(7)
00D7B236 CB retf
00D7B237 6377 9D arpl word ptr ds:[edi-0x63],si
00D7B23A 79 23 jns XQ宠迷你?00D7B25F
00D7B23C 8BA1 6438FCDE mov esp,dword ptr ds:[ecx+0xDEFC3864]
00D7B242 BE 90662E06 mov esi,0x62E6690
00D7B247 F0:C8 9E348E lock enter 0x349E,0x8E ; 不允许锁定前缀
00D7B24C F9 stc
00D7B24D 5C pop esp
00D7B24E 1AB7 1EDEF453 sbb dh,byte ptr ds:[edi+0x53F4DE1E]
00D7B254 0E push cs
00D7B255 A4 movs byte ptr es:[edi],byte ptr ds:[esi]
00D7B256 97 xchg eax,edi
00D7B257 6C ins byte ptr es:[edi],dx
00D7B258 B2 11 mov dl,0x11
00D7B25A C8 BF9C56 enter 0x9CBF,0x56
先想想其他办法,既然是白名单,内存里面看有没有,回到401000搜索一下字符串。
发现了2个列表。估计是白名单了。
但是感觉怎么才这么点账号。可能还有个列表不在text区段。
再找了一下,又发现了一个列表:
剩下的问题很简单了,内存修改。
参考了《加密解密第三版》18.2.4 DLL劫持技术的例子稍微改改就能用了。
#include <Windows.h>
#define EXTERNC extern "C"
#define NAKED __declspec(naked)
#define EXPORT __declspec(dllexport)
#define ALCPP EXPORT NAKED
#define ALSTD EXTERNC EXPORT NAKED void __stdcall
#define ALCFAST EXTERNC EXPORT NAKED void __fastcall
#define ALCDECL EXTERNC EXPORT NAKED void __cdecl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <list>
using namespace std;
list<MEMORY_BASIC_INFORMATION> MemList ;
SYSTEM_INFO SysInfo ;
MEMORYSTATUS MemStatus ;
#include <TlHelp32.h>
#define STRINGLEN 8
BOOL bRunning = FALSE;
BOOL bFind=FALSE;
DWORD WINAPI SearchAndModify(LPVOID lParam)
{
#ifdef _DEBUG
CHAR tip[100]={0};
#endif
DWORD dwSAddr=0x401000;
BYTE pHeader[2]={0x33,0xC0};
if(bRunning)
return 1;
bRunning=TRUE;
Sleep(5000);
while(1)
{
if(memcmp((LPVOID)dwSAddr, pHeader, 2)==0)//先让壳把程序解压后再搜索内存
break;
Sleep(1000);
}
GetSystemInfo ( &SysInfo ) ;
HANDLE hProcess = INVALID_HANDLE_VALUE;
//第一步 查询
MEMORY_BASIC_INFORMATION MemBaseInfo ;
BYTE pPatternBuffer[STRINGLEN]={0x34,0x34,0x31,0x32,0x35,0x32,0x39,0x37};//原始账号
BYTE pWrite[STRINGLEN]= {0x31,0x34,0x36,0x32,0x32,0x30,0x38,0x34};//修改后的账号(随便写的,不是我的号)
while(1)
{
DWORD dwInfoSize = sizeof(MEMORY_BASIC_INFORMATION) ;
DWORD dwCurPos =(DWORD)SysInfo.lpMinimumApplicationAddress ;
DWORD dwMax=0x06000000;//(DWORD)SysInfo.lpMaximumApplicationAddress ;
while ( dwCurPos < dwMax)
{
VirtualQuery((LPVOID)dwCurPos, &MemBaseInfo, dwInfoSize ) ;// 查询进程的指定地址的状态信息
if(MEM_COMMIT==MemBaseInfo.State && PAGE_READWRITE==MemBaseInfo.Protect)//检测是否为“提交”状态 检测是否可读
{
#ifdef _DEBUG
wsprintf(tip,"base:%X ,size:%X ReadWrite",MemBaseInfo.BaseAddress,MemBaseInfo.RegionSize);
OutputDebugString(tip);
#endif
if(MemBaseInfo.RegionSize==0x100000)//检测内存大小
goto start;//跳出2层while
}
// 定位到下一个区域
dwCurPos = (DWORD)MemBaseInfo.BaseAddress + MemBaseInfo.RegionSize ;
}
Sleep(1000);
}
start:
MEMORY_BASIC_INFORMATION *p=&MemBaseInfo;
LPBYTE lpAddr = (LPBYTE )((DWORD)p->BaseAddress) ;
#ifdef _DEBUG
wsprintf(tip,"Find memory: base:%X ,size:%X",lpAddr,p->RegionSize);
OutputDebugString(tip);
#endif
DWORD dwSize = 0, dwReadBytes = 0 ;
while(1)
{
for ( int i = 0; i < p->RegionSize; i++ )
{
#ifdef _DEBUG
wsprintf(tip,"lpAddr[i]:%X",&lpAddr[i]);
OutputDebugString(tip);
#endif
if(memcmp(&lpAddr[i], pPatternBuffer, STRINGLEN)==0)
{
if(&lpAddr[i]==pPatternBuffer)//字符本身不改
continue;
bFind=TRUE;
DWORD dwWriteBytes = 0 ;
#ifdef _DEBUG
OutputDebugString("Find success!!");
#endif
//VirtualProtect ( (LPVOID)&lpAddr[i], STRINGLEN, PAGE_READWRITE, &dwOldProtect ) ;
memcpy(&lpAddr[i],pWrite,STRINGLEN);
//VirtualProtect ( (LPVOID)&lpAddr[i], STRINGLEN, dwOldProtect, NULL ) ;
continue;
}
}
if(bFind)
break;
else
Sleep(1000);
}
OutputDebugString("over");
return 0;
}
namespace MemCode
{
HMODULE m_hModule = NULL; // 原始模块句柄
DWORD m_dwReturn[500] = {0}; // 原始函数返回地址
// 加载原始模块
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH]={0};
TCHAR tzTemp[MAX_PATH]={0};
GetSystemDirectory(tzPath, sizeof(tzPath));
strcat(tzPath,"\\lpk.dll");
m_hModule = LoadLibrary(tzPath);
if (m_hModule == NULL)
{
wsprintf(tzTemp, TEXT("无法加载 %s,程序无法正常运行。"), tzPath);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
}
return (m_hModule != NULL);
}
// 释放原始模块
inline VOID WINAPI Free()
{
if (m_hModule)
{
FreeLibrary(m_hModule);
}
}
// 获取原始函数地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
TCHAR szProcName[16]={0};
TCHAR tzTemp[MAX_PATH]={0};
if (m_hModule == NULL)
{
if (Load() == FALSE)
{
ExitProcess(-1);
}
}
fpAddress = GetProcAddress(m_hModule, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintf(szProcName, "%d", pszProcName);
pszProcName = szProcName;
}
wsprintf(tzTemp, TEXT("无法找到函数 %hs,程序无法正常运行。"), pszProcName);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
ExitProcess(-2);
}
return fpAddress;
}
}
win7不加载这个dll,改了一下注册表重启就好了。测试了一下,注册成功。
然后这份代码有个bug,运行后可能会崩溃,内存错误,也可能不崩溃,我后来改好了。我故意留给大家当做思考题吧。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!