Краш при использовании системы лицензирования

Issues related to VMProtect
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Краш при использовании системы лицензирования

Post by lach »

Минимизировал и прикрепил пример на котором это воспроизводится, библиотека собрана под Linux
Конфиг дефолтный, за исключением включённой системы лицензирования и использованием скрипта https://github.com/CertainLach/vmprotec ... script.lua (TL;DR: на функции начинающиеся с vmprotect_ применяет опции согласно их названию, и удаляет их из экспортов)

Библиотека экспортирует одну функцию - do_the_thing
Функция принимает буфер, первый байт = 4, остальные - лицензионный ключ для системы лицензирования и завершающий \0
При получении лицензионного ключа функция его активирует, делает SetSerialNumber, и если ошибок не было (их логгирует) дёргает функцию защищённую лицензией (lockToKey)
Если передать буфер у которого первый байт = 13, то оно сразу вызывает функцию защищённую лицензией

До применения vmprotect (Запуск с VMProtectSDK заглушками) код успешно работает, если ему передать \x04activationcode\0, то он выведет
```
Op 4
activate license
set serial
call lock by key function
called
```

Падений нет
После применения vmprotect при передаче правильного кода лицензии (\x04aaaa-aaaa-aaaa-aaaa\0) - код получает серийник, но падает на вызове функции под защитой
```
Op 4
activate license
set serial
call lock by key function
terminated by signal SIGSEGV (Address boundary error)
```

Если же не вызывая активацию попробовать вызвать код под лицензией (Аргумент \x0d), то
```
Op 13
# И тут появляется окно zenity с ошибкой
```

Обычные покрытые функции работают правильно (do_the_thing помечена как ultra), падения происходят только на lockByKey.
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Re: Краш при использовании системы лицензирования

Post by lach »

Пример не прикрепляется на форум: https://send.0la.ch/download/530f3a2b3d ... RtF9vvwSZg
Admin
Site Admin
Posts: 2703
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Краш при использовании системы лицензирования

Post by Admin »

Этот код удаляет элемент в том числе и из вектора "exports" и вы проскакиваете следующий item после delete:

Code: Select all

			for i = 1, exports:count() do
				if exports:item(i):address() == fn:address() then
					bprint("And removing from exports")
					exports:delete(i)
					break
				end
			end
Нужно бегать по вектору в обратном направлении.

P.S. В вашем примере нет бинарника, который вызывает вашу SO.
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Re: Краш при использовании системы лицензирования

Post by lach »

Этот код удаляет элемент в том числе и из вектора "exports" и вы проскакиваете следующий item после delete:
Похоже что в этом репродюсере проблема действительно с этим
К сожалению в реальном коде это работает несколько иначе, и там функций изначально в .dynsym нет; Значит там другая проблема, переделаю репродюсер.

Однако судя по всему, ElfExports:delete() сейчас не работает, поскольку даже с исправленным скриптом библиотека на выходе падает:
https://github.com/CertainLach/vmprotec ... script.lua

Если экспорты не удалять, то выхлоп работает

Пытался их удалить в нескольких разных хуках, не только в OnBeforeSaveFile, символы из .dynsym успешно исчезают

Код для вызова библиотеки:

Code: Select all

#include <string.h>
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdint.h>

int main(int argc, char* argv[]) {
	char* libname = argc >= 2 ? argv[1] : "repro.so";
	char* action = argc >= 3 ? argv[2] : "";
	printf("Loading library %s\n", libname);
	void* lib = dlopen(libname, RTLD_NOW);
	if (!lib) {
		printf("Failed to load lib: %s\n", dlerror());
		return 1;
	}
	printf("Getting export\n");
	void* (*do_the_thing)(char*) = dlsym(lib, "do_the_thing");
	if (!do_the_thing) {
		printf("Failed to find symbol: %s\n", dlerror());
		return 1;
	}
	if (strcmp(action, "activate") == 0) {
		if (argc < 4) {
			printf("Usage: [lib] activate [code]");
			return 1;
		}
		char* code = argv[3];
		char* req = malloc(1 + strlen(code) + 1);
		req[0] = 4;
		strcpy(req + 1, code);
		printf("Activating with code \"%s\"\n", code);
		uint16_t* status = do_the_thing(req);
		if (status == NULL || *status != 0) {
			printf("Activation failed with status %x\n", *status);
			return 1;
		}
	} else if (strcmp(action, "call_locked") == 0) {
		char req[1];
		req[0] = 13;
		printf("Calling locked function\n");
		uint16_t* status = do_the_thing(req);
	} else {
		printf("Unknown action\n");
		return 1;
	}
	printf("Success\n");
	return 0;
}
Активация + вызов функции с lock by key
gcc main.c -o main && ./main ./repro.so activate activationcode

Вызов функции с lock by key без активации
gcc main.c -o main && ./main ./repro.so call_locked
Admin
Site Admin
Posts: 2703
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Краш при использовании системы лицензирования

Post by Admin »

lach wrote: Wed Jun 11, 2025 11:36 am Однако судя по всему, ElfExports:delete() сейчас не работает, поскольку даже с исправленным скриптом библиотека на выходе падает:
Вопрос был не про то, что это является причиной креша, а в том, что ваш код LUA работает неправильно и пропускает один из элементов в exports после delete.

В любом случае ваш функционал можно сделать намного проще, т.к. у exports есть itemByAddress:

Code: Select all

			bprint("And queuing for export removal")
			
			local export = file:exports():itemByAddress(fn:address())
			if (export) then
				bprint("Deleting export " .. export:name() .. " (" .. address:tostring() .. ")")
				export:destroy()
			end
			
			deent()
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Re: Краш при использовании системы лицензирования

Post by lach »

Вопрос был не про то, что это является причиной креша, а в том, что ваш код LUA работает неправильно и пропускает один из элементов в exports после delete.
Это я понял, в последней версии скрипта это исправлено

Однако что исправленный вариант

Code: Select all

	for i = exports:count(), 1, -1 do
		local export = exports:item(i)
		local address = export:address();
		
		if tableHas(toDelete, address) then
			bprint("Deleting export " .. export:name() .. " (" .. address:tostring() .. ")")
			exports:delete(i)
		end
	end
Что вариант с itemByAddress

Code: Select all

	for _, address in ipairs(toDelete) do
		local export = exports:itemByAddress(address)
		if export then
			bprint("Deleting export " .. export:name() .. " (" .. address:tostring() .. ")")
			export:destroy()
		end
	end
Приводят к падению библиотеки при попытке её вызова
Admin
Site Admin
Posts: 2703
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Краш при использовании системы лицензирования

Post by Admin »

Скрипт к падению не имеет никакого отношения. Он у вас просто неправильно работал и все.
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Re: Краш при использовании системы лицензирования

Post by lach »

Code: Select all

К сожалению в реальном коде это работает несколько иначе
Оказлось что реальный код падает по той же причине - используется exports:delete, тут по ошибке было подключено 2 версии Rust vmprotect sdk работающие по разным принципам, приватная версия предварительно обрабатывает код через llvm, и там в сошке изначально нет экспортов
Версия с названиями функций (та что публичная, https://github.com/CertainLach/vmprotect) устроена проще, но в ней для красоты желательно срезать экспорты

Code: Select all

Он у вас просто неправильно работал и все.
Я что-то не понимаю что именно с изначальным кодом не так)
Он обходит таблицу exports каждый раз когда нужно удалить экспорт, и как только экспорт найден и удалён - делает break, т.е после удаления не будет проверяться элемент i+1

Я вижу проблему только если бы там было

Code: Select all

	for i = 1, exports:count() do
		local export = exports:item(i)
		local address = export:address();
		
		if tableHas(toDelete, address) then
			bprint("Deleting export " .. export:name() .. " (" .. address:tostring() .. ")")
			exports:delete(i)
		end
	end
В этом случае действительно после :delete() произойдёт сдвиг элементов, и элемент i+1 будет пропущен
Admin
Site Admin
Posts: 2703
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Краш при использовании системы лицензирования

Post by Admin »

Да, с break внутри цикла ошибки нет.
Admin
Site Admin
Posts: 2703
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Краш при использовании системы лицензирования

Post by Admin »

Проверяйте 2374 билд.
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Re: Краш при использовании системы лицензирования

Post by lach »

Спасибо, на репродюсере помогло
Но к сожалению на реальном коде оно всё ещё падает
Приблизил репродюсер к реальному, на нём падает; апи библиотеки аналогичное, послал в ПМ
Admin
Site Admin
Posts: 2703
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Краш при использовании системы лицензирования

Post by Admin »

У вас do_the_thing работает через символ "vmprotect_ultra_do_the_thing_inner_186237384678405675810701436432396487933_ptr", который вы удаляете вместе с экспортом:

Code: Select all

.text:000000000007DDC0                 public do_the_thing
.text:000000000007DDC0 do_the_thing    proc near               ; DATA XREF: LOAD:00000000000007F8↑o
.text:000000000007DDC0 ; __unwind {
.text:000000000007DDC0                 jmp     cs:vmprotect_ultra_do_the_thing_inner_186237384678405675810701436432396487933_ptr
.text:000000000007DDC0 ; } // starts at 7DDC0
.text:000000000007DDC0 do_the_thing    endp
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Re: Краш при использовании системы лицензирования

Post by lach »

Блин, действительно, спасибо.

Надо выкинуть из библиотеки использование экспортов, и по нормальному названию матчить, проблема лишь в том что оно mangled, и vmprotect не может названия обработать

Сделал так, + через FFI подключил demangler, и решил кучу проблем
Было бы однако неплохо если бы сам vmprotect научился это делать. Я так понимаю используемая сейчас библиотека притворяется что умеет demangling Rust символов, однако зачастую у неё ничего не выходит (везде mangled названия)
В Rust дефолтная (legacy) схема mangling не стабильная, а v0 схему нужно включать явно, и её никто не поддерживает
Единственный правильный demangler - https://github.com/rust-lang/rustc-demangle

Code: Select all

local rustc_demangle_lib = vmprotect.openLib("/path/to/librustc_demangle.so")
if not rustc_demangle_lib then
	error("failed to open librustc_demangle")
end
local rustc_demangle = rustc_demangle_lib:getFunction("rustc_demangle", "int", "string", "pointer", "size_t")

local c_lib = vmprotect.openLib("/path/to/libc.so.6")
if not c_lib then
	error("failed to open libc")
end
local malloc = c_lib:getFunction("malloc", "pointer", "size_t")
local free = c_lib:getFunction("malloc", "void", "pointer")
local strdup = c_lib:getFunction("strdup", "string", "pointer")

function demangle(identifier)
	local demangle_buf = malloc(4096)
	local res_code = rustc_demangle(identifier, demangle_buf, 4096)
	local res = nil
	if res_code == 0 then
		-- Buffer is too small, or the string is not a valid rust identifier
		goto free
	end
	-- I think memory leak occurs here?.. Haven't found a good way to convert pointer to string in vmprotect lua api, any identity function would work
	res = strdup(demangle_buf)
	
	::free::
	free(demangle_buf)
	return res
end

if demangle("_RNvCskwGfYPst2Cb_3foo16example_function") ~= "foo::example_function" then
	error("demangler is not working properly")
end
Admin
Site Admin
Posts: 2703
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Краш при использовании системы лицензирования

Post by Admin »

Было бы однако неплохо если бы сам vmprotect научился это делать. Я так понимаю используемая сейчас библиотека притворяется что умеет demangling Rust символов, однако зачастую у неё ничего не выходит (везде mangled названия)
У вас сейчас там вообще нет никаких символов кроме экспорта.
lach
Posts: 30
Joined: Sat Mar 23, 2019 1:18 pm

Re: Краш при использовании системы лицензирования

Post by lach »

Code: Select all

У вас сейчас там вообще нет никаких символов кроме экспорта.
В данном случае потому что библиотека стрипнута

Сейчас flow в vmprotect rust sdk - функция с маркером

Code: Select all

#[protect(ultra)]
fn add(a: u32, b: u32) -> u32 {
    a + b
}
Преобразуется в

Code: Select all

#[inline(always)]
fn add(a: u32, b: u32) -> u32 {
    #[inline(never)]
    #[no_mangle]
    fn vmprotect_ultra_add_0123456789(a: u32, b: u32) {
        a + b
    }
    vmprotect_ultra_add_0123456789(a, b)
}

Code: Select all

#[no_mangle]
в Rust означает одновременно и то что название функции не надо mangleить, и то что её она имеет external linkage (К сожалению, без экспорта оно не умеет): https://internals.rust-lang.org/t/preci ... angle/4098, отсюда и выходят экспорты вида vmprotect_ultra_add_0123456789

Я собрал бинарь без strip, и убрал

Code: Select all

#[no_mangle]
(отправил в PM), однако в таком виде скрипт разумеется не работает, потому что у функций уже не те названия которые он ожидает

С таким кодом нужно дополнительно парсить названия

Code: Select all

        local name, protinfo = name:match("^(.+)::vmprotect_(.+)::h" .. string.rep("[0-9]", 16) .. "$")
Однако с таким кодом парсинга тут пропускаются методы вроде vmprotect_ultra_biguints_from_str_4, потому что vmprotect для них name выдаёт в mangled виде
Post Reply