好友
阅读权限100
听众
最后登录1970-1-1
|
爱飞的猫
发表于 2015-9-1 21:30
本帖最后由 云在天 于 2019-6-7 13:13 编辑
参考帖子:.NET破解之太乐地图下载器【非暴破】 <- 软件介绍相关请参考这篇
第一次写破解过程,如有错误还请指出。
★★ 仅供学习交流之用 请勿用于商业用途 ★★
单词表/工具
IDE: 开发环境,比如商用的 Visual Studio,免费开源的 Sharp Develop。
Encrypt: 加密
String: 字符串
Dog: 狗,在此处可理解为加密狗
.NET: 微软开发的变成框架,可使用 .NET Reflector 进行反编译。
使用到的逆向工具可以在爱盘找到:http://down.52pojie.cn/Tools/NET/
IDE 请自行搜索 :3
一、拆解文件,寻找验证
脱壳这个很简单 扔到 de4dot 神器自动脱掉了。
把处理后的文件扔到 .NET Reflector 里面,右键模组 -> Go To Entry Point 进入入口点
观察代码,程序在入口点通过建立互斥体检测程序是否已经运行,然后进入「MainForm」窗口。
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | [STAThread]
private static void Main()
{
try
{
bool flag;
Mutex mutex = new Mutex( true , Application.ProductName, out flag);
if (flag)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
Class2.np6LKJCzZAthI();
Application.Run( new MainForm());
mutex.ReleaseMutex();
}
else
{
MessageBox.Show( null , "软件已开启,请确认AZMap.exe进程关闭后再启动软件。" , "提示" , MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Application.Exit();
}
}
catch (Exception exception)
{
LogUtil.Error(exception.Message, exception);
}
}
|
在主窗口初始化的代码内,「初始化运行环境」的下面有一行很可疑,「InitLicense 初始化授权」:
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | public MainForm()
{
Class2.np6LKJCzZAthI();
this ._useCache = true ;
this ._useHistory = true ;
this .lastPoint = new PointLatLng();
Control.CheckForIllegalCrossThreadCalls = false ;
this ._loginWindow = new Login();
new Thread( new ThreadStart( this .startLoginWindow)).Start();
this .setLoginWindowsStatus( "初始化运行环境..." );
this .InitializeComponent();
this .InitLicense();
this ._commands = new MetroCommands();
|
看到代码后,可以发现我们没有找错,许可授权在这里读取:
[C#] 纯文本查看 复制代码 1 2 3 4 5 6 7 8 9 | private void InitLicense()
{
try
{
SN.RegisterProductCode = "wfg783X8Joo=" ;
LicenseType lic = SN.Registered(RegisterProduct.Desktop);
this .RefreshFormTitle(lic);
this .StartDogListener();
|
SN 全名 Serial Number[序列号],这个类名也很直白,就是我们研究的目标了。
二、拆解验证,糊点代码
点进去 SN 这个类,然后单击 Reflector 界面最底部的「Expand Methods」展开这个类所有代码,方便分析:
把全部文件拷贝到 Visual Studio 或文字编辑器备份,定位到 Registered 这个方法:
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 | public static LicenseType Registered(RegisterProduct regProduct = 1)
{
if (NTFind() == 0)
{
if (NTLogin() && NTReadIv())
{
return NTReadLicense();
}
return LicenseType.Trial;
}
string path = GetLocalAZMapPath(regProduct) + "azmap_4" ;
string str2 = GetLocalPath(regProduct) + "azmap_check_4" ;
|
这段代码,表示程序会优先检查加密狗内部的授权。若是加密狗不存在,则进行序列号的校验。
把狗相关的函数提取出来,分析一下调用:
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | private static int NTFind()
{
return NT88API.NTFindFirst(NTCode);
}
private static bool NTLogin()
{
bool flag = false ;
try
{
if (NT88API.NTLogin(string_0) == 0)
{
flag = true ;
}
}
catch (Exception)
{
}
return flag;
}
private static bool NTReadIv()
{
bool flag = false ;
try
{
byte [] pData = new byte [7];
if ((NT88API.NTRead(0, 7, pData) == 0) && (Encoding.Default.GetString(pData) == NTIv))
{
flag = true ;
}
}
catch (Exception)
{
}
return flag;
}
|
NTFind: 调用 API 「NTFindFirst」 查询第一个加密狗的存在,返回 0 表示成功。
NTLogin: 调用 API 「NTLogin」 尝试登录,返回 0 表示成功 (加了容错处理,所以代码看起来比较怪),方法返回 true 表示成功。
NTReadIv: 调用 API 「NTRead」 读入加密狗数据,并与内置的密文进行比对验证,方法返回 true 表示获取成功并验证通过。
NTReadLicense: 读取狗上面的数据,根据数据返回相应的授权信息。
前两个,直接在 DLL 修改为返回 0 即可:
后面一个有点麻烦,因为需要读取数据到内存进行验证,而数据又不能凭空出现。
把「NTReadIv」这个方法的功能翻译成白话大概就是:
1. 申请 7 个字节的数据块。
2. 读取狗的数据:从 0 开始读取 7 个字节,读到的内容写到刚才申请的数据块里面。
3. 如果数据块的内容与「NTIv」这个变量的内容一致则验证成功,否则失败。
而在此之前,我们需要知道所谓的密文是什么内容;回到构建函数 (SN -> .cctor)
[C#] 纯文本查看 复制代码 1 2 3 4 5 6 7 | static SN()
{
Class3.lQUOYnkznSKeP();
NTCode = EncryptString.ThisIsLegalString( "h9puNortcwP/Y2Kl6MDz4A0obLNWW4uD" );
string_0 = EncryptString.ThisIsLegalString( "kWzdAyI6OhVUwAE69SdTMW4skDH2i+uEM2mrsvDGcdiv2wWi2xU8/A==" );
NTIv = EncryptString.ThisIsLegalString( "jztU+R63Ke4=" );
|
到这里就比较简单了,进去 EncryptString 这个类全部拷贝到 IDE,然后简单做一个界面 [自行美化吧~]:
解密按钮的事件代码按照程序里的写法糊进去就行:
[C#] 纯文本查看 复制代码 1 2 3 4 | private void btnDecrypt_Click( object sender, EventArgs e)
{
txtPlain.Text = EncryptString.ThisIsLegalString(txtSource.Text);
}
|
解密后,可以看到这样的内容:
NTCode: 1234567890arctiler
string_0: 3eee0d60fbb583e1bf33c6990d5f9e0d
NTIv: azmap09
顺便: SN.RegisterProductCode 的文字解密后是「desktop」,可理解为桌面版。
此时,可以知道加密狗从 0 到 7 的内容是 azmap09 这一串字符了,记录下来:
当这三个验证都通过后,程序就会调用 NTReadLicense 读取狗上面记录的内容;
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 | private static LicenseType NTReadLicense()
{
LicenseType free = LicenseType.Free;
try
{
byte [] pData = new byte [2];
if (NT88API.NTRead(0x10, 0x12, pData) != 0)
{
return free;
}
|
又看到了熟悉的 NTRead 函数。此时,读取的是 0x10(16) 开始的两个字节一直到 0x12 的位置。
接着往下看,一系列的判断分支,根据分支内容判断授权版本。在此之前,调查一下程序定义的授权类型:
[C#] 纯文本查看 复制代码 1 2 3 4 5 6 7 8 9 | public enum LicenseType
{
Enterprise,
Professional,
Standerd,
Trial,
Free,
Given
}
|
目标很清晰,就是我们功能最多、最全的「企业版」。
因此,把代码优化、精简一下:
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | private static LicenseType NTReadLicense()
{
LicenseType license = LicenseType.Free;
byte [] pData = new byte [2];
if (NT88API.NTRead(0x10, 0x12, pData) != 0)
{
return LicenseType.Free;
}
string str = Encoding.Default.GetString(pData);
if (str != null )
{
switch (ApplicationConfig.Version) {
case 0:
switch (str) {
case "-1" :
license = LicenseType.Enterprise;
break ;
case "-3" :
license = LicenseType.Professional;
break ;
case "-5" :
license = LicenseType.Standerd;
break ;
default :
license = LicenseType.Free;
}
break ;
case 1:
switch (str) {
case "-2" :
license = LicenseType.Professional;
break ;
case "-4" :
license = LicenseType.Standerd;
break ;
default :
license = LicenseType.Free;
}
break ;
case 2:
if (str == "-9" ) {
license = LicenseType.Enterprise;
} else {
license = LicenseType.Free;
}
break ;
}
}
R.Instance.SetLicenseType(license);
R.Instance.SetDogLicenseType(license);
return license;
}
|
得出结论,加密狗授权信息是根据加密狗里面的数据以及 ApplicationConfig.Version 里面的数据判断的。而 Version 这个数据需要调查一下是 0、1 还是 2:
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | public static int get_Version()
{
int result = 1;
if (getValue( "Version" ) != null )
{
int .TryParse(getValue( "Version" ), out result);
}
return result;
}
private static string getValue( object key)
{
try
{
object obj2 = SQLiteHelper.ExecuteScalar(conn, "select value from Config where key = @key" , new SQLiteParameter[] { new SQLiteParameter( "@key" , key) });
if (obj2 == null )
{
return null ;
}
return obj2.ToString();
}
catch
{
return null ;
}
}
static ApplicationConfig()
{
Class3.lQUOYnkznSKeP();
conn = null ;
connLock = new object ();
inited = false ;
initedObject = new object ();
if (conn == null )
{
lock (connLock)
{
if (conn == null )
{
conn = SQLiteHelper.GetSQLiteConn( "Config" );
if (!SQLiteHelper.ContainTable(conn, "Config" ))
{
SQLiteHelper.ExcuteNoneQuery(conn, "create table Config(key nvarchar(256) primary key,value nvarchar(256))" , null , null );
}
}
}
}
}
|
而当我把数据库用工具打开时,却提示我数据库被加密或无效.. 走进去「GetSQLiteConn」看看怎么处理:
[C#] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 |
connection = new SQLiteConnection {
ConnectionString = builder.ToString()
};
connection.SetPassword(DB_PASSWORD);
connection.Open();
static SQLiteHelper()
{
Class3.lQUOYnkznSKeP();
DB_PASSWORD = EncryptString.ThisIsLegalString( "jztU+R63Ke4=" );
SQLITE_PAGE_SIZE = 0x8000;
}
|
熟悉的加密文字,扔到工具里解密看看,还真是这个「azmap09」。
于是糊了几行代码,取得数据库储存的代码号是 0。因此如果想让版本为企业版,加密狗的内容必须是 -1 了。
三、戏弄程序,编写狗狗
首先摸清目标狗的导出函数:
然后编写个相同的导出函数;我这里首先用的是 C++
[C++] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #ifdef FAKELIB_EXPORTS
#define FAKELIB_API extern "C" __declspec(dllexport) int __stdcall
#else
#define FAKELIB_API __declspec(dllimport)
#endif
FAKELIB_API _NT3DESCBCDecrypt( char * vi, char * pDataBuffer, int length);
FAKELIB_API _NT3DESCBCEncrypt( char * vi, char * pDataBuffer, int length);
FAKELIB_API _NTCheckLicense( int licenseCode);
FAKELIB_API _NTFindFirst( char * NTCode);
FAKELIB_API _NTGetHardwareID( char * hardwareID);
FAKELIB_API _NTLogin( char * LoginCode);
FAKELIB_API _NTLogout();
FAKELIB_API _NTRead( int address, int Length, char * pDataBuffer);
FAKELIB_API _NTWrite( int address, int Length, char * pDataBuffer);
|
[C++] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include <windows.h>
#include "FakeLib.h"
int __stdcall _NT3DESCBCDecrypt( char * vi, char * pDataBuffer, int length)
{
return 0;
}
int __stdcall _NT3DESCBCEncrypt( char * vi, char * pDataBuffer, int length)
{
return 0;
}
int __stdcall _NTCheckLicense( int licenseCode)
{
return 0;
}
int __stdcall _NTFindFirst( char * NTCode)
{
return 0;
}
int __stdcall _NTGetHardwareID( char * hardwareID)
{
return 0;
}
int __stdcall _NTLogin( char * LoginCode)
{
return 0;
}
int __stdcall _NTLogout()
{
return 0;
}
char * _dogData = "azmap09.........-1" ;
int __stdcall _NTRead( int address, int endAddress, char * pDataBuffer)
{
int size = endAddress - address;
DWORD oldProtect;
VirtualProtect(pDataBuffer, size, PAGE_READWRITE, &oldProtect);
memcpy (pDataBuffer, _dogData + address, size);
VirtualProtect(pDataBuffer, size, oldProtect, &oldProtect);
return 0;
}
int __stdcall _NTWrite( int address, int Length, char * pDataBuffer)
{
return 0;
}
|
最后带上修正导出表的 def 文件:LIBRARY FakeLib
EXPORTS
NT3DESCBCDecrypt=_NT3DESCBCDecrypt
NT3DESCBCEncrypt=_NT3DESCBCEncrypt
NTCheckLicense=_NTCheckLicense
NTFindFirst=_NTFindFirst
NTGetHardwareID=_NTGetHardwareID
NTLogin=_NTLogin
NTLogout=_NTLogout
NTRead=_NTRead
NTWrite=_NTWrite
编译后更名 NT88.dll,扔进去看看:
企业版。嘛,就这样了 :3
附、汇编重写
使用 FASM 制作,生成的文件超级小。
还有个原因就是 MSVC 编译器会加一些奇怪的导出函数不会改.. 太不美观了!
[Asm] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | format PE GUI 4.0 DLL
entry DllMain
include 'win32ax.inc'
section '.text' code readable executable
proc DllMain hinstDLL,fdwReason,lpvReserved
mov al , 1
ret
endp
proc NTRead address,endAddress,pDataBuffer
local oldProtect: DWORD
push esi
push edi
mov ecx , [endAddress]
mov esi , [address]
sub ecx , esi
push ecx
mov edi , [pDataBuffer]
lea eax , [oldProtect]
push eax
push PAGE_READWRITE
push ecx
push edi
call [VirtualProtect]
mov ecx , [ esp ]
mov esi , _dogData
add esi , [address]
cld
rep movsb
pop ecx
lea eax , [oldProtect]
push eax
push [oldProtect]
push ecx
sub edi , ecx
push edi
call [VirtualProtect]
pop edi
pop esi
xor eax , eax
ret
endp
proc NT3Arg a1, a2, a3
xor eax , eax
ret
endp
proc NT1Arg a1
xor eax , eax
ret
endp
proc NT0Arg
xor eax , eax
retn
endp
section '.data' data readable writeable
_dogData db 'azmap09.........-1'
section '.idata' import data readable writeable
library kernel, 'KERNEL32.DLL'
import kernel,\
VirtualProtect, 'VirtualProtect'
section '.edata' export data readable
export 'NT88.DLL' ,\
NT3Arg, 'NT3DESCBCDecrypt' ,\
NT3Arg, 'NT3DESCBCEncrypt' ,\
NT1Arg, 'NTCheckLicense' ,\
NT1Arg, 'NTFindFirst' ,\
NT1Arg, 'NTGetHardwareID' ,\
NT1Arg, 'NTLogin' ,\
NT0Arg, 'NTLogout' ,\
NTRead, 'NTRead' ,\
NT3Arg, 'NTWrite'
section '.reloc' fixups data readable discardable
|
★★ 仅供学习交流之用 请勿用于商业用途 ★★
P.s. 没看错的话 NT88.dll 还可以直接写数据到狗里面。
太乐逆向辅助工具[C# 源码-VS2015].7z
(414.43 KB, 下载次数: 651)
fake_NT88_fix1.7z
(1.63 KB, 下载次数: 968)
Fix1:
修正 Windows 7+ 系统的崩溃问题,感谢 my1229 的测试。
--- 存档用,有错误;修正版请看上面
fake_NT88.7z
(1.55 KB, 下载次数: 416)
|
免费评分
-
查看全部评分
|