Where im failing on native dll?

  • Автор темы Автор темы Spectre
  • Дата начала Дата начала
В продолжение важной темы, так получилось по итогу с uc текст передать в dll? ?
С int и byte все ?
 

i guess its possible but i really dont make any idea about what i need to do
 
Буду пробовать изучать спасибо! я просто мало сталкивался с c++

Я проверил ваш код работает но после выполняя зависает Stack.Code++; использую

Test


Заработало использовал 2 раза Stack.Code++; до и после выполнения код


Мне кажется я нашел причину и ошибка исчезла теперь Stack.Code++ использую один раз.
Я правильно сделал?
C++:
  Code += sizeof(INT)+1;

Я нашёл, что первый вызов "Stack.Code++" - это байт, представляющий некий тип.

Вот как я это обнаружил:
C++:
    const INT B = Stack.ReadByte();

    INT value;
    if (B == 29)
        value = Stack.ReadInt();
    else if (B == 44)
        value = Stack.ReadByte();

C++:
inline BYTE ReadByte()
    {
        return *Code++;
    }
    inline void SkipBytes(const INT Count)
    {
        Code += Count;
    }
    inline void SkipByte()
    {
        ++Code;
    }

    inline INT ReadInt()
    {
        INT Result;
        Result = *(INT*)Code;
        Code += sizeof(INT);
        return Result;
    }

В интерфейсе:
C++:
native(4005) final function execDebugReadInt(int value);
Когда я вызывал функцию через "execDebugReadInt(10);" (читая интегер) я получал ошибки. Потом подумал... и попробовал "execDebugReadInt(1000000);". В результате - ошибок нет. Так я понял, что тип меняется в зависимости от числа. 10 => байт, 1000000 => интегер. Потом я посмотрел на тот же самый "Stack.Code++" перед "Stack.ReadInt()". Он оказался поинтером к типу - 29 если интегер, 44 если байт.

Потом я нашёл этот старый енум, который соответствует моему откритию (EX_IntConst = 29, EX_IntConstByte = 44):
C++:
//
// Evaluatable expression item types.
//
enum EExprToken
{
    // Variable references.
    EX_LocalVariable        = 0x00,    // A local variable.
    EX_InstanceVariable        = 0x01,    // An object variable.
    EX_DefaultVariable        = 0x02,    // Default variable for a concrete object.

    // Tokens.
    EX_Return                = 0x04,    // Return from function.
    EX_Switch                = 0x05,    // Switch.
    EX_Jump                    = 0x06,    // Goto a local address in code.
    EX_JumpIfNot            = 0x07,    // Goto if not expression.
    EX_Stop                    = 0x08,    // Stop executing state code.
    EX_Assert                = 0x09,    // Assertion.
    EX_Case                    = 0x0A,    // Case.
    EX_Nothing                = 0x0B,    // No operation.
    EX_LabelTable            = 0x0C,    // Table of labels.
    EX_GotoLabel            = 0x0D,    // Goto a label.
    EX_EatString            = 0x0E, // Ignore a dynamic string.
    EX_Let                    = 0x0F,    // Assign an arbitrary size value to a variable.
    EX_DynArrayElement      = 0x10, // Dynamic array element.!!
    EX_New                  = 0x11, // New object allocation.
    EX_ClassContext         = 0x12, // Class default metaobject context.
    EX_MetaCast             = 0x13, // Metaclass cast.
    EX_LetBool                = 0x14, // Let boolean variable.
    EX_MapKeyElement        = 0x15,    // Map key element.!!
    //
    EX_EndFunctionParms        = 0x16,    // End of function call parameters.
    EX_Self                    = 0x17,    // Self object.
    EX_Skip                    = 0x18,    // Skippable expression.
    EX_Context                = 0x19,    // Call a function through an object context.
    EX_ArrayElement            = 0x1A,    // Array element.
    EX_VirtualFunction        = 0x1B,    // A function call with parameters.
    EX_FinalFunction        = 0x1C,    // A prebound function call with parameters.
    EX_IntConst                = 0x1D,    // Int constant.
    EX_FloatConst            = 0x1E,    // Floating point constant.
    EX_StringConst            = 0x1F,    // String constant.
    EX_ObjectConst            = 0x20,    // An object constant.
    EX_NameConst            = 0x21,    // A name constant.
    EX_RotationConst        = 0x22,    // A rotation constant.
    EX_VectorConst            = 0x23,    // A vector constant.
    EX_ByteConst            = 0x24,    // A byte constant.
    EX_IntZero                = 0x25,    // Zero.
    EX_IntOne                = 0x26,    // One.
    EX_True                    = 0x27,    // Bool True.
    EX_False                = 0x28,    // Bool False.
    EX_NativeParm           = 0x29, // Native function parameter offset.
    EX_NoObject                = 0x2A,    // NoObject.
    EX_IntConstByte            = 0x2C,    // Int constant that requires 1 byte.
    EX_BoolVariable            = 0x2D,    // A bool variable which requires a bitmask.
    EX_DynamicCast            = 0x2E,    // Safe dynamic class casting.
    EX_Iterator             = 0x2F, // Begin an iterator operation.
    EX_IteratorPop          = 0x30, // Pop an iterator level.
    EX_IteratorNext         = 0x31, // Go to next iteration.
    EX_StructCmpEq          = 0x32,    // Struct binary compare-for-equal.
    EX_StructCmpNe          = 0x33,    // Struct binary compare-for-unequal.
    EX_UnicodeStringConst   = 0x34, // Unicode string constant.
    EX_EatCtrExpr           = 0x35, // Ignore a constructor expression.
    //
    EX_StructMember         = 0x36, // Struct member.
    EX_StructConstruct      = 0x37, // Struct constructor.
    //
    EX_GlobalFunction        = 0x38, // Call non-state version of a function.

    // Native conversions.
    EX_MinConversion        = 0x39,    // Minimum conversion token.
    EX_RotatorToVector        = 0x39,
    EX_ByteToInt            = 0x3A,
    EX_ByteToBool            = 0x3B,
    EX_ByteToFloat            = 0x3C,
    EX_IntToByte            = 0x3D,
    EX_IntToBool            = 0x3E,
    EX_IntToFloat            = 0x3F,
    EX_BoolToByte            = 0x40,
    EX_BoolToInt            = 0x41,
    EX_BoolToFloat            = 0x42,
    EX_FloatToByte            = 0x43,
    EX_FloatToInt            = 0x44,
    EX_FloatToBool            = 0x45,
    //
    EX_ObjectToBool            = 0x47,
    EX_NameToBool            = 0x48,
    EX_StringToByte            = 0x49,
    EX_StringToInt            = 0x4A,
    EX_StringToBool            = 0x4B,
    EX_StringToFloat        = 0x4C,
    EX_StringToVector        = 0x4D,
    EX_StringToRotator        = 0x4E,
    EX_VectorToBool            = 0x4F,
    EX_VectorToRotator        = 0x50,
    EX_RotatorToBool        = 0x51,
    EX_ByteToString            = 0x52,
    EX_IntToString            = 0x53,
    EX_BoolToString            = 0x54,
    EX_FloatToString        = 0x55,
    EX_ObjectToString        = 0x56,
    EX_NameToString            = 0x57,
    EX_VectorToString        = 0x58,
    EX_RotatorToString        = 0x59,
    EX_MaxConversion        = 0x60,    // Maximum conversion token.

    // Natives.
    EX_ExtendedNative        = 0x60,
    EX_FirstNative            = 0x70,
    EX_MallocConstRef        = 0xD1,
    EX_RefIsValid            = 0x0191,
    EX_ArrayOperation        = 0x01F3,
    EX_ArrayIterator        = 0x0203,
    EX_MapIterator            = 0x0204,
    EX_MapHasValue            = 0x0201,
    EX_MapRemoveValue        = 0x0217,
    EX_MapEmptyValue        = 0x021A,
    EX_AnyOperator            = 0x021D,
    EX_TeriaryCondition        = 0x021E, // Evaluate variable.
    EX_DeleteObject            = 0x0288,
    EX_Max                    = 0x1000,
};
 
Последнее редактирование:
This is what I've came with to read basic data from Unreal. The problem here is it works just fine if you call function directly with the values for example `MyUnrealFunc("some string", 12, 4532);` Then you could use these to read these values. But the problem comes when you pass variable as argument. Then the first byte corresponds to `EX_InstanceVariable` and is `0x01` and then next 4 bytes is probably a pointer to memory. But if you check that memory, it does contain some weird data, nothing close to the actual value of the variable. Probably it's some kind of serialization? Do you have any luck with reading that data?

C++:
#pragma once

#include <string>

#define EX_INT 29
#define EX_FLOAT 30
#define EX_STRING 31
#define EX_TRUE 39
#define EX_FALSE 40
#define EX_BYTE 44

void ReadInt(unsigned char*& Code, unsigned int& output) {
    output = *reinterpret_cast<int*>(Code);
    Code += sizeof(output);
    Code++;
}

void ReadFloat(unsigned char*& Code, float& output) {
    output = *reinterpret_cast<float*>(Code);
    Code += sizeof(output); // Move pointer forward by the size of a float
    Code++;
}

void ReadByte(unsigned char*& Code, unsigned char& output) {
    output = *Code;
    Code += 2;
}

void ReadBool(unsigned char*& Code, bool& output) {
    switch (*Code) {
    case EX_TRUE:
        output = true;
        break;

    case EX_FALSE:
        output = false;
        break;
    }

    Code++;
}

void ReadString(unsigned char*& Code, std::string& output) {
    // Ensure codePtr is valid
    if (Code)
    {
        // Extract the null-terminated string
        while (*Code != '\0')
        {
            output += static_cast<char>(*Code);
            Code++; // Move to the next byte
        }

        // Increment past the null terminator
        Code++;
    }
}
 
Not really, I stopped looking at that point, because I've got too tired of diggin in that deep. When you call a function using unrealscript variables as parameters, they are actually saved somewhere in the unrealscript virtual machine. And EX_InstanceVariable technically points to an index, where the variable is stored within the virtual machine.


C++:
// Grab string as reference when possible.
#define P_GET_STR_LOCAL(var, code) \
    if (*Stack.Code == EX_LocalVariable || *Stack.Code == EX_InstanceVariable) \
    { \
        Stack.Step(Stack.Object, nullptr); \
        const FString& var = *reinterpret_cast<FString*>(GPropAddr); \
        code; \
    } \
    else \
    { \
        FString var; \
        Stack.Step(Stack.Object, &var); \
        code; \
    }

GPropAddr is located somewhere in Core.h file:

C++:
/*----------------------------------------------------------------------------
    Global variables.
----------------------------------------------------------------------------*/

// Core globals.
CORE_API extern FMemStack                GMem;
CORE_API extern FOutputDevice*            GLog;
CORE_API extern FOutputDevice*            GNull;
CORE_API extern FOutputDevice*            GThrow;
CORE_API extern FOutputDeviceError*        GError;
CORE_API extern FFeedbackContext*        GWarn;
CORE_API extern FConfigCache*            GConfig;
CORE_API extern FTransactionBase*        GUndo;
CORE_API extern FOutputDevice*            GLogHook;
CORE_API extern FExec*                    GExec;
CORE_API extern FMalloc*                GMalloc;
CORE_API extern FFileManager*            GFileManager;
CORE_API extern USystem*                GSys;
CORE_API extern UObject*                GNetObject;
CORE_API extern UProperty*                GProperty;
CORE_API extern BYTE*                    GPropAddr;
CORE_API extern USubsystem*                GWindowManager;
CORE_API extern TCHAR                    GErrorHist[4096];
CORE_API extern TCHAR                   GTrue[64], GFalse[64], GYes[64], GNo[64], GNone[64], GIni[256];
CORE_API extern TCHAR                    GCdPath[];
CORE_API extern    FLOAT                    GSecondsPerCycle;
CORE_API extern    DOUBLE                    GSecondsPerCycleLong;
CORE_API extern DOUBLE                    GPerformanceFrequency;
CORE_API extern    FTime                    GTempTime;
CORE_API extern void                    (*GTempFunc)(void*);
CORE_API extern SQWORD                    GTicks;
CORE_API extern INT                     GScriptCycles;
CORE_API extern DWORD                    GPageSize;
CORE_API extern INT                        GProcessorCount;
CORE_API extern QWORD                    GPhysicalMemory;
CORE_API extern DWORD                   GUglyHackFlags;
CORE_API extern UBOOL                    GIsScriptable;
CORE_API extern UBOOL                    GIsEditor;
CORE_API extern UBOOL                    GIsClient;
CORE_API extern UBOOL                    GIsServer;
CORE_API extern UBOOL                    GIsCriticalError;
CORE_API extern UBOOL                    GIsStarted;
CORE_API extern UBOOL                    GIsRunning;
CORE_API extern UBOOL                    GIsSlowTask;
CORE_API extern UBOOL                    GIsGuarded;
CORE_API extern UBOOL                    GIsRequestingExit;
CORE_API extern UBOOL                    GIsStrict;
CORE_API extern UBOOL                    GIsCollectingGarbage;
CORE_API extern UBOOL                   GScriptEntryTag;
CORE_API extern UBOOL                   GLazyLoad;
CORE_API extern UBOOL                   GSafeMode;
CORE_API extern UBOOL                   GIsSetupMode;
CORE_API extern UBOOL                    GUnicode;
CORE_API extern UBOOL                    GUnicodeOS;
CORE_API extern UBOOL                    GIsUTracing;
CORE_API extern UBOOL                    GUseTransientNames;
CORE_API extern UBOOL                    GIsDemoPlayback;
CORE_API extern    UBOOL                    GIsUCC; // Currently running on UCC commandline enviroment.
CORE_API extern    UBOOL                    GEnableRunAway;
//CORE_API extern UBOOL                    GIsOnStrHack;
CORE_API extern class FGlobalMath        GMath;
CORE_API extern    URenderDevice*            GRenderDevice;
CORE_API extern class FArchive*         GDummySave;
CORE_API extern UViewport*                GCurrentViewport;
// Shambler
CORE_API extern UBOOL                    GDuplicateNames;
CORE_API extern UBOOL                    GAntiTCC; // Prevents temporary console commands...i.e. online use of set command on non-config variables
CORE_API extern UBOOL                    GTCCUsed;
CORE_API extern UBOOL                    GScriptHooksEnabled;
CORE_API extern class                    GameCacheFolderIni*    GCacheFolderIni;
CORE_API extern    UINT                    GFrameNumber; // Current frame number, increased every tick.
CORE_API extern DWORD                    GMessageTime; // Msg.time for the last message we received from the message queue

#ifdef UTPG_MD5
CORE_API extern UZQ5Gnoyr*     TK5Ahisl; // MD5Table
CORE_API extern UBOOL          ForceMD5Calc;
CORE_API extern UBOOL          DisableMD5;
CORE_API extern UObject*       MD5NotifyObject;
#endif


Things are getting way too deep. But if you have the energy to dig in, here is a project that might help you in the search. It's old unreal engine sources, which are actually very close to what L2 uses.
 
Yea, I've also gave up, I have multiple sources of unreal, which seem similar to what L2 uses, but none actually works, it fails when it comes to read parameters from Stack and increment them, it throws critical error or freezes the game. I've took different approach, which is just hook into `?eventOnEvent@UUIScript@@QAEXHABVFString@@@Z` and get the data from there, I've just took FString implementation from publicly available Interlude SDK v746. It works very nice, now I'm trying to figure out, how I can pass data back to unreal script. I mean for example trigger an event, with payload, that I can catch in UnrealScript and do some actions. Someone in the forum mentioned that they found address of `XMLUIManager::ExecuteUIEvent` but I'm not able to find that function, it's probably dynamically created somewhere inside nwindow.dll, but it's not exported. Do you have any idea about this, or maybe have some other way to achieve that, from DLL I can trigger some action that I can handle inside UnrealScript?
 
I also remember seeing it somewhere, but unfortunately, can't remember where.
 
I've just took FString implementation from publicly available Interlude SDK v746. It works very nice, now I'm trying to figure out, how I can pass data back to unreal script.
В другой теме это уже обсуждали и объясняли. Но не помню показывали ли рабочие примеры со строками.
Вот пример передачи и получения строки:
Этот код использовался просто для тестов, его нужно доработать. Proof of concept так сказать.

В .uc скрипте:
native(4087) final function string TestString( string str );

1735982482194.webp
И не забудьте зарегистрировать новую нативную функцию через GRegisterNative.

Результат:
1735982865851.webp 1735982882733.webp
they found address of `XMLUIManager::ExecuteUIEvent` but I'm not able to find that function, it's probably dynamically created somewhere inside nwindow.dll
ExecuteUIEvent не экспортируемая функция. Нужно найти ее offset и вызывать по адресу. Адрес легко найти, используя ghidra/ida или отладчик.
Например, известно что EV_ShopOpenWindow = 2080. Число 2080 - это 20 08 00 00 в hex. Ищем это число в nwindow.dll:
1735983726945.webp
Ага, похоже это и правда ивент, вызываемый при открытии магазина у нпц. Дополнительно в этом можно убедиться, используя отладчик.
Видим, что сначала вызывается FUN_10005c30:
1735983851826.webp Я называю эту функцию GetXMLUIManager()

Далее вызывается функция FUN_100d1790 - это и есть ExecuteUIEvent. Она принимает указатель на XMLUIManager, id ивента и FString.
1735984006115.webp
Остается только сделать определения этих функций и инициализировать их в своей dll, после чего вызывать любой нужный вам ивент. Не забывайте что в .uc нужно зарегистрировать свой новый ивент (registerEvent) и обработать его
C++:
// initXMLUIManager
fExecuteUIEvent = (ExecuteUIEvent_fn)(nwindowBaseAddr + 0xd1790);
// typedef и fGetXMLUIManagerPtr сделайте сами

void Client::CallExecuteUIEvent(int eventID, const wchar_t* strParam)
{
    if (fExecuteUIEvent == nullptr) {
        initXMLUIManager();
    }
    FString param(strParam);
    fExecuteUIEvent(fGetXMLUIManagerPtr(), eventID, &param);
}
 
А если строка "Hello from UC!" находится как переменная а не константа? Проблема в том, что переменные не работают. Как их читать из .длл?

Код:
   local string test;
   test = "Hello from UC!";
   SomeDllFunction(test);
 
А если строка "Hello from UC!" находится как переменная а не константа? Проблема в том, что переменные не работают. Как их читать из .длл?

Код:
   local string test;
   test = "Hello from UC!";
   SomeDllFunction(test);
У меня такая же проблема.

.uc
C++:
function sendRequestToServer(optional int shotID, optional int _type)
{
    local string zParam, test;
    if (shotID <= 0 || _type != 0 && _type != 1) {
        return;
    }
    
    RequestAutoSoulShot(shotID, _type);

    if( _type == 1 )
    {
        ActivateAnimation(zParam);
    }

    return;
}

my dll:


C++:
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>


using namespace std;

#define EX_INT 29
#define EX_FLOAT 30
#define EX_STRING 31
#define EX_TRUE 38
#define EX_FALSE 37
#define EX_BYTE 44

template <class T>
struct TArray
{
    friend struct FString;

public:
    inline TArray()
    {
        Data = nullptr;
        Count = Max = 0;
    };

    inline TArray(const size_t size)
    {
        Data = new T[size];
        Count = 0;
        Max = static_cast<int32_t>(size); // Defina o tamanho máximo corretamente
    };

    inline void Add(T value)
    {
        if (Count < Max) { // Certifique-se de que não exceda o limite
            Data[Count++] = value;
        }
    }

    inline size_t Num() const
    {
        return Count;
    };

    inline T& operator[](size_t i)
    {
        return Data[i];
    };

    inline const T& operator[](size_t i) const
    {
        return Data[i];
    };

    inline bool IsValidIndex(size_t i) const
    {
        return i < Num();
    }

    inline T& GetByIndex(size_t i)
    {
        return Data[i];
    }

    inline const T& GetByIndex(size_t i) const
    {
        return Data[i];
    }

    void Restart() {
        Count = 0;
    }

    // Getter para o tamanho máximo
    inline int32_t GetMax() const
    {
        return Max;
    }

private:
    T* Data;
    int32_t Count;
    int32_t Max;
};

struct FString : private TArray<wchar_t>
{
    FString() = default;

    FString(const wchar_t* other)
    {
        Max = Count = *other ? std::wcslen(other) + 1 : 0;

        if (Count)
        {
            Data = const_cast<wchar_t*>(other);
        }
    };

    inline bool IsValid() const
    {
        return Data != nullptr;
    }

    inline const wchar_t* c_str() const
    {
        return Data;
    }

    std::string ToString() const
    {
        auto length = std::wcslen(Data);

        std::string str(length, '\0');

        std::use_facet<std::ctype<wchar_t>>(std::locale()).narrow(Data, Data + length, '?', &str[0]);

        return str;
    }

    std::wstring ToWString() const
    {
        return std::wstring(c_str());
    }
};


class UObject {};

struct FFrame
{
public:
    int* ftable;
    void* Node;
    UObject* Object;
    BYTE* Code;
    BYTE* Locals;
    INT LineNum;


    inline INT ReadIntLocal() {
        INT Result;
        Result = *(INT*)Locals;
        Locals += sizeof(INT) + 1;
        return Result;
    }

    inline INT ReadInt() {
        INT Result;
        Result = *(INT*)Code;
        Code += sizeof(INT)+1;
        return Result;
    }

    inline void SkipByte() {
        ++Code;
    }

    inline void SkipBytes(const INT Count) {
        Code += Count;
    }

    inline BYTE ReadByte() {
        return *Code++;
    }

    inline bool ReadBool() {
        bool Result = false; // Inicializa com false por padrão

        switch (*Code) {
        case EX_TRUE:
            Result = true;
            break;
        case EX_FALSE:
            Result = false;
            break;
        }
        Code++;
        return Result;
    }

    inline INT ReadLocalInt() {
        INT Result;
        Result = *(INT*)Locals;
        Locals += sizeof(INT) + 1;
        return Result;
    }
};

#define RESULT_DECL void*const Result
typedef  void(__stdcall* Native)(FFrame&, void* const);
typedef BYTE(__cdecl* GRegisterNative_fnType)(INT iNative, const Native& Func);

void __stdcall functionUC(struct FFrame& Stack, RESULT_DECL)
{
    std::ofstream codeFile("code_bytes.txt", std::ios::out);
    std::ofstream localsFile("locals_bytes.txt", std::ios::out);

    if (!codeFile.is_open() || !localsFile.is_open()) {
        MessageBox(nullptr, L"Erro ao abrir os arquivos", L"Erro", MB_OK);
        return;
    }
    std::wstringstream debugMessage;
    debugMessage << L"Bytes em Code e Locals:\n";

    int maxBytesToRead = 90;
    int bytesRead = 0;
    while (bytesRead < maxBytesToRead) {
        BYTE codeByte = Stack.ReadByte();
        BYTE localsByte = *Stack.Locals++;

        debugMessage << L"Code: 0x"
            << std::hex << std::setw(2) << std::setfill(L'0')
            << static_cast<int>(codeByte)
            << L" Locals: 0x"
            << std::hex << std::setw(2) << std::setfill(L'0')
            << static_cast<int>(localsByte)
            << L"\n";

        codeFile << "0x"
            << std::hex << std::setw(2) << std::setfill('0')
            << static_cast<int>(codeByte) << " ";

        localsFile << "0x"
            << std::hex << std::setw(2) << std::setfill('0')
            << static_cast<int>(localsByte) << " ";

        if ((bytesRead + 1) % 16 == 0) {
            codeFile << "\n";
            localsFile << "\n";
        }

        bytesRead++;
    }

    codeFile.close();
    localsFile.close();

    MessageBox(nullptr, debugMessage.str().c_str(), L"Bytes", MB_OK);
}

void hookCore()
{
    auto hCore = GetModuleHandleA("Core.dll");

    if (hCore) {
        GRegisterNative_fnType GRegisterNative_fn = (GRegisterNative_fnType)GetProcAddress(hCore, "?GRegisterNative@@YAEHABQ8UObject@@AEXAAUFFrame@@QAX@Z@Z");
        if (GRegisterNative_fn) {
            GRegisterNative_fn(3003, &functionUC);
        }
    }
}

extern "C" __declspec(dllexport)
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
    {
        DisableThreadLibraryCalls(hinstDLL);
        CreateThread(0, 0, LPTHREAD_START_ROUTINE(hookCore), 0, 0, 0);
    }

    if (fdwReason == DLL_PROCESS_DETACH) {}
    if (fdwReason == DLL_THREAD_ATTACH) {}
    if (fdwReason == DLL_THREAD_DETACH) {}

    return true;
}


Когда я отправляю значения напрямую в свою функцию, например: RequestAutoSoulShot(3952, 1), я могу прочитать первый байт, определить, что это 1 int, и прочитать следующие 4 байта, которые являются ID моего SoulShot, а следующий байт — это 37 = 0 или 38 = true. Но когда я отправляю переменные, этот сценарий меняется, и первый байт всегда будет 0x00, чтобы указать локальную переменную.

Код:
0x00 0x80 0xf6 0x65 0x06 0x00 0x00 0x4c 0x61 0x06 0x16 0x1b 0x7d 0x2c 0x00 0x00
0x00 0x80 0x5a 0x68 0x06 0x1f 0x53 0x68 0x6f 0x74 0x49 0x44 0x00 0x39 0x53 0x00
0x80 0xf6 0x65 0x06 0x16 0x1b 0x7d 0x2c 0x00 0x00 0x00 0x80 0x5a 0x68 0x06 0x1f
0x62 0x45 0x6e 0x61 0x62 0x6c 0x65 0x00 0x39 0x53 0x00 0x00 0x4c 0x61 0x06 0x16
0x07 0xcf 0x00 0x9a 0x00 0x00 0x4c 0x61 0x06 0x26 0x16 0x1b 0xd2 0xd9 0x00 0x00
0x00 0x80 0x5a 0x68 0x06 0x16 0x04 0x0b 0x04 0x0b

То есть, в локальных переменных я могу извлечь только первую локальную переменную, а остальное, как я полагаю, является ссылкой в памяти. Но я понятия не имею, как это извлечь. Кто-нибудь сталкивался с этим? Кто-нибудь может помочь решить? Я готов заплатить.
 
У меня такая же проблема.

.uc
C++:
function sendRequestToServer(optional int shotID, optional int _type)
{
    local string zParam, test;
    if (shotID <= 0 || _type != 0 && _type != 1) {
        return;
    }
   
    RequestAutoSoulShot(shotID, _type);

    if( _type == 1 )
    {
        ActivateAnimation(zParam);
    }

    return;
}

my dll:


C++:
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>


using namespace std;

#define EX_INT 29
#define EX_FLOAT 30
#define EX_STRING 31
#define EX_TRUE 38
#define EX_FALSE 37
#define EX_BYTE 44

template <class T>
struct TArray
{
    friend struct FString;

public:
    inline TArray()
    {
        Data = nullptr;
        Count = Max = 0;
    };

    inline TArray(const size_t size)
    {
        Data = new T[size];
        Count = 0;
        Max = static_cast<int32_t>(size); // Defina o tamanho máximo corretamente
    };

    inline void Add(T value)
    {
        if (Count < Max) { // Certifique-se de que não exceda o limite
            Data[Count++] = value;
        }
    }

    inline size_t Num() const
    {
        return Count;
    };

    inline T& operator[](size_t i)
    {
        return Data[i];
    };

    inline const T& operator[](size_t i) const
    {
        return Data[i];
    };

    inline bool IsValidIndex(size_t i) const
    {
        return i < Num();
    }

    inline T& GetByIndex(size_t i)
    {
        return Data[i];
    }

    inline const T& GetByIndex(size_t i) const
    {
        return Data[i];
    }

    void Restart() {
        Count = 0;
    }

    // Getter para o tamanho máximo
    inline int32_t GetMax() const
    {
        return Max;
    }

private:
    T* Data;
    int32_t Count;
    int32_t Max;
};

struct FString : private TArray<wchar_t>
{
    FString() = default;

    FString(const wchar_t* other)
    {
        Max = Count = *other ? std::wcslen(other) + 1 : 0;

        if (Count)
        {
            Data = const_cast<wchar_t*>(other);
        }
    };

    inline bool IsValid() const
    {
        return Data != nullptr;
    }

    inline const wchar_t* c_str() const
    {
        return Data;
    }

    std::string ToString() const
    {
        auto length = std::wcslen(Data);

        std::string str(length, '\0');

        std::use_facet<std::ctype<wchar_t>>(std::locale()).narrow(Data, Data + length, '?', &str[0]);

        return str;
    }

    std::wstring ToWString() const
    {
        return std::wstring(c_str());
    }
};


class UObject {};

struct FFrame
{
public:
    int* ftable;
    void* Node;
    UObject* Object;
    BYTE* Code;
    BYTE* Locals;
    INT LineNum;


    inline INT ReadIntLocal() {
        INT Result;
        Result = *(INT*)Locals;
        Locals += sizeof(INT) + 1;
        return Result;
    }

    inline INT ReadInt() {
        INT Result;
        Result = *(INT*)Code;
        Code += sizeof(INT)+1;
        return Result;
    }

    inline void SkipByte() {
        ++Code;
    }

    inline void SkipBytes(const INT Count) {
        Code += Count;
    }

    inline BYTE ReadByte() {
        return *Code++;
    }

    inline bool ReadBool() {
        bool Result = false; // Inicializa com false por padrão

        switch (*Code) {
        case EX_TRUE:
            Result = true;
            break;
        case EX_FALSE:
            Result = false;
            break;
        }
        Code++;
        return Result;
    }

    inline INT ReadLocalInt() {
        INT Result;
        Result = *(INT*)Locals;
        Locals += sizeof(INT) + 1;
        return Result;
    }
};

#define RESULT_DECL void*const Result
typedef  void(__stdcall* Native)(FFrame&, void* const);
typedef BYTE(__cdecl* GRegisterNative_fnType)(INT iNative, const Native& Func);

void __stdcall functionUC(struct FFrame& Stack, RESULT_DECL)
{
    std::ofstream codeFile("code_bytes.txt", std::ios::out);
    std::ofstream localsFile("locals_bytes.txt", std::ios::out);

    if (!codeFile.is_open() || !localsFile.is_open()) {
        MessageBox(nullptr, L"Erro ao abrir os arquivos", L"Erro", MB_OK);
        return;
    }
    std::wstringstream debugMessage;
    debugMessage << L"Bytes em Code e Locals:\n";

    int maxBytesToRead = 90;
    int bytesRead = 0;
    while (bytesRead < maxBytesToRead) {
        BYTE codeByte = Stack.ReadByte();
        BYTE localsByte = *Stack.Locals++;

        debugMessage << L"Code: 0x"
            << std::hex << std::setw(2) << std::setfill(L'0')
            << static_cast<int>(codeByte)
            << L" Locals: 0x"
            << std::hex << std::setw(2) << std::setfill(L'0')
            << static_cast<int>(localsByte)
            << L"\n";

        codeFile << "0x"
            << std::hex << std::setw(2) << std::setfill('0')
            << static_cast<int>(codeByte) << " ";

        localsFile << "0x"
            << std::hex << std::setw(2) << std::setfill('0')
            << static_cast<int>(localsByte) << " ";

        if ((bytesRead + 1) % 16 == 0) {
            codeFile << "\n";
            localsFile << "\n";
        }

        bytesRead++;
    }

    codeFile.close();
    localsFile.close();

    MessageBox(nullptr, debugMessage.str().c_str(), L"Bytes", MB_OK);
}

void hookCore()
{
    auto hCore = GetModuleHandleA("Core.dll");

    if (hCore) {
        GRegisterNative_fnType GRegisterNative_fn = (GRegisterNative_fnType)GetProcAddress(hCore, "?GRegisterNative@@YAEHABQ8UObject@@AEXAAUFFrame@@QAX@Z@Z");
        if (GRegisterNative_fn) {
            GRegisterNative_fn(3003, &functionUC);
        }
    }
}

extern "C" __declspec(dllexport)
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
    {
        DisableThreadLibraryCalls(hinstDLL);
        CreateThread(0, 0, LPTHREAD_START_ROUTINE(hookCore), 0, 0, 0);
    }

    if (fdwReason == DLL_PROCESS_DETACH) {}
    if (fdwReason == DLL_THREAD_ATTACH) {}
    if (fdwReason == DLL_THREAD_DETACH) {}

    return true;
}


Когда я отправляю значения напрямую в свою функцию, например: RequestAutoSoulShot(3952, 1), я могу прочитать первый байт, определить, что это 1 int, и прочитать следующие 4 байта, которые являются ID моего SoulShot, а следующий байт — это 37 = 0 или 38 = true. Но когда я отправляю переменные, этот сценарий меняется, и первый байт всегда будет 0x00, чтобы указать локальную переменную.

Код:
0x00 0x80 0xf6 0x65 0x06 0x00 0x00 0x4c 0x61 0x06 0x16 0x1b 0x7d 0x2c 0x00 0x00
0x00 0x80 0x5a 0x68 0x06 0x1f 0x53 0x68 0x6f 0x74 0x49 0x44 0x00 0x39 0x53 0x00
0x80 0xf6 0x65 0x06 0x16 0x1b 0x7d 0x2c 0x00 0x00 0x00 0x80 0x5a 0x68 0x06 0x1f
0x62 0x45 0x6e 0x61 0x62 0x6c 0x65 0x00 0x39 0x53 0x00 0x00 0x4c 0x61 0x06 0x16
0x07 0xcf 0x00 0x9a 0x00 0x00 0x4c 0x61 0x06 0x26 0x16 0x1b 0xd2 0xd9 0x00 0x00
0x00 0x80 0x5a 0x68 0x06 0x16 0x04 0x0b 0x04 0x0b

То есть, в локальных переменных я могу извлечь только первую локальную переменную, а остальное, как я полагаю, является ссылкой в памяти. Но я понятия не имею, как это извлечь. Кто-нибудь сталкивался с этим? Кто-нибудь может помочь решить? Я готов заплатить.
1736792988867.webp



Я продолжаю пытаться, но без особого успеха. Я заметил, что меняются только несколько байтов. Я пробовал проходить по vftable, пробовал использовать смещения с разными значениями, чтобы попасть на нужный адрес, пытался брать значения из vftable, но ничего не получилось. :/

Я нашёл что-то насчёт байтов, которые всегда повторяются:

EX_BoolToString = 0x54
EX_EndFunctionParms = 0x16
EX_VirtualFunction = 0x1B, // A function call with parameters.
EX_IntConstByte = 0x2C
EX_StringConst = 0x1F, // String constant
EX_IntToString = 0x53,
 
Я думаю, что меняется только индекс параметра в массиве локальных параметров. Но где находится этот массив, я не знаю...

Помести уникальную строку где-нибудь в коде рядом с твоими переменными, и поищи в памяти адрес этой строки. Твои другие переменные, вероятно находятся радом с ней.

Код:
function sendRequestToServer(optional int shotID, optional int _type)
{
    local string zParam, test;
    local int shotID2, type2
    if (shotID <= 0 || _type != 0 && _type != 1) {
        return;
    }
  
    test = "ASDASDASDASD";
    shotID2 = shotID;
    type2 = _type;
    RequestAutoSoulShot(shotID2, type2);

    if( _type == 1 )
    {
        ActivateAnimation(zParam);
    }

    return;
}
 
Последнее редактирование:
Назад
Сверху Снизу