如題所示,U3D的Mono解密,這個玩意我昨晚在我的技術群里貌似看到說U3D是要棄用了Mono這個機制,也不知道是真是假,趁著還沒棄用,我也就來說說這個機制的一個另類解密,
關于Mono這東西我就不啰嗦了,我就直接進入正題,眾所周知,Mono的加密主要是針對 Assembly-CSharp.dll,此DLL包含了游戲的所有功能性函式,并且可以通過工具dnSpy.exe加載后進行查看,

此DLL公開等于說原始碼公開,可以通過C#工程引入該DLL自寫一個GameObject注入到游戲里呼叫游戲自帶函式實作作弊,


大多的加密手段就是對這DLL進行二進制處理,也就是把檔案位元組給進行處理,對于這種加密的處理方式很簡單,Mono.dll是U3D用來初始化并加載dll的一個模塊,他里面有一個函式mono_image_open_from_data_with_name,這里放一下他的函式代碼
MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{
return mono_image_open_from_data_internal (data, data_len, need_copy, status, refonly, FALSE, name);
}
MonoImage *
mono_image_open_from_data_internal (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, gboolean metadata_only,
const char *name)
{
MonoCLIImageInfo *iinfo;
MonoImage *image;
char *datac;
if (!data || !data_len) {
if (status)
*status = MONO_IMAGE_IMAGE_INVALID;
return NULL;
}
datac = data;
if (need_copy) {
datac = (char *)g_try_malloc (data_len);
if (!datac) {
if (status)
*status = MONO_IMAGE_ERROR_ERRNO;
return NULL;
}
memcpy (datac, data, data_len);
}
image = g_new0 (MonoImage, 1);
image->raw_data = datac;
image->raw_data_len = data_len;
image->raw_data_allocated = need_copy;
image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);
iinfo = g_new0 (MonoCLIImageInfo, 1);
image->image_info = iinfo;
image->ref_only = refonly;
image->metadata_only = metadata_only;
image->ref_count = 1;
image = do_mono_image_load (image, status, TRUE, TRUE);
if (image == NULL)
return NULL;
return register_image (image);
}
看不懂沒關系,我們只需要關注他的data,data_len,name這三個引數,這三個分別表示當前被加載模塊的二進制內容,二進制長度,模塊名,大部分游戲廠商都會在這里判斷模塊名是否為Assembly-CSharp,然后進行二進制內容解密,那么只需要用除錯工具在這個函式下段,然后在這里分析結束的位置,然后直接dump即可,這里放一下大概代碼

直接在解密完畢的位置下斷,然后把rdi給dump出來就行,得到的就是解密后的dll,也可以自寫腳本,

____________________________________________________________________________________________________________________________________________________________
上面介紹了Assembly-CSharp的一種加密和解密方式,雖說是加密,但是非常下飯,基本有手就行,今天就來說說另一種加密方式,小白鼠是國內某款游戲,現在應該應該不開放了,但是游戲依舊躺在我硬碟里,該游戲同樣是加密Assembly-CSharp,但是有個不同點就是該檔案只有1kb,這個就很可疑,因為這等于說這檔案是空的,用十六進制打開檔案也能看出里面無內容,那么游戲很可能是聯網獲取新的,或者是記憶體釋放,那么除了去分析后解密,就沒有辦法拿到解密后的檔案嗎,答案是否定的,這個時候就需要上點硬技術了,
static MonoImage *
register_image (MonoImage *image)
{
MonoImage *image2;
GHashTable *loaded_images = get_loaded_images_hash (image->ref_only); // 重點關注物件
mono_images_lock ();
image2 = (MonoImage *)g_hash_table_lookup (loaded_images, image->name);
if (image2) {
/* Somebody else beat us to it */
mono_image_addref (image2);
mono_images_unlock ();
mono_image_close (image);
return image2;
}
GHashTable *loaded_images_by_name = get_loaded_images_by_name_hash (image->ref_only);
g_hash_table_insert (loaded_images, image->name, image); // 重點關注物件
if (image->assembly_name && (g_hash_table_lookup (loaded_images_by_name, image->assembly_name) == NULL))
g_hash_table_insert (loaded_images_by_name, (char *) image->assembly_name, image);
mono_images_unlock ();
return image;
}
這里可以清楚的看到,U3D直接把需要的加載的模塊插入了一個HashTable,然后完成模塊的一個裝載,這里我們先不管這個模塊進行了如何加密和解密,既然你要扔給U3D托管,那么你不可能扔了一個加密模塊的給U3D托管,除非你的U3D引擎進行了一個大規模的魔改,所以說,我們只需要找到這個HashTable,然后去遍歷一下里面的模塊,我們是不是就能拿到解密后的Assembly-CSharp,答案是肯定的,那么現在重點物件從 如何解密Assembly-CSharp變成了 如何使用loaded_images,
我們應該如何去找到這個loaded_images?沒辦法,上分析,除錯器打開游戲跳到mono_image_open_from_data_with_name這個函式然后對照原始碼進行分析,

從原始碼得知,register_image是最后一個呼叫的函式,我們直接找到最后一個函式,進去后分析得知,


拿到loaded_images后,我們下一步就是要去遍歷這個HashTabel,兩個辦法,一個是自己去搭建GHashTable庫,然后自己去跑一遍,第二個辦法,自己去分析g_hash_table_insert函式的流程,我選擇后者 =,=
Ida打開mono.dll后搜索g_hash_table_insert,有一個

進去F5后無腦暴力分析,

分析了一個大概流程后,打開CE,用資料結構工具分析驗證看看

很明顯,這里應該就是模塊串列,我們再隨便進去一個去看看,通過剛剛IDA的分析,0x0是模塊名,0x8是image,


和我們剛剛分析的一致,現在我們還差一個東西,那就是image的結構,我們在原始碼查找一下,
struct _MonoImage {
………
guint32 raw_data_len;
char *raw_data; //模塊二進制
char *name; //模塊名
…………
}
這里就貼出重要的,問題來了,我們怎么知道他的偏移,別忘了,這里有
MonoImage *
mono_image_open_from_data_internal (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, gboolean metadata_only,
const char *name)
{
MonoCLIImageInfo *iinfo;
MonoImage *image;
char *datac;
if (!data || !data_len) {
if (status)
*status = MONO_IMAGE_IMAGE_INVALID;
return NULL;
}
datac = data;
if (need_copy) {
datac = (char *)g_try_malloc (data_len);
if (!datac) {
if (status)
*status = MONO_IMAGE_ERROR_ERRNO;
return NULL;
}
memcpy (datac, data, data_len);
}
image = g_new0 (MonoImage, 1);
image->raw_data = datac; <<<<<<<<<<<<<<<
image->raw_data_len = data_len; <<<<<<<<<<<<<<<
image->raw_data_allocated = need_copy;
image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name); <<<<<<<<<<<<<<<
iinfo = g_new0 (MonoCLIImageInfo, 1);
image->image_info = iinfo;
image->ref_only = refonly;
image->metadata_only = metadata_only;
image->ref_count = 1;
image = do_mono_image_load (image, status, TRUE, TRUE);
if (image == NULL)
return NULL;
return register_image (image);
}
我們ida回傳去分析
__int64 __fastcall sub_18006CA20(__int64 data, unsigned int data_len, int need_cpy, _DWORD *status, __int64 refonly, char metadata_only, __int64 name)
{
__int64 len; // r14
_DWORD *v8; // rdi
char v9; // si
__int64 data_; // rbx
__int64 data__; // rbp
__int64 v12; // rax
__int64 result; // rax
__int64 v14; // rbx
__int64 m_name; // rax
signed __int64 v16; // rax
__int64 v18; // rax
__int64 v19; // rax
len = data_len;
v8 = status;
v9 = need_cpy;
data_ = data;
if ( data && data_len )
{
data__ = data;
if ( need_cpy )
{
v12 = sub_180004B80(data_len);
data__ = v12;
if ( !v12 )
{
if ( v8 )
*v8 = 1;
return 0i64;
}
sub_180314D40(v12, data_, len);
}
v14 = sub_180004AE0(1856i64);
*(_BYTE *)(v14 + 0x1C) &= 0xFDu;
*(_BYTE *)(v14 + 0x1C) |= 2 * (v9 & 1);
*(_QWORD *)(v14 + 0x10) = data__; //data
*(_DWORD *)(v14 + 0x18) = len; //data_len
if ( name )
{
v16 = -1i64;
while ( *(_BYTE *)(name + v16++ + 1) != 0 )
;
m_name = sub_180004A10(name, (unsigned int)(v16 + 1));
}
else
{
m_name = sub_180006230("data-%p", data__);
}
*(_QWORD *)(v14 + 0x20) = m_name; //name
v18 = sub_180004AE0(408i64);
*(_BYTE *)(v14 + 0x1C) &= 0xBFu;
*(_BYTE *)(v14 + 0x1D) &= 0xFEu;
*(_QWORD *)(v14 + 0x50) = v18;
*(_DWORD *)v14 = 1;
*(_BYTE *)(v14 + 0x1C) |= (refonly & 1) << 6;
*(_BYTE *)(v14 + 0x1D) |= metadata_only & 1;
v19 = sub_1800699B0(v14, v8, 1i64);
if ( !v19 )
return 0i64;
result = sub_18006D6D0(v19);
}
else
{
if ( status )
*status = 3;
result = 0i64;
}
return result;
}
可知
0x10 raw_data
0x18 raw_data_len
0x20 name
我們再用CE看看

可以看到,直接是一個標準的PE檔案,說明這里存的是二進制內容,

到這里,我們就已經分析完了loaded_images的一個記憶體結構,接下來就是寫一個遍歷工具,然后打開游戲,運行工具,等待DLL生成,這里我用易語言寫了一個,


運行,,,
原檔案

dump檔案

Dnspy打開

到這里,就已經完美提取了,這里的話,個人覺得,也可以提取一些第三方掛鉤mono的DLL,但是本人沒去試過,有興趣的可以去試試,輸入法和鍵盤同時出了問題,打字很難受,如果文章中存在錯別字,,,見諒,,,,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/296960.html
標籤:其他
