Skip to content

Commit e9c2ef9

Browse files
committed
added LM_UnloadModuleEx for linux
1 parent 14b0910 commit e9c2ef9

File tree

3 files changed

+135
-5
lines changed

3 files changed

+135
-5
lines changed

src/linux/module.c

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ LM_LoadModule(lm_string_t path,
140140
/********************************/
141141

142142
lm_bool_t
143-
find_dlopen_module_callback(lm_module_t *module, lm_void_t *arg)
143+
find_dlfcn_module_callback(lm_module_t *module, lm_void_t *arg)
144144
{
145145
static const char *name_matches[] = {
146146
"libc.", "libc-", "libdl.", "libdl-", "ld-musl-", "ld-musl."
@@ -194,7 +194,7 @@ LM_LoadModuleEx(const lm_process_t *process,
194194
return ret;
195195

196196
dlopen_mod.base = LM_ADDRESS_BAD;
197-
LM_EnumModulesEx(process, find_dlopen_module_callback, &dlopen_mod);
197+
LM_EnumModulesEx(process, find_dlfcn_module_callback, &dlopen_mod);
198198
if (dlopen_mod.base == LM_ADDRESS_BAD)
199199
return ret;
200200

@@ -216,9 +216,9 @@ LM_LoadModuleEx(const lm_process_t *process,
216216
ptlib.args[1] = RTLD_LAZY;
217217
if (process->bits == 64) {
218218
*(uint64_t *)&ptlib.stack[0] = (uint64_t)path_addr;
219-
*(int32_t *)&ptlib.stack[4] = (int32_t)RTLD_LAZY;
219+
*(int32_t *)&ptlib.stack[8] = (int32_t)RTLD_LAZY;
220220
} else {
221-
*(uint32_t *)&ptlib.stack[0] = (uint64_t)path_addr;
221+
*(uint32_t *)&ptlib.stack[0] = (uint32_t)path_addr;
222222
*(int32_t *)&ptlib.stack[4] = (int32_t)RTLD_LAZY;
223223
}
224224

@@ -292,3 +292,123 @@ LM_UnloadModule(const lm_module_t *module)
292292

293293
return LM_TRUE;
294294
}
295+
296+
/********************************/
297+
298+
typedef struct {
299+
lm_address_t dlopen_addr;
300+
lm_address_t dlclose_addr;
301+
} find_dlfcn_t;
302+
303+
lm_bool_t
304+
find_dlfcn_symbols_callback(lm_symbol_t *symbol, lm_void_t *arg)
305+
{
306+
static const char *dlopen_matches[] = {
307+
"__libc_dlopen_mode", "dlopen"
308+
};
309+
static const char *dlclose_matches[] = {
310+
"__libc_dlclose", "dlclose"
311+
};
312+
find_dlfcn_t *parg = (find_dlfcn_t *)arg;
313+
size_t i;
314+
315+
for (i = 0; i < LM_ARRLEN(dlopen_matches); ++i) {
316+
if (!strcmp(symbol->name, dlopen_matches[i])) {
317+
parg->dlopen_addr = symbol->address;
318+
}
319+
}
320+
321+
for (i = 0; i < LM_ARRLEN(dlclose_matches); ++i) {
322+
if (!strcmp(symbol->name, dlclose_matches[i])) {
323+
parg->dlclose_addr = symbol->address;
324+
}
325+
}
326+
327+
return (parg->dlopen_addr == LM_ADDRESS_BAD || parg->dlclose_addr == LM_ADDRESS_BAD) ? LM_TRUE : LM_FALSE;
328+
}
329+
330+
LM_API lm_bool_t LM_CALL
331+
LM_UnloadModuleEx(const lm_process_t *process,
332+
const lm_module_t *module)
333+
{
334+
lm_bool_t ret = LM_FALSE;
335+
lm_module_t dlfcn_mod;
336+
find_dlfcn_t dlfcn = { LM_ADDRESS_BAD, LM_ADDRESS_BAD };
337+
ptrace_libcall_t ptlib;
338+
long link_map_iter;
339+
struct link_map link_map;
340+
char path[LM_PATH_MAX];
341+
void *handle = NULL;
342+
long call_ret;
343+
344+
if (!process || !module)
345+
return ret;
346+
347+
dlfcn_mod.base = LM_ADDRESS_BAD;
348+
LM_EnumModulesEx(process, find_dlfcn_module_callback, &dlfcn_mod);
349+
if (dlfcn_mod.base == LM_ADDRESS_BAD)
350+
return ret;
351+
352+
LM_EnumSymbols(&dlfcn_mod, find_dlfcn_symbols_callback, &dlfcn);
353+
if (dlfcn.dlopen_addr == LM_ADDRESS_BAD || dlfcn.dlclose_addr == LM_ADDRESS_BAD)
354+
return ret;
355+
356+
/* Setup arguments both on the stack and on registers to prevent possible issues */
357+
ptlib.address = dlfcn.dlopen_addr;
358+
ptlib.args[0] = (long)NULL;
359+
ptlib.args[1] = RTLD_LAZY;
360+
if (process->bits == 64) {
361+
*(uint64_t *)&ptlib.stack[0] = (uint64_t)NULL;
362+
*(int32_t *)&ptlib.stack[8] = (int32_t)RTLD_LAZY;
363+
} else {
364+
*(uint32_t *)&ptlib.stack[0] = (uint32_t)NULL;
365+
*(int32_t *)&ptlib.stack[4] = (int32_t)RTLD_LAZY;
366+
}
367+
368+
if (ptrace_attach(process->pid))
369+
goto EXIT;
370+
371+
link_map_iter = ptrace_libcall(process->pid, process->bits, &ptlib);
372+
if (link_map_iter == -1 || link_map_iter == 0)
373+
goto DETACH_EXIT;
374+
375+
/* Search for the correct handle, just like in LM_UnloadModule */
376+
for (; link_map_iter; link_map_iter = (long)link_map.l_next) {
377+
if (ptrace_read(process->pid, link_map_iter, &link_map, sizeof(link_map)) != sizeof(link_map))
378+
goto DETACH_EXIT;
379+
380+
if (ptrace_read(process->pid, (long)link_map.l_name, path, sizeof(path)) == 0)
381+
goto DETACH_EXIT;
382+
path[sizeof(path) - 1] = '\0';
383+
384+
if (!strcmp(path, module->path)) {
385+
handle = (void *)link_map_iter;
386+
break;
387+
}
388+
}
389+
390+
if (!handle) {
391+
/* If no handle was found, it means that the module is already unloaded */
392+
ret = LM_TRUE;
393+
goto DETACH_EXIT;
394+
}
395+
396+
/* Call dlclose with the handle we found */
397+
ptlib.address = dlfcn.dlclose_addr;
398+
ptlib.args[0] = (long)handle;
399+
if (process->bits == 64) {
400+
*(uint64_t *)&ptlib.stack[0] = (uint64_t)handle;
401+
} else {
402+
*(uint32_t *)&ptlib.stack[0] = (uint32_t)handle;
403+
}
404+
405+
call_ret = ptrace_libcall(process->pid, process->bits, &ptlib);
406+
if (call_ret != 0)
407+
goto DETACH_EXIT;
408+
409+
ret = LM_TRUE;
410+
DETACH_EXIT:
411+
ptrace_detach(process->pid);
412+
EXIT:
413+
return ret;
414+
}

tests/unit/module.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,13 @@ char *test_LM_LoadModuleEx(struct load_module_args *arg)
100100

101101
return NULL;
102102
}
103+
104+
char *test_LM_UnloadModuleEx(struct load_module_args *arg)
105+
{
106+
lm_process_t *ptargetproc = arg->ptargetproc;
107+
lm_module_t *pmod = arg->pmod;
108+
109+
mu_assert("failed to unload external module", LM_UnloadModuleEx(ptargetproc, pmod) == LM_TRUE);
110+
mu_assert("function attempted to run with bad arguments (invalid proc)", LM_UnloadModuleEx(LM_NULLPTR, pmod) == LM_FALSE);
111+
mu_assert("function attempted to run with bad arguments (invalid mod)", LM_UnloadModuleEx(ptargetproc, LM_NULLPTR) == LM_FALSE);
112+
}

tests/unit/unit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ void test_module(lm_process_t *pcurproc, lm_process_t *ptargetproc)
106106
UNIT_TEST_P(LM_LoadModule, &mod);
107107
UNIT_TEST_P(LM_UnloadModule, &mod);
108108
UNIT_TEST_P(LM_LoadModuleEx, &arg);
109-
/* TODO: Add test for LM_UnloadModuleEx */
109+
UNIT_TEST_P(LM_UnloadModuleEx, &arg);
110110
}
111111

112112
void test_segment(lm_process_t *pcurproc, lm_process_t *ptargetproc)

0 commit comments

Comments
 (0)