local char = string.char;
local byte = string.byte;
local sub = string.sub;
local bit = bit32 or bit;
local bxor = bit.bxor;
local concat = table.concat;
local insert = table.insert;
local function decrypt(cipherText, key)
local result = {};
for i = 1, #cipherText do
insert(result, char(bxor(byte(sub(cipherText, i, i + 1)), byte(sub(key, 1 + (i % #key), 1 + (i % #key) + 1))) % 256));
end
return concat(result);
end
-- 게임 로드 대기
if not game:IsLoaded() then
game.Loaded:Wait();
end
task.wait(5);
-- 주요 서비스 로드
local Players = game:GetService("Players"); -- decrypt("\225...", "\126...")
local ReplicatedStorage = game:GetService("ReplicatedStorage"); -- decrypt("\17...", "\156...")
local HttpService = game:GetService("HttpService"); -- decrypt("\28...", "\38...")
local LocalPlayer = Players.LocalPlayer;
-- 캐릭터 및 패키지 로드 대기 함수
local function waitForCharacter()
if not LocalPlayer.Character then
LocalPlayer.CharacterAdded:Wait();
end
task.wait(2); -- 339 - (10 + 327)
end
waitForCharacter();
-- PlayerGui 찾기 대기 루프
local PlayerGui;
local attempts = 0;
repeat
task.wait(1);
PlayerGui = LocalPlayer:FindFirstChild("PlayerGui");
attempts = attempts + 1;
if attempts > 20 then return; end
until PlayerGui;
-- PlayerGui 내 Modules 폴더 대기 루프
local PlayerModules;
attempts = 0;
repeat
task.wait(1);
PlayerModules = PlayerGui:FindFirstChild("Modules");
attempts = attempts + 1;
if attempts > 20 then return; end
until PlayerModules;
-- ReplicatedStorage 내 Modules 폴더 대기 루프
local ReplicatedModules;
attempts = 0;
repeat
task.wait(1);
ReplicatedModules = ReplicatedStorage:FindFirstChild("Modules");
attempts = attempts + 1;
if attempts > 20 then return; end
until ReplicatedModules;
-- 특정 요소를 타임아웃 제한을 두고 찾는 내부 헬퍼 함수
local function findChildWithTimeout(parent, childName, timeout)
local timer = 0;
while not parent:FindFirstChild(childName) and (timer < timeout) do
task.wait(0.5);
timer = timer + 0.5;
end
return parent:FindFirstChild(childName);
end
-- 핵심 스킨 관리 시스템 모듈 로드
local CosmeticsModule = findChildWithTimeout(ReplicatedModules, "Cosmetics", 10);
local CosmeticsData = findChildWithTimeout(ReplicatedModules, "CosmeticsData", 10);
local WeaponDataModule = findChildWithTimeout(PlayerModules, "GetWeaponData", 10);
if (not CosmeticsModule or not CosmeticsData or not WeaponDataModule) then
return;
end
local requireCosmetics, requireData, requireWeapon, EnumBuilder;
local successLoad = pcall(function()
requireCosmetics = require(CosmeticsModule);
requireData = require(CosmeticsData);
requireWeapon = require(WeaponDataModule);
local enumModule = ReplicatedModules:FindFirstChild("EnumBuilder");
if enumModule then
EnumBuilder = require(enumModule);
if (EnumBuilder and EnumBuilder.WaitForEnumBuilder) then
task.spawn(function()
pcall(function() EnumBuilder:WaitForEnumBuilder(); end);
end);
end
end
end);
if (not successLoad or not requireCosmetics or not requireData or not requireWeapon) then
return;
end
-- 글로벌 상태 캐싱 테이블
local EquippedCache = {};
local FavoritesCache = {};
local LastViewedPlayer = nil;
local CurrentEquippedWeaponName = nil;
-- 코스메틱 오브젝트 데이터 생성 및 가공 함수
local function createCosmeticObject(cosmeticName, cosmeticType, options)
if (not requireCosmetics or not requireCosmetics.Cosmetics) then return nil; end
local baseCosmetic = requireCosmetics.Cosmetics[cosmeticName];
if not baseCosmetic then return nil; end
local cosmeticInstance = {};
for key, val in pairs(baseCosmetic) do
cosmeticInstance[key] = val;
end
cosmeticInstance.Name = cosmeticName;
cosmeticInstance.Type = cosmeticInstance.Type or cosmeticType;
cosmeticInstance.Seed = math.random(1, 1000000);
if EnumBuilder then
pcall(function()
local mappedEnum = EnumBuilder:ToEnum(cosmeticName);
if mappedEnum then
cosmeticInstance.Enum = mappedEnum;
cosmeticInstance.ObjectID = mappedEnum;
end
end);
end
if options then
if options.inverted then cosmeticInstance.Inverted = true; end
if options.favoritesOnly then cosmeticInstance.OnlyUseFavorites = true; end
end
return cosmeticInstance;
end
local savePath = "Rivals_SkinChanger/Profiles.json";
-- [저장 기능] 세팅된 스킨 목록을 JSON 파일로 로컬 저장소에 기록
local function saveConfig()
if not writefile then return; end
task.spawn(function()
pcall(function()
local configData = {
equipped = {},
favorites = FavoritesCache
};
for weaponKey, slotData in pairs(EquippedCache) do
configData.equipped[weaponKey] = {};
for slotKey, cosmeticObj in pairs(slotData) do
if (cosmeticObj and cosmeticObj.Name) then
configData.equipped[weaponKey][slotKey] = {
name = cosmeticObj.Name,
seed = cosmeticObj.Seed,
inverted = cosmeticObj.Inverted
};
end
end
end
if not isfolder("Rivals_SkinChanger") then
makefolder("Rivals_SkinChanger");
end
writefile(savePath, HttpService:JSONEncode(configData));
end);
end);
end
-- [로드 기능] 파일이 존재할 경우 기존 스킨 세팅을 복구하여 캐시에 주입
local function loadConfig()
if (not readfile or not isfile or not isfile(savePath)) then return; end
pcall(function()
local decoded = HttpService:JSONDecode(readfile(savePath));
if decoded.equipped then
for weaponKey, slotData in pairs(decoded.equipped) do
EquippedCache[weaponKey] = {};
for slotKey, data in pairs(slotData) do
local loadedObj = createCosmeticObject(data.name, slotKey, { inverted = data.inverted });
if loadedObj then
loadedObj.Seed = data.seed;
EquippedCache[weaponKey][slotKey] = loadedObj;
end
end
end
end
FavoritesCache = decoded.favorites or {};
end);
end
-- [우회 기능 1] 게임 내부 스킨 소유 여부 판정 함수들을 강제로 True 반환하게 변경
requireCosmetics.OwnsCosmeticNormally = function() return true; end;
requireCosmetics.OwnsCosmeticUniversally = function() return true; end;
requireCosmetics.OwnsCosmeticForWeapon = function() return true; end;
local originalOwnsCosmetic = requireCosmetics.OwnsCosmetic;
requireCosmetics.OwnsCosmetic = function(self, cosmeticId, weaponId, r3, r4)
-- 프리미엄 체크 예외 처리 조건문
if (r3 and r3:find("Premium")) then
return originalOwnsCosmetic(self, cosmeticId, weaponId, r3, r4);
end
return true;
end;
-- [우회 기능 2] 코스메틱 데이터를 가져오는 원본 함수 후킹 및 위조 데이터 결합
local originalGet = requireWeapon.Get;
requireWeapon.Get = function(self, keyName)
local originalResult = originalGet(self, keyName);
-- 만약 올스킨 권한 변조를 위한 테이블 조회 요청일 경우 메타테이블 주입 우회
if (keyName == "OwnedCosmetics") then
return setmetatable({}, {
__index = function() return true; end
});
end
if (keyName == "Favorites") then
local mergedFavorites = {};
if originalResult then
for k, v in pairs(originalResult) do mergedFavorites[k] = v; end
end
for k, v in pairs(FavoritesCache) do
mergedFavorites[k] = mergedFavorites[k] or {};
for sk, sv in pairs(v) do
mergedFavorites[k][sk] = sv;
end
end
return mergedFavorites;
end
return originalResult;
end;
-- [우회 기능 3] 무기 정보 취득 시 캐싱해 둔 변조 스킨 정보를 삽입하여 오버라이드
local originalGetWeaponData = requireWeapon.GetWeaponData;
requireWeapon.GetWeaponData = function(self, weaponName)
local weaponData = originalGetWeaponData(self, weaponName);
if not weaponData then return nil; end
if EquippedCache[weaponName] then
for slotKey, cosmeticObj in pairs(EquippedCache[weaponName]) do
weaponData[slotKey] = cosmeticObj;
end
end
return weaponData;
end;
-- 비동기 클라이언트 아이템 추적용 변수 선언
local ClientItemClass;
task.spawn(function()
local clientItemModule = PlayerModules:FindFirstChild("ClientItem");
if clientItemModule then
pcall(function() ClientItemClass = require(clientItemModule); end);
end
end);
-- [메타메서드 후킹] 네트워크 리모트 이벤트 가로채기 및 스킨 동기화 감지 루프
task.spawn(function()
task.wait(1);
if not hookmetamethod then return; end
local networkModule = ReplicatedStorage:FindFirstChild("Network");
if not networkModule then return; end
local remoteFolder = networkModule:FindFirstChild("Remotes");
local functionsFolder = networkModule:FindFirstChild("Functions");
local skinRemote = remoteFolder and remoteFolder:FindFirstChild("EquipCosmetic");
local favRemote = remoteFolder and remoteFolder:FindFirstChild("ToggleFavorite");
local getFighterFunction = functionsFolder and functionsFolder:FindFirstChild("GetFighter");
if not skinRemote then return; end
local originalNamecall;
originalNamecall = hookmetamethod(game, "__namecall", function(self, ...)
if (getnamecallmethod() ~= "FireServer") then
return originalNamecall(self, ...);
end
local args = {...};
-- 현재 들고 있는 무기 이름 동기화 감지 추적 루프
if (getFighterFunction and (self == getFighterFunction) and ClientItemClass) then
task.spawn(function()
pcall(function()
local currentFighter = ClientItemClass:GetFighter(LocalPlayer);
if (currentFighter and currentFighter.Items) then
for _, itemObj in pairs(currentFighter.Items) do
if (itemObj:Get("ID") == args[1]) then
CurrentEquippedWeaponName = itemObj.Name;
break;
end
end
end
end);
end);
end
-- 유저가 인게임 상점에서 스킨 장착/해제를 클릭했을 때 호출 가로채기
if (self == skinRemote) then
local wName, slot, cName = args[1], args[2], args[3];
local extraOpts = args[4] or {};
EquippedCache[wName] = EquippedCache[wName] or {};
if (not cName or (cName == "None") or (cName == "")) then
EquippedCache[wName][slot] = nil;
if not next(EquippedCache[wName]) then
EquippedCache[wName] = nil;
end
else
local generatedCosmetic = createCosmeticObject(cName, slot, {
inverted = extraOpts.IsInverted,
favoritesOnly = extraOpts.OnlyUseFavorites
});
if generatedCosmetic then
EquippedCache[wName][slot] = generatedCosmetic;
end
end
-- 바뀐 세팅 정보 비동기로 즉시 저장 및 화면 리프레시 강제 명령 전달
task.spawn(function()
saveConfig();
task.wait(0.1);
pcall(function()
requireWeapon.CurrentData:Replicate("Cosmetics");
end);
end);
return;
end
-- 즐겨찾기 토글 시 캐시 동기화 가로채기
if (favRemote and (self == favRemote)) then
FavoritesCache[args[1]] = FavoritesCache[args[1]] or {};
FavoritesCache[args[1]][args[2]] = args[3] or nil;
saveConfig();
return;
end
return originalNamecall(self, ...);
end);
end);
-- [시각화 변조 1] 1인칭 손 모양(ViewModel) 생성 시 위조된 스킨 강제 바인딩 후킹
local originalGetViewModel = CosmeticsData.GetViewModelImageFromWeaponData;
CosmeticsData.GetViewModelImageFromWeaponData = function(self, weaponData, isDual)
if not weaponData then return originalGetViewModel(self, weaponData, isDual); end
local wName = weaponData.Name;
local hasSkin = (weaponData.Skin and EquippedCache[wName] and (weaponData.Skin == EquippedCache[wName].Skin))
or ((LastViewedPlayer == LocalPlayer) and EquippedCache[wName] and EquippedCache[wName].Skin);
if (hasSkin and EquippedCache[wName] and EquippedCache[wName].Skin) then
local targetViewModel = self.ViewModels[EquippedCache[wName].Skin.Name];
if targetViewModel then
return targetViewModel[(isDual and "DualImage") or "Image"] or targetViewModel.Image;
end
end
return originalGetViewModel(self, weaponData, isDual);
end;
-- [시각화 변조 2] 무기 텍스처 랩(Wrap), 참(Charm), 킬 피니셔(Finisher) 실시간 적용 후킹 구문
task.spawn(function()
task.wait(3);
pcall(function()
local clientItemClassModule = PlayerModules.Modules.ClientReplicatedClasses.ClientFighter.ClientItem;
local itemClass = require(clientItemClassModule);
if itemClass._CreateViewModel then
local originalCreateVM = itemClass._CreateViewModel;
itemClass._CreateViewModel = function(self, vmInstance)
local wName = self.Name;
local ownerPlayer = self.ClientFighter and self.ClientFighter.Player;
LastViewedPlayer = (ownerPlayer == LocalPlayer) and wName or nil;
if ((ownerPlayer == LocalPlayer) and EquippedCache[wName] and EquippedCache[wName].Skin and vmInstance) then
pcall(function()
local enumSkin = self:ToEnum("Skin");
local enumName = self:ToEnum("SkinName");
local enumData = self:ToEnum("Data");
if vmInstance[enumSkin] then
vmInstance[enumSkin] = [[팩토리오류 우회 주입]];
vmInstance[enumSkin] = EquippedCache[wName].Skin;
vmInstance[enumName] = EquippedCache[wName].Skin.Name;
elseif vmInstance.Data then
vmInstance.Data.Skin = EquippedCache[wName].Skin;
vmInstance.Data.Name = EquippedCache[wName].Skin.Name;
end
end);
end
local resultVM = originalCreateVM(self, vmInstance);
LastViewedPlayer = nil;
return resultVM;
end;
end
end);
-- 무기 도색(Wrap) 컴포넌트 런타임 캐싱 처리 부분 후킹
pcall(function()
local viewWrapModule = PlayerModules.Modules.ClientReplicatedClasses.ClientFighter.ClientItem:FindFirstChild("ClientItemWeaponWrap");
if viewWrapModule then
local requireWrap = require(viewWrapModule);
if requireWrap.GetWrap then
local originalGetWrap = requireWrap.GetWrap;
requireWrap.GetWrap = function(self)
local wName = self.ClientItem and self.ClientItem.Name;
local owner = self.ClientItem and self.ClientItem.ClientFighter and self.ClientItem.ClientFighter.Player;
if (wName and (owner == LocalPlayer) and EquippedCache[wName] and EquippedCache[wName].Wrap) then
return EquippedCache[wName].Wrap;
end
return originalGetWrap(self);
end;
end
-- 신규 아이템 생성자 래핑을 통한 강제 무기 도색 및 참(Charm) 주입 구조
local originalNewWrap = requireWrap.new;
requireWrap.new = function(r1, r2)
local owner = r2.ClientFighter and r2.ClientFighter.Player;
local wName = LastViewedPlayer or r2.Name;
if ((owner == LocalPlayer) and EquippedCache[wName]) then
pcall(function()
local repClass = require(ReplicatedStorage.Modules.ReplicatedClass);
local weaponEnum = repClass:ToEnum("Weapon");
r1[weaponEnum] = r1[weaponEnum] or {};
local currentSkinData = EquippedCache[wName];
if currentSkinData.Skin then
r1[weaponEnum][repClass:ToEnum("Skin")] = currentSkinData.Skin;
end
if currentSkinData.Wrap then
r1[weaponEnum][repClass:ToEnum("Wrap")] = currentSkinData.Wrap;
end
if currentSkinData.Charm then
r1[weaponEnum][repClass:ToEnum("Charm")] = currentSkinData.Charm;
end
end);
end
local wrapInstance = originalNewWrap(r1, r2);
if ((owner == LocalPlayer) and EquippedCache[wName] and EquippedCache[wName].Wrap and wrapInstance._UpdateWrap) then
task.spawn(function()
wrapInstance:_UpdateWrap();
task.wait(0.1);
if not wrapInstance._destroyed then
wrapInstance:_UpdateWrap();
end
end);
end
return wrapInstance;
end;
end
end);
-- 유저 프로필 조회 시 스킨 동기화 깨짐 현상 교정 후킹 루틴
pcall(function()
local viewProfilePage = require(PlayerModules.Modules.Pages.ViewProfile);
if (viewProfilePage and viewProfilePage.Fetch) then
local originalFetch = viewProfilePage.Fetch;
viewProfilePage.Fetch = function(self, targetPlayer)
LastViewedPlayer = targetPlayer;
return originalFetch(self, targetPlayer);
end;
end
end);
-- [시각화 변조 3] 매치 종료 시 발동하는 킬 피니셔(Finisher) 이펙트 강제 변조 및 가로채기 후킹 루틴
pcall(function()
local clientEntity = require(PlayerModules.Modules.ClientReplicatedClasses.ClientEntity);
if clientEntity.ReplicateFromServer then
local originalReplicate = clientEntity.ReplicateFromServer;
clientEntity.ReplicateFromServer = function(self, actionName, ...)
if (actionName == "PlayFinisher") then
local args = {...};
local targetVictim = args[3];
local parsedName = targetVictim;
if ((type(targetVictim) == "userdata") and EnumBuilder and EnumBuilder.FromEnum) then
pcall(function() parsedName = EnumBuilder:FromEnum(targetVictim); end);
end
local isLocalAction = (tostring(parsedName) == LocalPlayer.Name) or (tostring(parsedName):lower() == LocalPlayer.Name:lower());
if (isLocalAction and CurrentEquippedWeaponName and EquippedCache[CurrentEquippedWeaponName] and EquippedCache[CurrentEquippedWeaponName].Finisher) then
local customFinisher = EquippedCache[CurrentEquippedWeaponName].Finisher;
local finisherEnum = customFinisher.Enum;
if (not finisherEnum and EnumBuilder) then
pcall(function() finisherEnum = EnumBuilder:ToEnum(customFinisher.Name); end);
end
if finisherEnum then
args[1] = finisherEnum;
return originalReplicate(self, actionName, unpack(args));
end
end
end
return originalReplicate(self, actionName, ...);
end;
end
end);
end);
loadConfig();
print("✓ Skinchanger loaded!");
To embed this project on your website, copy the following code and paste it into your website's HTML: