朋友们好啊,我是混元编程门掌门人马保国,刚才有个朋友问我马老师发生什么事了,我说怎么回事?给我发了一个张截图,我一看,哦,源赖氏佐田,有一个年轻人,10多岁,塔说,塔写了个unity(SSJJ)游戏的外挂,还是注入直接调用SDK,功能十分牛逼,问我马老师你能不能检测出我?我说可以,我说你用mono注入不好用,他不服气,我说小朋友,你两个手指来跟我一根手指比敲代码,我来检测你,你来过检测,他敲不动,他说我这个检测没用,我说我这个有用,这是化劲,传统功夫讲化劲的,他说要和我试试,我说可以,我一说,他啪就站起来了,很快啊,然后上来就是一个嫖开源代码注入器,dnspy反编译游戏代码,调用SDK做挂,我大意了啊,没有闪。它的挂给我游戏蹭了一下,我当时就流眼泪了,但是没关系啊,我两分钟过后就好了,把他检测了。(-_-以上是废话,可以掠过)
unity游戏使用的c#虚拟机是mono,一般注入型又能调用SDK的c#dll外挂一般都会调用mono的函数来完成注入,通常来说流程是这样
在这里我们有两个方面可以努力一下:
其一是在注入时,我们可以通过hook mono的载入dll函数,验证这个dll是否属于游戏自身的dll,来达到防止注入的目的;亦或是将自己的所有dll都以自己知道的加密方式加密,然后载入dll时按照加密方式解密,这样如果一个未经加密的dll想要注入进来,经过解密流程后就变得不可用
其二便是主动遍历模块,mono载入c#模块跟windows载入dll模块类似,会维护一个全局的链表,存储所有模块的信息,我们遍历这个链表即可得到所有载入dll的信息,然后根据需要判断该dll是否为可疑dll
从mono下载最新的mono源码,用VS打开,直接来到载入镜像函数mono_image_open_from_data_with_name
处
可以看到,没有名字的镜像调用这个函数载入时,mono会自动为其分配一个名字,这个名字格式就是data-镜像内存地址
继续往下跟进,发现调用了do_mono_image_load
函数,这里就是镜像载入的核心函数,最后调用的register_image
是我们关注的重点,因为执行了这个函数,image就彻底被加入到了mono维护的链表中,我们点进去看一下这个函数
注意到代码的第18-22行完成了一个 g_hash_table_insert
的操作,而在glib中,hash_table是一个类似于map的映射表,我们可以看到,这个插入操作往loaded_images
这个表里面插入了一项,而这个loaded_images
可以在第5行代码看到是一个全局变量
于是乎我们找到了这个全局链表的位置
image.c 47-51
他们的初始化位于mono_images_init
所以我们可以通过特征很方便的定位到这两个表的地址,然后用glib开源的方法遍历即可
另外,还有一个表也很有用,可以作为检测的途径,就是 assembly.c
中的 loaded_assemblies
assembly.c 114-117
定位变量
遍历
g_hash_table_foreach
和g_list_foreach
等都是glib的函数,在mono的源码里有,自己去CV吧
遍历回调函数
小伙子忙说对不起对不起,我不懂规矩啊,我说他是乱打的,他可不是乱打啊,铮铮鞭腿左刺拳训练有素,后来他说他练过三四年泰拳,看来是有备而来
他说他把链表断了,然后把PE头也清掉了,还把所有字符串都清空了
我去~
这个年轻人,不讲武德
来,骗!
来,偷袭!
我69岁的老同志。
这好吗?这不好。我劝这位年轻人,耗子尾汁。好好反思。以后不要再犯这样的小聪明。武林要以和为贵,要讲武德,不要搞窝里斗。
远程写入dll数据
-
> 外部获取mono导出函数表地址
-
> 写入调用mono函数的shellcode
-
> 远程创建线程调用shellcode
-
> 查询加载结果
远程写入dll数据
-
> 外部获取mono导出函数表地址
-
> 写入调用mono函数的shellcode
-
> 远程创建线程调用shellcode
-
> 查询加载结果
MonoImage
*
mono_image_open_from_data_with_name (char
*
data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus
*
status, gboolean refonly, 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
=
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
-
>ref_count
=
1
;
image
=
do_mono_image_load (image, status, TRUE, TRUE);
if
(image
=
=
NULL)
return
NULL;
return
register_image (image);
}
MonoImage
*
mono_image_open_from_data_with_name (char
*
data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus
*
status, gboolean refonly, 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
=
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
-
>ref_count
=
1
;
image
=
do_mono_image_load (image, status, TRUE, TRUE);
if
(image
=
=
NULL)
return
NULL;
return
register_image (image);
}
static MonoImage
*
register_image (MonoImage
*
image)
{
MonoImage
*
image2;
GHashTable
*
loaded_images
=
image
-
>ref_only ? loaded_images_refonly_hash : loaded_images_hash;
mono_images_lock ();
image2
=
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;
}
g_hash_table_insert (loaded_images, image
-
>name, image);
if
(image
-
>assembly_name && (g_hash_table_lookup (loaded_images, image
-
>assembly_name)
=
=
NULL))
g_hash_table_insert (loaded_images, (char
*
) image
-
>assembly_name, image);
mono_images_unlock ();
return
image;
}
static MonoImage
*
register_image (MonoImage
*
image)
{
MonoImage
*
image2;
GHashTable
*
loaded_images
=
image
-
>ref_only ? loaded_images_refonly_hash : loaded_images_hash;
mono_images_lock ();
image2
=
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;
}
g_hash_table_insert (loaded_images, image
-
>name, image);
if
(image
-
>assembly_name && (g_hash_table_lookup (loaded_images, image
-
>assembly_name)
=
=
NULL))
g_hash_table_insert (loaded_images, (char
*
) image
-
>assembly_name, image);
mono_images_unlock ();
return
image;
}
/
*
*
Keeps track of the various assemblies loaded
*
/
static GHashTable
*
loaded_images_hash;
static GHashTable
*
loaded_images_refonly_hash;
/
*
*
Keeps track of the various assemblies loaded
*
/
static GHashTable
*
loaded_images_hash;
static GHashTable
*
loaded_images_refonly_hash;
/
*
*
*
mono_images_init:
*
*
Initialize the
global
variables used by this module.
*
/
void
mono_images_init (void)
{
InitializeCriticalSection (&images_mutex);
loaded_images_hash
=
g_hash_table_new (g_str_hash, g_str_equal);
loaded_images_refonly_hash
=
g_hash_table_new (g_str_hash, g_str_equal);
debug_assembly_unload
=
g_getenv (
"MONO_DEBUG_ASSEMBLY_UNLOAD"
) !
=
NULL;
mutex_inited
=
TRUE;
}
/
*
*
*
mono_images_init:
*
*
Initialize the
global
variables used by this module.
*
/
void
mono_images_init (void)
{
InitializeCriticalSection (&images_mutex);
loaded_images_hash
=
g_hash_table_new (g_str_hash, g_str_equal);
loaded_images_refonly_hash
=
g_hash_table_new (g_str_hash, g_str_equal);
debug_assembly_unload
=
g_getenv (
"MONO_DEBUG_ASSEMBLY_UNLOAD"
) !
=
NULL;
mutex_inited
=
TRUE;
}
/
*
*
keeps track of loaded assemblies
*
/
static GList
*
loaded_assemblies
=
NULL;
/
*
*
keeps track of loaded assemblies
*
/
static GList
*
loaded_assemblies
=
NULL;
PVOID LocateLoadedAssemblies() {
/
*
Function: mono_assembly_foreach
Byte:
56
push esi
57
push edi
BF
84
B4
20
10
mov edi, offset stru_1020B484
57
push edi; lpCriticalSection
FF
15
4C
61
14
10
call ds : EnterCriticalSection
FF
35
7C
B4
20
10
push loaded_assemblies
*
/
PVOID monoModule
=
GetModuleHandleA(
"mono.dll"
);
PVOID mono_assembly_foreach
=
GetProcAddress((HMODULE)monoModule,
"mono_assembly_foreach"
);
vector<ULONG64> res;
int
Cnt
=
SearchMemory((HANDLE)
-
1
, (char
*
)
"56 57 BF ?? ?? ?? ?? 57 ?? ?? ?? ?? ?? ?? FF 35 ?? ?? ?? ??"
, (unsigned
long
long
)mono_assembly_foreach, (unsigned
long
long
)((DWORD)mono_assembly_foreach
+
50
),
10
, res);
Print
(
"SigCnt2 = %d"
, Cnt);
if
(Cnt
=
=
1
) {
ULONG64 OffsetAddress
=
res[
0
]
+
16
;
int
LoadedAssemblies
=
*
(
int
*
)OffsetAddress;
Print
(
"LoadedAssemblies = %X"
, LoadedAssemblies);
return
(PVOID)(LoadedAssemblies);
}
return
0
;
return
0
;
}
PVOID LocateLoadedImagesHashAddress() {
/
*
Function: mono_image_init
Byte:
57
push edi
56
push esi
E8 FA
74
FC FF call sub_100012C0
57
push edi
56
push esi
A3 F4 B6
20
10
mov loaded_images_hash, eax
*
/
PVOID monoModule
=
GetModuleHandleA(
"mono.dll"
);
Print
(
"MonoModule = %X"
, monoModule);
PVOID mono_image_init
=
GetProcAddress((HMODULE)monoModule,
"mono_images_init"
);
Print
(
"mono_image_init = %X"
, mono_image_init);
vector<ULONG64> res;
int
Cnt
=
SearchMemory((HANDLE)
-
1
, (char
*
)
"57 56 E8 ?? ?? ?? ?? 57 56 A3 ?? ?? ?? ??"
, (unsigned
long
long
)mono_image_init, (unsigned
long
long
)((DWORD)mono_image_init
+
50
),
10
, res);
Print
(
"SigCnt = %d"
, Cnt);
if
(Cnt
=
=
1
) {
ULONG64 OffsetAddress
=
res[
0
]
+
10
;
int
LoadedImagesHashAddress
=
*
(
int
*
)OffsetAddress;
Print
(
"LoadedImagesHashAddress = %X"
, LoadedImagesHashAddress);
return
(PVOID)(LoadedImagesHashAddress);
}
return
0
;
return
0
;
}
PVOID LocateLoadedAssemblies() {
/
*
Function: mono_assembly_foreach
Byte:
56
push esi
57
push edi
BF
84
B4
20
10
mov edi, offset stru_1020B484
57
push edi; lpCriticalSection
FF
15
4C
61
14
10
call ds : EnterCriticalSection
FF
35
7C
B4
20
10
push loaded_assemblies
*
/
PVOID monoModule
=
GetModuleHandleA(
"mono.dll"
);
PVOID mono_assembly_foreach
=
GetProcAddress((HMODULE)monoModule,
"mono_assembly_foreach"
);
vector<ULONG64> res;
int
Cnt
=
SearchMemory((HANDLE)
-
1
, (char
*
)
"56 57 BF ?? ?? ?? ?? 57 ?? ?? ?? ?? ?? ?? FF 35 ?? ?? ?? ??"
, (unsigned
long
long
)mono_assembly_foreach, (unsigned
long
long
)((DWORD)mono_assembly_foreach
+
50
),
10
, res);
Print
(
"SigCnt2 = %d"
, Cnt);
if
(Cnt
=
=
1
) {
ULONG64 OffsetAddress
=
res[
0
]
+
16
;
int
LoadedAssemblies
=
*
(
int
*
)OffsetAddress;
Print
(
"LoadedAssemblies = %X"
, LoadedAssemblies);
return
(PVOID)(LoadedAssemblies);
}
return
0
;
return
0
;
}
PVOID LocateLoadedImagesHashAddress() {
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-12-20 23:44
被renbohan编辑
,原因: 原创