Post by NicusorN5 on Aug 27, 2022 11:33:48 GMT
Hello everyone - this is my first post here since this forum was initially replaced with ClassDev.
On topic:
While trying to find a fix for TinSoldier 's issue with help files not working, I have found out that it is possible to implement custom objects in 3D Rad v722 by exporting few functions.
![list of functions pic](https://media.discordapp.net/attachments/1116170774793961525/1116171202755567676/pic1.png)
(Note: images are hosted on 3DRadSpace's Discord server)
These are functions exported by the object.dll library. For this example I loaded the Skinmesh object library.
Some of you may remember that there's also a DLL called object_redist.dll. It exports these functions
![](https://media.discordapp.net/attachments/1116170774793961525/1116171202097061939/pic3.png)
Now, this is interesting, but how do we implement these functions ourselves? I don't really know...
With my limited knowledge in reverse engineering, I attempt finding out how few exported functions work, in random order.
DialogFunc implements the dialog based window used in 3D Rad to edit the object's properties:
![](https://media.discordapp.net/attachments/1116170774793961525/1116171202491338773/pic2.png)
I noticed this is very similar (or it can be) a window procedure function. Except this is a dialog window procedure (DLGPROC) . These two procedures can both handle window messages, even the ones handled by DefWindowProc like WM_PAINT, WM_NOTIFY and WM_COMMAND. A dialog window procedure must always handle the WM_INITDIALOG (dialog initialization window message) and WM_CLOSE window messages to handle dialog creation and dialog return code ((in this case, the Skinmesh dialog is a modal dialog box). The dialog return code is set by the EndDialog(HWND,INT_PTR) function)). An other difference is the return value of the functions. Returning 0 from a DLGPROC means that the system will handle the messages next, while in a normal window, the return value may differ depending on the message. (For example WM_GETTEXTLENGTH returns the lenght of the window text (either the text of a TextBox control, window title, etc). Returning 1 from a DLGPROC functions means that the application handled the message succesfully.
I wrote a very basic Win32 application with 2 buttons to demonstrate how a window procedure function is looking.
(I inserted images because I hate how PasteBin uses syntax highlightning. This is a long post indeed. I also hate how the code tag doesn't have any syntax highlightning too)
Source for copy pasting
This is how the basic application looks:
![](https://media.discordapp.net/attachments/1116170774793961525/1116171201212063875/pic6.png)
This example aside, looking at the decompiled source of DialogFunc first handles the message id 272(0x110). This is WM_INITDIALOG. The next handled message is WM_COMMAND (Note that there's a check if the message is different than WM_COMMAND the functions exits returning 0, letting the system handle the messages.) This means DialogFunc only handles (I think, unless I missed something) WM_INITDIALOG AND WM_COMMAND.
The WM_COMMAND message handles button click notifications, when a accelerator keystroke is sent (aka keyboard shortcuts), when a user selects a command from a menu, etc. (Source)
![](https://media.discordapp.net/attachments/1116170774793961525/1116171201795084319/pic4.png)
![](https://media.discordapp.net/attachments/1116170774793961525/1116171201497280673/pic5.png)
LibInit is used propably to set object data that is in the 3D Rad's heap memory:
![](https://media.discordapp.net/attachments/1116170774793961525/1116171199135883284/pic10.png?width=466&height=683)
I can't know what these variables are since I need to use a decompiler together with IDA. The issue is that I've tried that with no success (IDA crashes when trying to attach a debugger to the DLL or 3D Rad
.
Now, ObjectRun is the most interesting function here. It handles drawing the skinmesh as far as I can see.
![](https://cdn.discordapp.com/attachments/1116170774793961525/1116171242278490173/pic11.png)
These are mosty likely DirectX 9 function calls or calls to a global variable abstract class instance.
This is what I was able to find out in 15 minutes of research. I don't expect you to understand the decompiled source, nor do I fully understand it myself. But I really want to try creating a custom object sometime.
What do you guys think?
On topic:
While trying to find a fix for TinSoldier 's issue with help files not working, I have found out that it is possible to implement custom objects in 3D Rad v722 by exporting few functions.
![list of functions pic](https://media.discordapp.net/attachments/1116170774793961525/1116171202755567676/pic1.png)
(Note: images are hosted on 3DRadSpace's Discord server)
These are functions exported by the object.dll library. For this example I loaded the Skinmesh object library.
Some of you may remember that there's also a DLL called object_redist.dll. It exports these functions
![](https://media.discordapp.net/attachments/1116170774793961525/1116171202097061939/pic3.png)
Now, this is interesting, but how do we implement these functions ourselves? I don't really know...
With my limited knowledge in reverse engineering, I attempt finding out how few exported functions work, in random order.
DialogFunc implements the dialog based window used in 3D Rad to edit the object's properties:
![](https://media.discordapp.net/attachments/1116170774793961525/1116171202491338773/pic2.png)
I noticed this is very similar (or it can be) a window procedure function. Except this is a dialog window procedure (DLGPROC) . These two procedures can both handle window messages, even the ones handled by DefWindowProc like WM_PAINT, WM_NOTIFY and WM_COMMAND. A dialog window procedure must always handle the WM_INITDIALOG (dialog initialization window message) and WM_CLOSE window messages to handle dialog creation and dialog return code ((in this case, the Skinmesh dialog is a modal dialog box). The dialog return code is set by the EndDialog(HWND,INT_PTR) function)). An other difference is the return value of the functions. Returning 0 from a DLGPROC means that the system will handle the messages next, while in a normal window, the return value may differ depending on the message. (For example WM_GETTEXTLENGTH returns the lenght of the window text (either the text of a TextBox control, window title, etc). Returning 1 from a DLGPROC functions means that the application handled the message succesfully.
I wrote a very basic Win32 application with 2 buttons to demonstrate how a window procedure function is looking.
#include <Windows.h>
#include <CommCtrl.h>
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HWND btn1, btn2;
LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)(COLOR_MENU + 1));
EndPaint(hwnd, &ps);
return 1;
}
case WM_COMMAND:
{
switch(HIWORD(wParam))
{
case BN_CLICKED:
{
if(lParam == reinterpret_cast<LPARAM>(btn1))
{
MessageBoxW(hwnd, TEXT("You pressed button 1!"), TEXT("Info"), MB_ICONINFORMATION);
}
if(lParam == reinterpret_cast<LPARAM>(btn2))
{
MessageBoxW(hwnd, TEXT("You pressed button 2!"), TEXT("Info"), MB_ICONINFORMATION);
}
break;
}
default:
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
case WM_CLOSE:
{
exit(0);
break;
}
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
}
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR cmdArgs, int wndCmdShow)
{
WNDCLASS wndclass{}; //{} - memsets the wndclass struct instance memory to 0
wndclass.lpfnWndProc = WndProc; // WndProc is the window procedure for the MyWindowClass window class
wndclass.lpszClassName = TEXT("MyWindowClass");
wndclass.hInstance = hInstance;
wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
RegisterClassW(&wndclass);
HWND hwnd = CreateWindowExW(
0,
TEXT("MyWindowClass"),
TEXT("My window"),
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
400,
200,
nullptr,
nullptr,
hInstance,
nullptr
);
btn1 = CreateWindowExW(
0,
TEXT("Button"),
TEXT("Button 1"),
BS_DEFCOMMANDLINK | WS_VISIBLE | WS_CHILD,
10,
10,
150,
55,
hwnd,
nullptr,
hInstance,
nullptr
);
SendMessageW(btn1, BCM_SETNOTE, 0, reinterpret_cast<LPARAM>(TEXT("Button 1 thing")));
btn2 = CreateWindowExW(
0,
TEXT("Button"),
TEXT("Button 2"),
BS_COMMANDLINK | WS_VISIBLE | WS_CHILD,
10,
65,
150,
55,
hwnd,
nullptr,
hInstance,
nullptr
);
SendMessageW(btn2, BCM_SETNOTE, 0, reinterpret_cast<LPARAM>(TEXT("Button 2 thing")));
MSG msg{};
while(GetMessageW(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
(I inserted images because I hate how PasteBin uses syntax highlightning. This is a long post indeed. I also hate how the code tag doesn't have any syntax highlightning too)
Source for copy pasting
This is how the basic application looks:
![](https://media.discordapp.net/attachments/1116170774793961525/1116171201212063875/pic6.png)
This example aside, looking at the decompiled source of DialogFunc first handles the message id 272(0x110). This is WM_INITDIALOG. The next handled message is WM_COMMAND (Note that there's a check if the message is different than WM_COMMAND the functions exits returning 0, letting the system handle the messages.) This means DialogFunc only handles (I think, unless I missed something) WM_INITDIALOG AND WM_COMMAND.
The WM_COMMAND message handles button click notifications, when a accelerator keystroke is sent (aka keyboard shortcuts), when a user selects a command from a menu, etc. (Source)
Now, since we're decompiling a dynamic link library, it should be noticed that DLLs also may have a main function called DllMain. DLLs may have entry functions to check if they're being loaded, unloaded, injected/attached, et cetera.
In our case, our entry point function looks like this.
![](https://media.discordapp.net/attachments/1116170774793961525/1116171201795084319/pic4.png)
Or decompiled into a C/C++-pseudocode:
![](https://media.discordapp.net/attachments/1116170774793961525/1116171201497280673/pic5.png)
We can easily see that the entry point function inside the Skinmesh DLL object is used to only set a global variable called hModule
by the decompiler. LibInit is used propably to set object data that is in the 3D Rad's heap memory:
![](https://media.discordapp.net/attachments/1116170774793961525/1116171199135883284/pic10.png?width=466&height=683)
I can't know what these variables are since I need to use a decompiler together with IDA. The issue is that I've tried that with no success (IDA crashes when trying to attach a debugger to the DLL or 3D Rad
![:(](http://storage.proboards.com/forum/images/smiley/sad.png)
Now, ObjectRun is the most interesting function here. It handles drawing the skinmesh as far as I can see.
![](https://cdn.discordapp.com/attachments/1116170774793961525/1116171242278490173/pic11.png)
These are mosty likely DirectX 9 function calls or calls to a global variable abstract class instance.
This is what I was able to find out in 15 minutes of research. I don't expect you to understand the decompiled source, nor do I fully understand it myself. But I really want to try creating a custom object sometime.
What do you guys think?