Форум программистов, компьютерный форум, киберфорум
C/C++: WinAPI
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/48: Рейтинг темы: голосов - 48, средняя оценка - 5.00
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033

Подмена чужой функции из dll своей

31.07.2013, 22:20. Показов 9238. Ответов 16
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте Господа !!! Чужая программа использует функцию (название известно) из её dll , требуется эту функцию подменить на свою функцию из dll.
Вот я "нахожу" чужую функцию
C++
1
dw=(DWORD)GetProcAddress(GetModuleHandle("name.dll"),"Function");
затем свою функцию из dll
C++
1
2
hlib=LoadLibrary("name2.dll");
            p=(PFN)GetProcAddress(hlib,"Function2");
Так вот как спроектировать Function2 на Function ?

Добавлено через 1 час 18 минут
Решил глянуть у Рихтера , компилятор не ругается, но и не чего не происходит.
Вот функция Рихтера к-ая призвана подменять
C++
1
2
3
4
5
6
7
8
9
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
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
ImageDirectoryEntryToData(hmodCaller, TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
if (pImportDesc == NULL)
return; // в этом модуле нет раздела импорта
// находим дескриптор раздела импорта со ссылками
// на функции DLL (вызываемого модуля)
for (; pImportDesc->Name; pImportDesc++) {
PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
break;
}
if (pImportDesc->Name == 0)
return; // этот модуль не импортирует никаких функций из данной DLL
// получаем таблицу адресов импорта (IAT) для функций DLL
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE) hmodCaller + pImportDesc->FirstThunk);
// заменяем адреса исходных функций адресами своих функций
for (; pThunk->u1.Function; pThunk++) {
// получаем адрес адреса функции
PROC* ppfn = (PROC*) &pThunk->u1.Function;
// та ли это функция, которая нас интересует?
BOOL fFound = (*ppfn == pfnCurrent);
//454
// см. текст программы-примера, в котором
// содержится трюковый код для Windows 98
if (fFound) {
// адреса сходятся, изменяем адрес в разделе импорта
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL );
return; // получилось, выходим
}
}
MessageBoxA(0,0,0,0);
}
вот программа -

C++
1
2
3
4
5
6
7
8
9
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
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 
PROC pfnOrig;
 
    static PROC p=0;
    static HMODULE hlib=0;
 
 
    switch (message)                  /* handle the messages */
    {
 
    case WM_CHAR:
 
pfnOrig = GetProcAddress(GetModuleHandle("User32.dll"), "MessageBoxA");
 
if(pfnOrig!=0){Beep(500,500);}
 
hlib=LoadLibrary("Project1.dll");
             if(hlib!=0){Beep(500,500);}
            p=(PROC)GetProcAddress(hlib,"SomeFunction");
             if(p!=0){Beep(500,500);}
 
 
ReplaceIATEntryInOneMod(
"User32.dll", // модуль, содержащий ANSI-функцию
pfnOrig, // адрес исходной функции в вызываемой DLL
p, // адрес заменяющей функции
hlib); // описатель модули, из которого надо вызывать новую функцию
 
 
        break;
 
 
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }
 
    return 0;
}
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
31.07.2013, 22:20
Ответы с готовыми решениями:

Использование чужой dll в своей программе
Есть dll-ка wavcarrier.dll. Её использует программа invisible secrets для шифрования файлов и связки их с wav файлом. Так вот, как можно...

Использование чужой DLL в своей программе
Доброго времени суток. Есть сторонняя DLL для работы с устройством подключенного через COM-Port. Есть только DLL и поэтому статическое...

Использование функции в своей программе из своей dll
Среда разработки Visual Studio 2008 Язык C++ Написал dll как в примере на сайте...

16
3176 / 1935 / 312
Регистрация: 27.08.2010
Сообщений: 5,131
Записей в блоге: 1
31.07.2013, 22:32
http://www.google.com/search?q=DLL+wrapper
0
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033
31.07.2013, 22:35  [ТС]
я гуглил , поэтому что бы ерундой не страдать я почитал Рихтера , но вот где то заминка не пойму
0
3176 / 1935 / 312
Регистрация: 27.08.2010
Сообщений: 5,131
Записей в блоге: 1
01.08.2013, 00:09
Вы уверены, что вам удалась замена в чужом процессе?

Добавлено через 10 минут
P.S.
Перехват API-функций в Windows NT/2000/XP
0
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033
01.08.2013, 09:24  [ТС]
gazlan, да да и эту статью я уже читал,я не из тех кто сразу пишет на форум не покапавшись в гугле.
0
3176 / 1935 / 312
Регистрация: 27.08.2010
Сообщений: 5,131
Записей в блоге: 1
01.08.2013, 10:30
Ну, а ProxyDLL чем не устраивает?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
01.08.2013, 10:42
Цитата Сообщение от Игорь с++ Посмотреть сообщение
Здравствуйте Господа !!! Чужая программа использует функцию (название известно) из её dll , требуется эту функцию подменить на свою функцию из dll.
Если подмену нужно сделать "на лету", когда приложение-жертва уже запущено,
тогда вариантов не много - пропатчить начало оригинальной функции, вставив
туда "jmp адрес-новой-функции".

Если есть контроль над загрузкой приложения-жертвы, особенно над загрузкой
его dll-ок, тогда появляются другие варианты. Например, изменение адреса
функции в таблице импорта exe или в таблице экспорта dll. Для этого, правда,
придется разобрать PE-образ, но это задача почти тривиальная, особенно для
таблицы экспорта.

Еще есть вариант - заменить оригинальную dll своей.
В своей dll, в секции экспорта, прописать форвардинг на соответствующие
функции оригинальной dll, кроме той, которую нужно перехватить.
Для нее пишется обычная экспорт-сигнатура и своя реализация.

Добавлено через 6 минут
Цитата Сообщение от Игорь с++ Посмотреть сообщение
dw=(DWORD)GetProcAddress(GetModuleHandle ("name.dll"),"Function");
Кстати, так нехорошо. GetProcAddress возвращает указатель.
sizeof (DWORD) и sizeof (указатель) равны только в 32-битных сборках.
На 64-битных sizeof (указатель) будет 8 байт, отсюда усечение (без предупреждения
компилятора, т.к. каст в стиле С) и непонятные вылеты программы время от времени...
0
3176 / 1935 / 312
Регистрация: 27.08.2010
Сообщений: 5,131
Записей в блоге: 1
01.08.2013, 10:59
В развитие темы Proxy: Generating .DLL Wrappers
0
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033
01.08.2013, 13:57  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
Если подмену нужно сделать "на лету", когда приложение-жертва уже запущено,
тогда вариантов не много - пропатчить начало оригинальной функции, вставив
туда "jmp адрес-новой-функции".
Я это и пытаюсь сделать . Вот и у Рихтера тоже есть , а не работает .

Добавлено через 14 минут
Вот полный код для подмены MessageBoxA , он не работает , почему ??????????
C++
1
2
3
4
5
6
7
8
9
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#include <windows.h>
#include <imagehlp.h>
 
/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
 
/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";
 
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller);
BOOL SetDebugPrivileges();
 
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */
 
    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);
 
    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
 
    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;
 
    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );
 
    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);
 
    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }
 
    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}
 
 
/*  This function is called by the Windows function DispatchMessage()  */
 
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 
PROC pfnOrig;
 
    static PROC p=0;
    static HMODULE hlib=0;
 
 
    switch (message)                  /* handle the messages */
    {
 
    case WM_CHAR:
 
SetDebugPrivileges();
 
pfnOrig = GetProcAddress(GetModuleHandle("User32.dll"), "MessageBoxA");
 
if(pfnOrig!=0){Beep(500,500);}
 
hlib=LoadLibrary("Project1.dll");
             if(hlib!=0){Beep(500,500);}
            p=(PROC)GetProcAddress(hlib,"SomeFunction");
             if(p!=0){Beep(500,500);}
 
 
ReplaceIATEntryInOneMod(
"User32.dll", // модуль, содержащий ANSI-функцию
pfnOrig, // адрес исходной функции в вызываемой DLL
p, // адрес заменяющей функции
hlib); // описатель модули, из которого надо вызывать новую функцию
 
 
        break;
 
 
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }
 
    return 0;
}
 
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
ImageDirectoryEntryToData(hmodCaller, TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
if (pImportDesc == NULL)
return; // в этом модуле нет раздела импорта
// находим дескриптор раздела импорта со ссылками
// на функции DLL (вызываемого модуля)
for (; pImportDesc->Name; pImportDesc++) {
PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
break;
}
if (pImportDesc->Name == 0)
    return; // этот модуль не импортирует никаких функций из данной DLL
// получаем таблицу адресов импорта (IAT) для функций DLL
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE) hmodCaller + pImportDesc->FirstThunk);
// заменяем адреса исходных функций адресами своих функций
for (; pThunk->u1.Function; pThunk++) {
// получаем адрес адреса функции
PROC* ppfn = (PROC*) &pThunk->u1.Function;
// та ли это функция, которая нас интересует?
BOOL fFound = (*ppfn == pfnCurrent);
//454
// см. текст программы-примера, в котором
// содержится трюковый код для Windows 98
if (fFound) {
// адреса сходятся, изменяем адрес в разделе импорта
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL );
return; // получилось, выходим
}
}
MessageBoxA(0,0,0,0);
}
 
 
BOOL SetDebugPrivileges()
{
    TOKEN_PRIVILEGES tp;
    LUID luid;
    HANDLE hToken;
 
    if(!OpenProcessToken(GetCurrentProcess(),TOKEN_READ|TOKEN_ADJUST_PRIVILEGES,&hToken))
        {return FALSE;}
 
    if(!LookupPrivilegeValueA(NULL,"SeDebugPrivilege",&luid))
        {CloseHandle(hToken);return FALSE;}
 
    tp.PrivilegeCount=1;
    tp.Privileges[0].Luid=luid;
    tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
 
    if(!AdjustTokenPrivileges(hToken,FALSE,&tp,0,NULL,NULL))
        {CloseHandle(hToken);return FALSE;}
 
    CloseHandle(hToken);
    return TRUE;
}
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
01.08.2013, 14:06
Патчить адреса в таблице импорта нужно только во время загрузки dll.
Если сделать это после, есть вероятность, что функция будет вызвана
напрямую, мимо "переходника", который патчится.
Ну и в коде функции ReplaceIATEntryInOneMod я вижу WriteProcessMemory, но
не вижу ни проверки атрибутов страницы (read-only ?), ни проверки ошибок.
0
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033
01.08.2013, 14:57  [ТС]
ок.посматрю тогда отпишусь.а по поваду длл это как же user32 отследить загрузку?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
01.08.2013, 15:17
Когда-то делал так: запускаем процесс c флагом DEBUG_ONLY_THIS_PROCESS.
А потом ловим события с помощью WaitForDebugEvent.
Получили LOAD_DLL_DEBUG_EVENT - смотрим, что за dll загрузилась.
Как только поймали загрузку нужной dll, выполняем патчинг.
1
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033
01.08.2013, 15:47  [ТС]
Спасибо конечно , но что то Рихтер даже не словом не полсловом не обмолвился об этом .

Добавлено через 6 минут
проверил функцию , почему то выполняется вот это условие и функция завершается -
C++
1
if (pImportDesc->Name == 0)
Добавлено через 9 минут
Цитата Сообщение от Убежденный Посмотреть сообщение
Ну и в коде функции ReplaceIATEntryInOneMod я вижу WriteProcessMemory, но
не вижу ни проверки атрибутов страницы (read-only ?), ни проверки ошибок.
Ну Рихтера я тоже не заметил всего этого , вообще не понятно почему функция стопорится .
0
 Аватар для Kaimi
38 / 33 / 6
Регистрация: 17.09.2012
Сообщений: 66
02.08.2013, 11:59
проверил функцию , почему то выполняется вот это условие и функция завершается -

if (pImportDesc->Name == 0)
А что непонятного то? Потому что Project1.dll не импортирует ничего из user32.dll.
0
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033
02.08.2013, 12:05  [ТС]
Kaimi, а по подробней,не очень понял.
0
 Аватар для Kaimi
38 / 33 / 6
Регистрация: 17.09.2012
Сообщений: 66
02.08.2013, 12:11
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
hlib=LoadLibrary("Project1.dll");
...
ReplaceIATEntryInOneMod(
"User32.dll", // модуль, содержащий ANSI-функцию
pfnOrig, // адрес исходной функции в вызываемой DLL
p, // адрес заменяющей функции
hlib); // описатель модули, из которого надо вызывать новую функцию
 
....
 
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {
....
for (; pImportDesc->Name; pImportDesc++) {
PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
break;
}
if (pImportDesc->Name == 0)
return; // этот модуль не импортирует никаких функций из данной DLL
Как написали код, так он и работает. Непонятно, что непонятного.
1
 Аватар для Игорь с++
500 / 474 / 63
Регистрация: 26.01.2011
Сообщений: 2,033
02.08.2013, 14:15  [ТС]
Всем спасибо !!! Вопрос решён .
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
02.08.2013, 14:15
Помогаю со студенческими работами здесь

Задача: подмена адреса функции из DLL.
Здравствуйте уважаемые программисты. Пожалуйста, помогите решить задачу. Нужно в моей программе подменить адрес функции причём из длл, во...

Упорядочить открытые окна (с использованием чужой dll-функции)
1.Задача: есть ряд окон которые надо упорядочить все они начинаются с заголовка (database). Выбрать размеры форм которые будут...

Как в своей DLL использовать функции из Microsoft.VisualBasic
Доброго времени суток! Создаю свою Dll и столкнулся с проблемой: не поддерживаются многие стандартные функции, такие как: Mid, Str,...

DLL hijacking или подмена DLL при открытии .docx
Набрел как-то на статью на хабре. Решил проверить на своем компе. Создал DLL следующего содержания: #include &lt;windows.h&gt; BOOL...

Отразить во фрейме на своей странице часть чужой
Коллеги, Описание проблемы: Я не большой специалист в программировании на HTML. Делаю страницу, на которой хочу отразить в собранном...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru