· 11 months ago · Nov 10, 2024, 06:26 PM
1--[[
2 SimpleSpy v2.2 SOURCE
3
4 SimpleSpy is a lightweight penetration testing tool that logs remote calls.
5
6 Credits:
7 exx - basically everything
8 Frosty - GUI to Lua
9]]
10
11--[[
12
13 Modification Made By - REDz
14
15]]
16setreadonly = function(gfc,gfd)
17pcall(function()
18print("pluh: ",gfc,gfd)
19end)
20end
21-- shuts down the previous instance of SimpleSpy
22if _G.SimpleSpyExecuted and type(_G.SimpleSpyShutdown) == "function" then
23 print(pcall(_G.SimpleSpyShutdown))
24end
25
26local Players = game:GetService("Players")
27local CoreGui = game:GetService("CoreGui")
28local Highlight =
29 loadstring(
30 game:HttpGet("https://github.com/exxtremestuffs/SimpleSpySource/raw/master/highlight.lua")
31 )()
32
33---- GENERATED (kinda sorta mostly) BY GUI to LUA ----
34
35-- Instances:
36
37local SimpleSpy2 = Instance.new("ScreenGui")
38local Background = Instance.new("Frame")
39local LeftPanel = Instance.new("Frame")
40local LogList = Instance.new("ScrollingFrame")
41local UIListLayout = Instance.new("UIListLayout")
42local RemoteTemplate = Instance.new("Frame")
43local ColorBar = Instance.new("Frame")
44local Text = Instance.new("TextLabel")
45local Button = Instance.new("TextButton")
46local RightPanel = Instance.new("Frame")
47local CodeBox = Instance.new("Frame")
48local ScrollingFrame = Instance.new("ScrollingFrame")
49local UIGridLayout = Instance.new("UIGridLayout")
50local FunctionTemplate = Instance.new("Frame")
51local ColorBar_2 = Instance.new("Frame")
52local Text_2 = Instance.new("TextLabel")
53local Button_2 = Instance.new("TextButton")
54local TopBar = Instance.new("Frame")
55local Simple = Instance.new("TextButton")
56local CloseButton = Instance.new("TextButton")
57local ImageLabel = Instance.new("ImageLabel")
58local MaximizeButton = Instance.new("TextButton")
59local ImageLabel_2 = Instance.new("ImageLabel")
60local MinimizeButton = Instance.new("TextButton")
61local ImageLabel_3 = Instance.new("ImageLabel")
62local ToolTip = Instance.new("Frame")
63local TextLabel = Instance.new("TextLabel")
64local gui = Instance.new("ScreenGui",Background)
65local nextb = Instance.new("ImageButton", gui)
66local gui = Instance.new("UICorner", nextb)
67
68--Properties:
69
70SimpleSpy2.Name = "SimpleSpy2"
71SimpleSpy2.ResetOnSpawn = false
72
73local SpyFind = CoreGui:FindFirstChild(SimpleSpy2.Name)
74
75if SpyFind and SpyFind ~= SimpleSpy2 then
76 SpyFind:Destroy()
77end
78
79Background.Name = "Background"
80Background.Parent = SimpleSpy2
81Background.BackgroundColor3 = Color3.new(1, 1, 1)
82Background.BackgroundTransparency = 1
83Background.Position = UDim2.new(0, 160, 0, 100)
84Background.Size = UDim2.new(0, 450, 0, 268)
85Background.Active = true
86Background.Draggable = true
87
88nextb.Position = UDim2.new(0,100,0,60)
89nextb.Size = UDim2.new(0,40,0,40)
90nextb.BackgroundColor3 = Color3.fromRGB(53, 52, 55)
91nextb.Image = "rbxassetid://7072720870"
92nextb.Active = true
93nextb.Draggable = true
94nextb.MouseButton1Down:connect(function()
95nextb.Image = (Background.Visible and "rbxassetid://7072720870") or "rbxassetid://7072719338"
96Background.Visible = not Background.Visible
97end)
98
99LeftPanel.Name = "LeftPanel"
100LeftPanel.Parent = Background
101LeftPanel.BackgroundColor3 = Color3.fromRGB(53, 52, 55)
102LeftPanel.BorderSizePixel = 0
103LeftPanel.Position = UDim2.new(0, 0, 0, 19)
104LeftPanel.Size = UDim2.new(0, 131, 0, 249)
105
106LogList.Name = "LogList"
107LogList.Parent = LeftPanel
108LogList.Active = true
109LogList.BackgroundColor3 = Color3.new(1, 1, 1)
110LogList.BackgroundTransparency = 1
111LogList.BorderSizePixel = 0
112LogList.Position = UDim2.new(0, 0, 0, 9)
113LogList.Size = UDim2.new(0, 131, 0, 232)
114LogList.CanvasSize = UDim2.new(0, 0, 0, 0)
115LogList.ScrollBarThickness = 4
116
117UIListLayout.Parent = LogList
118UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
119UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
120
121RemoteTemplate.Name = "RemoteTemplate"
122RemoteTemplate.Parent = LogList
123RemoteTemplate.BackgroundColor3 = Color3.new(1, 1, 1)
124RemoteTemplate.BackgroundTransparency = 1
125RemoteTemplate.Size = UDim2.new(0, 117, 0, 27)
126
127ColorBar.Name = "ColorBar"
128ColorBar.Parent = RemoteTemplate
129ColorBar.BackgroundColor3 = Color3.fromRGB(255, 242, 0)
130ColorBar.BorderSizePixel = 0
131ColorBar.Position = UDim2.new(0, 0, 0, 1)
132ColorBar.Size = UDim2.new(0, 7, 0, 18)
133ColorBar.ZIndex = 2
134
135Text.Name = "Text"
136Text.Parent = RemoteTemplate
137Text.BackgroundColor3 = Color3.new(1, 1, 1)
138Text.BackgroundTransparency = 1
139Text.Position = UDim2.new(0, 12, 0, 1)
140Text.Size = UDim2.new(0, 105, 0, 18)
141Text.ZIndex = 2
142Text.Font = Enum.Font.SourceSans
143Text.Text = "TEXT"
144Text.TextColor3 = Color3.new(1, 1, 1)
145Text.TextSize = 14
146Text.TextXAlignment = Enum.TextXAlignment.Left
147Text.TextWrapped = true
148
149Button.Name = "Button"
150Button.Parent = RemoteTemplate
151Button.BackgroundColor3 = Color3.new(0, 0, 0)
152Button.BackgroundTransparency = 0.75
153Button.BorderColor3 = Color3.new(1, 1, 1)
154Button.Position = UDim2.new(0, 0, 0, 1)
155Button.Size = UDim2.new(0, 117, 0, 18)
156Button.AutoButtonColor = false
157Button.Font = Enum.Font.SourceSans
158Button.Text = ""
159Button.TextColor3 = Color3.new(0, 0, 0)
160Button.TextSize = 14
161
162RightPanel.Name = "RightPanel"
163RightPanel.Parent = Background
164RightPanel.BackgroundColor3 = Color3.fromRGB(37, 36, 38)
165RightPanel.BorderSizePixel = 0
166RightPanel.Position = UDim2.new(0, 131, 0, 19)
167RightPanel.Size = UDim2.new(0, 319, 0, 249)
168
169CodeBox.Name = "CodeBox"
170CodeBox.Parent = RightPanel
171CodeBox.BackgroundColor3 = Color3.new(0.0823529, 0.0745098, 0.0784314)
172CodeBox.BorderSizePixel = 0
173CodeBox.Size = UDim2.new(0, 319, 0, 119)
174
175ScrollingFrame.Parent = RightPanel
176ScrollingFrame.Active = true
177ScrollingFrame.BackgroundColor3 = Color3.new(1, 1, 1)
178ScrollingFrame.BackgroundTransparency = 1
179ScrollingFrame.Position = UDim2.new(0, 0, 0.5, 0)
180ScrollingFrame.Size = UDim2.new(1, 0, 0.5, -9)
181ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
182ScrollingFrame.ScrollBarThickness = 4
183
184UIGridLayout.Parent = ScrollingFrame
185UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
186UIGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
187UIGridLayout.CellPadding = UDim2.new(0, 0, 0, 0)
188UIGridLayout.CellSize = UDim2.new(0, 94, 0, 27)
189
190FunctionTemplate.Name = "FunctionTemplate"
191FunctionTemplate.Parent = ScrollingFrame
192FunctionTemplate.BackgroundColor3 = Color3.new(1, 1, 1)
193FunctionTemplate.BackgroundTransparency = 1
194FunctionTemplate.Size = UDim2.new(0, 117, 0, 23)
195
196ColorBar_2.Name = "ColorBar"
197ColorBar_2.Parent = FunctionTemplate
198ColorBar_2.BackgroundColor3 = Color3.new(1, 1, 1)
199ColorBar_2.BorderSizePixel = 0
200ColorBar_2.Position = UDim2.new(0, 7, 0, 10)
201ColorBar_2.Size = UDim2.new(0, 7, 0, 18)
202ColorBar_2.ZIndex = 3
203
204Text_2.Name = "Text"
205Text_2.Parent = FunctionTemplate
206Text_2.BackgroundColor3 = Color3.new(1, 1, 1)
207Text_2.BackgroundTransparency = 1
208Text_2.Position = UDim2.new(0, 19, 0, 10)
209Text_2.Size = UDim2.new(0, 69, 0, 18)
210Text_2.ZIndex = 2
211Text_2.Font = Enum.Font.SourceSans
212Text_2.Text = "TEXT"
213Text_2.TextColor3 = Color3.new(1, 1, 1)
214Text_2.TextSize = 14
215Text_2.TextStrokeColor3 = Color3.new(0.145098, 0.141176, 0.14902)
216Text_2.TextXAlignment = Enum.TextXAlignment.Left
217Text_2.TextWrapped = true
218
219Button_2.Name = "Button"
220Button_2.Parent = FunctionTemplate
221Button_2.BackgroundColor3 = Color3.new(0, 0, 0)
222Button_2.BackgroundTransparency = 0.69999998807907
223Button_2.BorderColor3 = Color3.new(1, 1, 1)
224Button_2.Position = UDim2.new(0, 7, 0, 10)
225Button_2.Size = UDim2.new(0, 80, 0, 18)
226Button_2.AutoButtonColor = false
227Button_2.Font = Enum.Font.SourceSans
228Button_2.Text = ""
229Button_2.TextColor3 = Color3.new(0, 0, 0)
230Button_2.TextSize = 14
231
232TopBar.Name = "TopBar"
233TopBar.Parent = Background
234TopBar.BackgroundColor3 = Color3.fromRGB(37, 35, 38)
235TopBar.BorderSizePixel = 0
236TopBar.Size = UDim2.new(0, 450, 0, 19)
237
238Simple.Name = "Simple"
239Simple.Parent = TopBar
240Simple.BackgroundColor3 = Color3.new(1, 1, 1)
241Simple.AutoButtonColor = false
242Simple.BackgroundTransparency = 1
243Simple.Position = UDim2.new(0, 5, 0, 0)
244Simple.Size = UDim2.new(0, 57, 0, 18)
245Simple.Font = Enum.Font.SourceSansBold
246Simple.Text = "SimpleSpy For Mobile"
247Simple.TextColor3 = Color3.new(0, 0, 1)
248Simple.TextSize = 14
249Simple.TextXAlignment = Enum.TextXAlignment.Left
250
251CloseButton.Name = "CloseButton"
252CloseButton.Parent = TopBar
253CloseButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
254CloseButton.BorderSizePixel = 0
255CloseButton.Position = UDim2.new(1, -19, 0, 0)
256CloseButton.Size = UDim2.new(0, 19, 0, 19)
257CloseButton.Font = Enum.Font.SourceSans
258CloseButton.Text = ""
259CloseButton.TextColor3 = Color3.new(0, 0, 0)
260CloseButton.TextSize = 14
261
262ImageLabel.Parent = CloseButton
263ImageLabel.BackgroundColor3 = Color3.new(1, 1, 1)
264ImageLabel.BackgroundTransparency = 1
265ImageLabel.Position = UDim2.new(0, 5, 0, 5)
266ImageLabel.Size = UDim2.new(0, 9, 0, 9)
267ImageLabel.Image = "http://www.roblox.com/asset/?id=5597086202"
268
269MaximizeButton.Name = "MaximizeButton"
270MaximizeButton.Parent = TopBar
271MaximizeButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
272MaximizeButton.BorderSizePixel = 0
273MaximizeButton.Position = UDim2.new(1, -38, 0, 0)
274MaximizeButton.Size = UDim2.new(0, 19, 0, 19)
275MaximizeButton.Font = Enum.Font.SourceSans
276MaximizeButton.Text = ""
277MaximizeButton.TextColor3 = Color3.new(0, 0, 0)
278MaximizeButton.TextSize = 14
279
280ImageLabel_2.Parent = MaximizeButton
281ImageLabel_2.BackgroundColor3 = Color3.new(1, 1, 1)
282ImageLabel_2.BackgroundTransparency = 1
283ImageLabel_2.Position = UDim2.new(0, 5, 0, 5)
284ImageLabel_2.Size = UDim2.new(0, 9, 0, 9)
285ImageLabel_2.Image = "http://www.roblox.com/asset/?id=5597108117"
286
287MinimizeButton.Name = "MinimizeButton"
288MinimizeButton.Parent = TopBar
289MinimizeButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
290MinimizeButton.BorderSizePixel = 0
291MinimizeButton.Position = UDim2.new(1, -57, 0, 0)
292MinimizeButton.Size = UDim2.new(0, 19, 0, 19)
293MinimizeButton.Font = Enum.Font.SourceSans
294MinimizeButton.Text = ""
295MinimizeButton.TextColor3 = Color3.new(0, 0, 0)
296MinimizeButton.TextSize = 14
297
298ImageLabel_3.Parent = MinimizeButton
299ImageLabel_3.BackgroundColor3 = Color3.new(1, 1, 1)
300ImageLabel_3.BackgroundTransparency = 1
301ImageLabel_3.Position = UDim2.new(0, 5, 0, 5)
302ImageLabel_3.Size = UDim2.new(0, 9, 0, 9)
303ImageLabel_3.Image = "http://www.roblox.com/asset/?id=5597105827"
304
305ToolTip.Name = "ToolTip"
306ToolTip.Parent = SimpleSpy2
307ToolTip.BackgroundColor3 = Color3.fromRGB(26, 26, 26)
308ToolTip.BackgroundTransparency = 0.1
309ToolTip.BorderColor3 = Color3.new(1, 1, 1)
310ToolTip.Size = UDim2.new(0, 200, 0, 50)
311ToolTip.ZIndex = 3
312ToolTip.Visible = false
313
314TextLabel.Parent = ToolTip
315TextLabel.BackgroundColor3 = Color3.new(1, 1, 1)
316TextLabel.BackgroundTransparency = 1
317TextLabel.Position = UDim2.new(0, 2, 0, 2)
318TextLabel.Size = UDim2.new(0, 196, 0, 46)
319TextLabel.ZIndex = 3
320TextLabel.Font = Enum.Font.SourceSans
321TextLabel.Text = "This is some slightly longer text."
322TextLabel.TextColor3 = Color3.new(1, 1, 1)
323TextLabel.TextSize = 14
324TextLabel.TextWrapped = true
325TextLabel.TextXAlignment = Enum.TextXAlignment.Left
326TextLabel.TextYAlignment = Enum.TextYAlignment.Top
327
328-------------------------------------------------------------------------------
329-- init
330local RunService = game:GetService("RunService")
331local UserInputService = game:GetService("UserInputService")
332local TweenService = game:GetService("TweenService")
333local ContentProvider = game:GetService("ContentProvider")
334local TextService = game:GetService("TextService")
335local Mouse
336
337local selectedColor = Color3.new(0.321569, 0.333333, 1)
338local deselectedColor = Color3.new(0.8, 0.8, 0.8)
339--- So things are descending
340local layoutOrderNum = 999999999
341--- Whether or not the gui is closing
342local mainClosing = false
343--- Whether or not the gui is closed (defaults to false)
344local closed = false
345--- Whether or not the sidebar is closing
346local sideClosing = false
347--- Whether or not the sidebar is closed (defaults to true but opens automatically on remote selection)
348local sideClosed = false
349--- Whether or not the code box is maximized (defaults to false)
350local maximized = false
351--- The event logs to be read from
352local logs = {}
353--- The event currently selected.Log (defaults to nil)
354local selected = nil
355--- The blacklist (can be a string name or the Remote Instance)
356local blacklist = {}
357--- The block list (can be a string name or the Remote Instance)
358local blocklist = {}
359--- Whether or not to add getNil function
360local getNil = false
361--- Array of remotes (and original functions) connected to
362local connectedRemotes = {}
363--- True = hookfunction, false = namecall
364local toggle = false
365local gm
366local original
367--- used to prevent recursives
368local prevTables = {}
369--- holds logs (for deletion)
370local remoteLogs = {}
371--- used for hookfunction
372local remoteEvent = Instance.new("RemoteEvent")
373--- used for hookfunction
374local remoteFunction = Instance.new("RemoteFunction")
375local originalEvent = remoteEvent.FireServer
376local originalFunction = remoteFunction.InvokeServer
377--- the maximum amount of remotes allowed in logs
378_G.SIMPLESPYCONFIG_MaxRemotes = 500
379--- how many spaces to indent
380local indent = 4
381--- used for task scheduler
382local scheduled = {}
383--- RBXScriptConnect of the task scheduler
384local schedulerconnect
385local SimpleSpy = {}
386local topstr = ""
387local bottomstr = ""
388local remotesFadeIn
389local rightFadeIn
390local codebox
391local p
392local getnilrequired = false
393
394-- autoblock variables
395local autoblock = false
396local history = {}
397local excluding = {}
398
399-- function info variables
400local funcEnabled = true
401
402-- remote hooking/connecting api variables
403local remoteSignals = {}
404local remoteHooks = {}
405
406-- original mouse icon
407local oldIcon
408
409-- if mouse inside gui
410local mouseInGui = false
411
412-- handy array of RBXScriptConnections to disconnect on shutdown
413local connections = {}
414
415-- whether or not SimpleSpy uses 'getcallingscript()' to get the script (default is false because detection)
416local useGetCallingScript = false
417
418--- used to enable/disable SimpleSpy's keyToString for remotes
419local keyToString = false
420
421-- determines whether return values are recorded
422local recordReturnValues = false
423
424-- functions
425
426--- Converts arguments to a string and generates code that calls the specified method with them, recommended to be used in conjunction with ValueToString (method must be a string, e.g. `game:GetService("ReplicatedStorage").Remote.remote:FireServer`)
427--- @param method string
428--- @param args any[]
429--- @return string
430function SimpleSpy:ArgsToString(method, args)
431 assert(typeof(method) == "string", "string expected, got " .. typeof(method))
432 assert(typeof(args) == "table", "table expected, got " .. typeof(args))
433 return v2v({ args = args }) .. "\n\n" .. method .. "(unpack(args))"
434end
435
436--- Converts a value to variables with the specified index as the variable name (if nil/invalid then the name will be assigned automatically)
437--- @param t any[]
438--- @return string
439function SimpleSpy:TableToVars(t)
440 assert(typeof(t) == "table", "table expected, got " .. typeof(t))
441 return v2v(t)
442end
443
444--- Converts a value to a variable with the specified `variablename` (if nil/invalid then the name will be assigned automatically)
445--- @param value any
446--- @return string
447function SimpleSpy:ValueToVar(value, variablename)
448 assert(variablename == nil or typeof(variablename) == "string", "string expected, got " .. typeof(variablename))
449 if not variablename then
450 variablename = 1
451 end
452 return v2v({ [variablename] = value })
453end
454
455--- Converts any value to a string, cannot preserve function contents
456--- @param value any
457--- @return string
458function SimpleSpy:ValueToString(value)
459 return v2s(value)
460end
461
462--- Generates the simplespy function info
463--- @param func function
464--- @return string
465function SimpleSpy:GetFunctionInfo(func)
466 assert(typeof(func) == "function", "Instance expected, got " .. typeof(func))
467 warn("Function info currently unavailable due to crashing in Synapse X")
468 return v2v({ functionInfo = {
469 info = debug.getinfo(func),
470 constants = debug.getconstants(func),
471 } })
472end
473
474--- Gets the ScriptSignal for a specified remote being fired
475--- @param remote Instance
476function SimpleSpy:GetRemoteFiredSignal(remote)
477 assert(typeof(remote) == "Instance", "Instance expected, got " .. typeof(remote))
478 if not remoteSignals[remote] then
479 remoteSignals[remote] = newSignal()
480 end
481 return remoteSignals[remote]
482end
483
484--- Allows for direct hooking of remotes **THIS CAN BE VERY DANGEROUS**
485--- @param remote Instance
486--- @param f function
487function SimpleSpy:HookRemote(remote, f)
488 assert(typeof(remote) == "Instance", "Instance expected, got " .. typeof(remote))
489 assert(typeof(f) == "function", "function expected, got " .. typeof(f))
490 remoteHooks[remote] = f
491end
492
493--- Blocks the specified remote instance/string
494--- @param remote any
495function SimpleSpy:BlockRemote(remote)
496 assert(
497 typeof(remote) == "Instance" or typeof(remote) == "string",
498 "Instance | string expected, got " .. typeof(remote)
499 )
500 blocklist[remote] = true
501end
502
503--- Excludes the specified remote from logs (instance/string)
504--- @param remote any
505function SimpleSpy:ExcludeRemote(remote)
506 assert(
507 typeof(remote) == "Instance" or typeof(remote) == "string",
508 "Instance | string expected, got " .. typeof(remote)
509 )
510 blacklist[remote] = true
511end
512
513--- Creates a new ScriptSignal that can be connected to and fired
514--- @return table
515function newSignal()
516 local connected = {}
517 return {
518 Connect = function(self, f)
519 assert(connected, "Signal is closed")
520 connected[tostring(f)] = f
521 return {
522 Connected = true,
523 Disconnect = function(self)
524 if not connected then
525 warn("Signal is already closed")
526 end
527 self.Connected = false
528 connected[tostring(f)] = nil
529 end,
530 }
531 end,
532 Wait = function(self)
533 local thread = coroutine.running()
534 local connection
535 connection = self:Connect(function()
536 connection:Disconnect()
537 if coroutine.status(thread) == "suspended" then
538 coroutine.resume(thread)
539 end
540 end)
541 coroutine.yield()
542 end,
543 Fire = function(self, ...)
544 for _, f in pairs(connected) do
545 coroutine.wrap(f)(...)
546 end
547 end,
548 }
549end
550
551--- Prevents remote spam from causing lag (clears logs after `_G.SIMPLESPYCONFIG_MaxRemotes` or 500 remotes)
552function clean()
553 local max = _G.SIMPLESPYCONFIG_MaxRemotes
554 if not typeof(max) == "number" and math.floor(max) ~= max then
555 max = 500
556 end
557 if #remoteLogs > max then
558 for i = 100, #remoteLogs do
559 local v = remoteLogs[i]
560 if typeof(v[1]) == "RBXScriptConnection" then
561 v[1]:Disconnect()
562 end
563 if typeof(v[2]) == "Instance" then
564 v[2]:Destroy()
565 end
566 end
567 local newLogs = {}
568 for i = 1, 100 do
569 table.insert(newLogs, remoteLogs[i])
570 end
571 remoteLogs = newLogs
572 end
573end
574
575--- Scales the ToolTip to fit containing text
576function scaleToolTip()
577 local size = TextService:GetTextSize(
578 TextLabel.Text,
579 TextLabel.TextSize,
580 TextLabel.Font,
581 Vector2.new(196, math.huge)
582 )
583 TextLabel.Size = UDim2.new(0, size.X, 0, size.Y)
584 ToolTip.Size = UDim2.new(0, size.X + 4, 0, size.Y + 4)
585end
586
587--- Executed when the toggle button (the SimpleSpy logo) is hovered over
588function onToggleButtonHover()
589 if not toggle then
590 TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(252, 51, 51) }):Play()
591 else
592 TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(68, 206, 91) }):Play()
593 end
594end
595
596--- Executed when the toggle button is unhovered over
597function onToggleButtonUnhover()
598 TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(255, 255, 255) }):Play()
599end
600
601--- Executed when the X button is hovered over
602function onXButtonHover()
603 TweenService:Create(CloseButton, TweenInfo.new(0.2), { BackgroundColor3 = Color3.fromRGB(255, 60, 60) }):Play()
604end
605
606--- Executed when the X button is unhovered over
607function onXButtonUnhover()
608 TweenService:Create(CloseButton, TweenInfo.new(0.2), { BackgroundColor3 = Color3.fromRGB(37, 36, 38) }):Play()
609end
610
611--- Toggles the remote spy method (when button clicked)
612function onToggleButtonClick()
613 if toggle then
614 TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(252, 51, 51) }):Play()
615 else
616 TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(68, 206, 91) }):Play()
617 end
618 toggleSpyMethod()
619end
620
621--- Reconnects bringBackOnResize if the current viewport changes and also connects it initially
622function connectResize()
623 local lastCam = workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackOnResize)
624 workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
625 lastCam:Disconnect()
626 if workspace.CurrentCamera then
627 lastCam = workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackOnResize)
628 end
629 end)
630end
631
632--- Brings gui back if it gets lost offscreen (connected to the camera viewport changing)
633function bringBackOnResize()
634 validateSize()
635 if sideClosed then
636 minimizeSize()
637 else
638 maximizeSize()
639 end
640 local currentX = Background.AbsolutePosition.X
641 local currentY = Background.AbsolutePosition.Y
642 local viewportSize = workspace.CurrentCamera.ViewportSize
643 if (currentX < 0) or (currentX > (viewportSize.X - (sideClosed and 131 or Background.AbsoluteSize.X))) then
644 if currentX < 0 then
645 currentX = 0
646 else
647 currentX = viewportSize.X - (sideClosed and 131 or Background.AbsoluteSize.X)
648 end
649 end
650 if (currentY < 0) or (currentY > (viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36)) then
651 if currentY < 0 then
652 currentY = 0
653 else
654 currentY = viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36
655 end
656 end
657 TweenService.Create(
658 TweenService,
659 Background,
660 TweenInfo.new(0.1),
661 { Position = UDim2.new(0, currentX, 0, currentY) }
662 ):Play()
663end
664
665--- Drags gui (so long as mouse is held down)
666--- @param input InputObject
667function onBarInput(input)
668 if input.UserInputType == Enum.UserInputType.MouseButton1 then
669 local lastPos = UserInputService.GetMouseLocation(UserInputService)
670 local mainPos = Background.AbsolutePosition
671 local offset = mainPos - lastPos
672 local currentPos = offset + lastPos
673 RunService.BindToRenderStep(RunService, "drag", 1, function()
674 local newPos = UserInputService.GetMouseLocation(UserInputService)
675 if newPos ~= lastPos then
676 local currentX = (offset + newPos).X
677 local currentY = (offset + newPos).Y
678 local viewportSize = workspace.CurrentCamera.ViewportSize
679 if
680 (currentX < 0 and currentX < currentPos.X)
681 or (
682 currentX > (viewportSize.X - (sideClosed and 131 or TopBar.AbsoluteSize.X))
683 and currentX > currentPos.X
684 )
685 then
686 if currentX < 0 then
687 currentX = 0
688 else
689 currentX = viewportSize.X - (sideClosed and 131 or TopBar.AbsoluteSize.X)
690 end
691 end
692 if
693 (currentY < 0 and currentY < currentPos.Y)
694 or (
695 currentY > (viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36)
696 and currentY > currentPos.Y
697 )
698 then
699 if currentY < 0 then
700 currentY = 0
701 else
702 currentY = viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36
703 end
704 end
705 currentPos = Vector2.new(currentX, currentY)
706 lastPos = newPos
707 TweenService.Create(
708 TweenService,
709 Background,
710 TweenInfo.new(0.1),
711 { Position = UDim2.new(0, currentPos.X, 0, currentPos.Y) }
712 ):Play()
713 end
714 -- if input.UserInputState ~= Enum.UserInputState.Begin then
715 -- RunService.UnbindFromRenderStep(RunService, "drag")
716 -- end
717 end)
718 table.insert(
719 connections,
720 UserInputService.InputEnded:Connect(function(inputE)
721 if input == inputE then
722 RunService:UnbindFromRenderStep("drag")
723 end
724 end)
725 )
726 end
727end
728
729--- Fades out the table of elements (and makes them invisible), returns a function to make them visible again
730function fadeOut(elements)
731 local data = {}
732 for _, v in pairs(elements) do
733 if typeof(v) == "Instance" and v:IsA("GuiObject") and v.Visible then
734 coroutine.wrap(function()
735 data[v] = {
736 BackgroundTransparency = v.BackgroundTransparency,
737 }
738 TweenService:Create(v, TweenInfo.new(0.5), { BackgroundTransparency = 1 }):Play()
739 if v:IsA("TextBox") or v:IsA("TextButton") or v:IsA("TextLabel") then
740 data[v].TextTransparency = v.TextTransparency
741 TweenService:Create(v, TweenInfo.new(0.5), { TextTransparency = 1 }):Play()
742 elseif v:IsA("ImageButton") or v:IsA("ImageLabel") then
743 data[v].ImageTransparency = v.ImageTransparency
744 TweenService:Create(v, TweenInfo.new(0.5), { ImageTransparency = 1 }):Play()
745 end
746 wait(0.5)
747 v.Visible = false
748 for i, x in pairs(data[v]) do
749 v[i] = x
750 end
751 data[v] = true
752 end)()
753 end
754 end
755 return function()
756 for i, _ in pairs(data) do
757 coroutine.wrap(function()
758 local properties = {
759 BackgroundTransparency = i.BackgroundTransparency,
760 }
761 i.BackgroundTransparency = 1
762 TweenService
763 :Create(i, TweenInfo.new(0.5), { BackgroundTransparency = properties.BackgroundTransparency })
764 :Play()
765 if i:IsA("TextBox") or i:IsA("TextButton") or i:IsA("TextLabel") then
766 properties.TextTransparency = i.TextTransparency
767 i.TextTransparency = 1
768 TweenService
769 :Create(i, TweenInfo.new(0.5), { TextTransparency = properties.TextTransparency })
770 :Play()
771 elseif i:IsA("ImageButton") or i:IsA("ImageLabel") then
772 properties.ImageTransparency = i.ImageTransparency
773 i.ImageTransparency = 1
774 TweenService
775 :Create(i, TweenInfo.new(0.5), { ImageTransparency = properties.ImageTransparency })
776 :Play()
777 end
778 i.Visible = true
779 end)()
780 end
781 end
782end
783
784--- Expands and minimizes the gui (closed is the toggle boolean)
785function toggleMinimize(override)
786 if mainClosing and not override or maximized then
787 return
788 end
789 mainClosing = true
790 closed = not closed
791 if closed then
792 if not sideClosed then
793 toggleSideTray(true)
794 end
795 LeftPanel.Visible = true
796 TweenService:Create(LeftPanel, TweenInfo.new(0.5), { Size = UDim2.new(0, 131, 0, 0) }):Play()
797 wait(0.5)
798 remotesFadeIn = fadeOut(LeftPanel:GetDescendants())
799 wait(0.5)
800 else
801 TweenService:Create(LeftPanel, TweenInfo.new(0.5), { Size = UDim2.new(0, 131, 0, 249) }):Play()
802 wait(0.5)
803 if remotesFadeIn then
804 remotesFadeIn()
805 remotesFadeIn = nil
806 end
807 bringBackOnResize()
808 end
809 mainClosing = false
810end
811
812--- Expands and minimizes the sidebar (sideClosed is the toggle boolean)
813function toggleSideTray(override)
814 if sideClosing and not override or maximized then
815 return
816 end
817 sideClosing = true
818 sideClosed = not sideClosed
819 if sideClosed then
820 rightFadeIn = fadeOut(RightPanel:GetDescendants())
821 wait(0.5)
822 minimizeSize(0.5)
823 wait(0.5)
824 RightPanel.Visible = false
825 else
826 if closed then
827 toggleMinimize(true)
828 end
829 RightPanel.Visible = true
830 maximizeSize(0.5)
831 wait(0.5)
832 if rightFadeIn then
833 rightFadeIn()
834 end
835 bringBackOnResize()
836 end
837 sideClosing = false
838end
839
840--- Expands code box to fit screen for more convenient viewing
841function toggleMaximize()
842 if not sideClosed and not maximized then
843 maximized = true
844 local disable = Instance.new("TextButton")
845 local prevSize = UDim2.new(0, CodeBox.AbsoluteSize.X, 0, CodeBox.AbsoluteSize.Y)
846 local prevPos = UDim2.new(0, CodeBox.AbsolutePosition.X, 0, CodeBox.AbsolutePosition.Y)
847 disable.Size = UDim2.new(1, 0, 1, 0)
848 disable.BackgroundColor3 = Color3.new()
849 disable.BorderSizePixel = 0
850 disable.Text = 0
851 disable.ZIndex = 3
852 disable.BackgroundTransparency = 1
853 disable.AutoButtonColor = false
854 CodeBox.ZIndex = 4
855 CodeBox.Position = prevPos
856 CodeBox.Size = prevSize
857 TweenService
858 :Create(
859 CodeBox,
860 TweenInfo.new(0.5),
861 { Size = UDim2.new(0.5, 0, 0.5, 0), Position = UDim2.new(0.25, 0, 0.25, 0) }
862 )
863 :Play()
864 TweenService:Create(disable, TweenInfo.new(0.5), { BackgroundTransparency = 0.5 }):Play()
865 disable.MouseButton1Click:Connect(function()
866 if
867 UserInputService:GetMouseLocation().Y + 36 >= CodeBox.AbsolutePosition.Y
868 and UserInputService:GetMouseLocation().Y + 36 <= CodeBox.AbsolutePosition.Y + CodeBox.AbsoluteSize.Y
869 and UserInputService:GetMouseLocation().X >= CodeBox.AbsolutePosition.X
870 and UserInputService:GetMouseLocation().X <= CodeBox.AbsolutePosition.X + CodeBox.AbsoluteSize.X
871 then
872 return
873 end
874 TweenService:Create(CodeBox, TweenInfo.new(0.5), { Size = prevSize, Position = prevPos }):Play()
875 TweenService:Create(disable, TweenInfo.new(0.5), { BackgroundTransparency = 1 }):Play()
876 maximized = false
877 wait(0.5)
878 disable:Destroy()
879 CodeBox.Size = UDim2.new(1, 0, 0.5, 0)
880 CodeBox.Position = UDim2.new(0, 0, 0, 0)
881 CodeBox.ZIndex = 0
882 end)
883 end
884end
885
886--- Adjusts the ui elements to the 'Maximized' size
887function maximizeSize(speed)
888 if not speed then
889 speed = 0.05
890 end
891 TweenService
892 :Create(
893 LeftPanel,
894 TweenInfo.new(speed),
895 { Size = UDim2.fromOffset(LeftPanel.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y) }
896 )
897 :Play()
898 TweenService
899 :Create(RightPanel, TweenInfo.new(speed), {
900 Size = UDim2.fromOffset(
901 Background.AbsoluteSize.X - LeftPanel.AbsoluteSize.X,
902 Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y
903 ),
904 })
905 :Play()
906 TweenService
907 :Create(
908 TopBar,
909 TweenInfo.new(speed),
910 { Size = UDim2.fromOffset(Background.AbsoluteSize.X, TopBar.AbsoluteSize.Y) }
911 )
912 :Play()
913 TweenService
914 :Create(ScrollingFrame, TweenInfo.new(speed), {
915 Size = UDim2.fromOffset(Background.AbsoluteSize.X - LeftPanel.AbsoluteSize.X, 110),
916 Position = UDim2.fromOffset(0, Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y),
917 })
918 :Play()
919 TweenService
920 :Create(CodeBox, TweenInfo.new(speed), {
921 Size = UDim2.fromOffset(
922 Background.AbsoluteSize.X - LeftPanel.AbsoluteSize.X,
923 Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y
924 ),
925 })
926 :Play()
927 TweenService
928 :Create(
929 LogList,
930 TweenInfo.new(speed),
931 { Size = UDim2.fromOffset(LogList.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y - 18) }
932 )
933 :Play()
934end
935
936--- Adjusts the ui elements to close the side
937function minimizeSize(speed)
938 if not speed then
939 speed = 0.05
940 end
941 TweenService
942 :Create(
943 LeftPanel,
944 TweenInfo.new(speed),
945 { Size = UDim2.fromOffset(LeftPanel.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y) }
946 )
947 :Play()
948 TweenService
949 :Create(
950 RightPanel,
951 TweenInfo.new(speed),
952 { Size = UDim2.fromOffset(0, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y) }
953 )
954 :Play()
955 TweenService
956 :Create(
957 TopBar,
958 TweenInfo.new(speed),
959 { Size = UDim2.fromOffset(LeftPanel.AbsoluteSize.X, TopBar.AbsoluteSize.Y) }
960 )
961 :Play()
962 TweenService
963 :Create(ScrollingFrame, TweenInfo.new(speed), {
964 Size = UDim2.fromOffset(0, 119),
965 Position = UDim2.fromOffset(0, Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y),
966 })
967 :Play()
968 TweenService
969 :Create(
970 CodeBox,
971 TweenInfo.new(speed),
972 { Size = UDim2.fromOffset(0, Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y) }
973 )
974 :Play()
975 TweenService
976 :Create(
977 LogList,
978 TweenInfo.new(speed),
979 { Size = UDim2.fromOffset(LogList.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y - 18) }
980 )
981 :Play()
982end
983
984--- Ensures size is within screensize limitations
985function validateSize()
986 local x, y = Background.AbsoluteSize.X, Background.AbsoluteSize.Y
987 local screenSize = workspace.CurrentCamera.ViewportSize
988 if x + Background.AbsolutePosition.X > screenSize.X then
989 if screenSize.X - Background.AbsolutePosition.X >= 450 then
990 x = screenSize.X - Background.AbsolutePosition.X
991 else
992 x = 450
993 end
994 elseif y + Background.AbsolutePosition.Y > screenSize.Y then
995 if screenSize.X - Background.AbsolutePosition.Y >= 268 then
996 y = screenSize.Y - Background.AbsolutePosition.Y
997 else
998 y = 268
999 end
1000 end
1001 Background.Size = UDim2.fromOffset(x, y)
1002end
1003
1004--- Gets the player an instance is descended from
1005function getPlayerFromInstance(instance)
1006 for _, v in pairs(Players:GetPlayers()) do
1007 if v.Character and (instance:IsDescendantOf(v.Character) or instance == v.Character) then
1008 return v
1009 end
1010 end
1011end
1012
1013--- Runs on MouseButton1Click of an event frame
1014function eventSelect(frame)
1015 if selected and selected.Log and selected.Log.Button then
1016 TweenService
1017 :Create(selected.Log.Button, TweenInfo.new(0.5), { BackgroundColor3 = Color3.fromRGB(0, 0, 0) })
1018 :Play()
1019 selected = nil
1020 end
1021 for _, v in pairs(logs) do
1022 if frame == v.Log then
1023 selected = v
1024 end
1025 end
1026 if selected and selected.Log then
1027 TweenService
1028 :Create(frame.Button, TweenInfo.new(0.5), { BackgroundColor3 = Color3.fromRGB(92, 126, 229) })
1029 :Play()
1030 codebox:setRaw(selected.GenScript)
1031 end
1032 if sideClosed then
1033 toggleSideTray()
1034 end
1035end
1036
1037--- Updates the canvas size to fit the current amount of function buttons
1038function updateFunctionCanvas()
1039 ScrollingFrame.CanvasSize = UDim2.fromOffset(UIGridLayout.AbsoluteContentSize.X, UIGridLayout.AbsoluteContentSize.Y)
1040end
1041
1042--- Updates the canvas size to fit the amount of current remotes
1043function updateRemoteCanvas()
1044 LogList.CanvasSize = UDim2.fromOffset(UIListLayout.AbsoluteContentSize.X, UIListLayout.AbsoluteContentSize.Y)
1045end
1046
1047--- Allows for toggling of the tooltip and easy setting of le description
1048--- @param enable boolean
1049--- @param text string
1050function makeToolTip(enable, text)
1051 if enable then
1052 if ToolTip.Visible then
1053 ToolTip.Visible = false
1054 RunService:UnbindFromRenderStep("ToolTip")
1055 end
1056 local first = true
1057 RunService:BindToRenderStep("ToolTip", 1, function()
1058 local topLeft = Vector2.new(Mouse.X + 20, Mouse.Y + 20)
1059 local bottomRight = topLeft + ToolTip.AbsoluteSize
1060 if topLeft.X < 0 then
1061 topLeft = Vector2.new(0, topLeft.Y)
1062 elseif bottomRight.X > workspace.CurrentCamera.ViewportSize.X then
1063 topLeft = Vector2.new(workspace.CurrentCamera.ViewportSize.X - ToolTip.AbsoluteSize.X, topLeft.Y)
1064 end
1065 if topLeft.Y < 0 then
1066 topLeft = Vector2.new(topLeft.X, 0)
1067 elseif bottomRight.Y > workspace.CurrentCamera.ViewportSize.Y - 35 then
1068 topLeft = Vector2.new(topLeft.X, workspace.CurrentCamera.ViewportSize.Y - ToolTip.AbsoluteSize.Y - 35)
1069 end
1070 if topLeft.X <= Mouse.X and topLeft.Y <= Mouse.Y then
1071 topLeft = Vector2.new(Mouse.X - ToolTip.AbsoluteSize.X - 2, Mouse.Y - ToolTip.AbsoluteSize.Y - 2)
1072 end
1073 if first then
1074 ToolTip.Position = UDim2.fromOffset(topLeft.X, topLeft.Y)
1075 first = false
1076 else
1077 ToolTip:TweenPosition(UDim2.fromOffset(topLeft.X, topLeft.Y), "Out", "Linear", 0.1)
1078 end
1079 end)
1080 TextLabel.Text = text
1081 ToolTip.Visible = true
1082 else
1083 if ToolTip.Visible then
1084 ToolTip.Visible = false
1085 RunService:UnbindFromRenderStep("ToolTip")
1086 end
1087 end
1088end
1089
1090--- Creates new function button (below codebox)
1091--- @param name string
1092---@param description function
1093---@param onClick function
1094function newButton(name, description, onClick)
1095 local button = FunctionTemplate:Clone()
1096 button.Text.Text = name
1097 button.Button.MouseEnter:Connect(function()
1098 makeToolTip(true, description())
1099 end)
1100 button.Button.MouseLeave:Connect(function()
1101 makeToolTip(false)
1102 end)
1103 button.AncestryChanged:Connect(function()
1104 makeToolTip(false)
1105 end)
1106 button.Button.MouseButton1Click:Connect(function(...)
1107 onClick(button, ...)
1108 end)
1109 button.Parent = ScrollingFrame
1110 updateFunctionCanvas()
1111end
1112
1113--- Adds new Remote to logs
1114--- @param name string The name of the remote being logged
1115--- @param type string The type of the remote being logged (either 'function' or 'event')
1116--- @param args any
1117--- @param remote any
1118--- @param function_info string
1119--- @param blocked any
1120function newRemote(type, name, args, remote, function_info, blocked, src, returnValue)
1121 local remoteFrame = RemoteTemplate:Clone()
1122 remoteFrame.Text.Text = string.sub(name, 1, 50)
1123 remoteFrame.ColorBar.BackgroundColor3 = type == "event" and Color3.new(255, 242, 0) or Color3.fromRGB(99, 86, 245)
1124 local id = Instance.new("IntValue")
1125 id.Name = "ID"
1126 id.Value = #logs + 1
1127 id.Parent = remoteFrame
1128 local weakRemoteTable = setmetatable({ remote = remote }, { __mode = "v" })
1129 local log = {
1130 Name = name,
1131 Function = function_info,
1132 Remote = weakRemoteTable,
1133 Log = remoteFrame,
1134 Blocked = blocked,
1135 Source = src,
1136 GenScript = "-- Generating, please wait... (click to reload)\n-- (If this message persists, the remote args are likely extremely long)",
1137 ReturnValue = returnValue,
1138 }
1139 logs[#logs + 1] = log
1140 schedule(function()
1141 log.GenScript = genScript(remote, args)
1142 if blocked then
1143 logs[#logs].GenScript = "-- THIS REMOTE WAS PREVENTED FROM FIRING THE SERVER BY SIMPLESPY\n\n"
1144 .. logs[#logs].GenScript
1145 end
1146 end)
1147 local connect = remoteFrame.Button.MouseButton1Click:Connect(function()
1148 eventSelect(remoteFrame)
1149 end)
1150 if layoutOrderNum < 1 then
1151 layoutOrderNum = 999999999
1152 end
1153 remoteFrame.LayoutOrder = layoutOrderNum
1154 layoutOrderNum = layoutOrderNum - 1
1155 remoteFrame.Parent = LogList
1156 table.insert(remoteLogs, 1, { connect, remoteFrame })
1157 clean()
1158 updateRemoteCanvas()
1159end
1160
1161--- Generates a script from the provided arguments (first has to be remote path)
1162function genScript(remote, args)
1163 prevTables = {}
1164 local gen = ""
1165 if #args > 0 then
1166 if not pcall(function()
1167 gen = v2v({ args = args }) .. "\n"
1168 end) then
1169 gen = gen
1170 .. "-- TableToString failure! Reverting to legacy functionality (results may vary)\nlocal args = {"
1171 if
1172 not pcall(function()
1173 for i, v in pairs(args) do
1174 if type(i) ~= "Instance" and type(i) ~= "userdata" then
1175 gen = gen .. "\n [object] = "
1176 elseif type(i) == "string" then
1177 gen = gen .. '\n ["' .. i .. '"] = '
1178 elseif type(i) == "userdata" and typeof(i) ~= "Instance" then
1179 gen = gen .. "\n [" .. string.format("nil --[[%s]]", typeof(v)) .. ")] = "
1180 elseif type(i) == "userdata" then
1181 gen = gen .. "\n [game." .. i:GetFullName() .. ")] = "
1182 end
1183 if type(v) ~= "Instance" and type(v) ~= "userdata" then
1184 gen = gen .. "object"
1185 elseif type(v) == "string" then
1186 gen = gen .. '"' .. v .. '"'
1187 elseif type(v) == "userdata" and typeof(v) ~= "Instance" then
1188 gen = gen .. string.format("nil --[[%s]]", typeof(v))
1189 elseif type(v) == "userdata" then
1190 gen = gen .. "game." .. v:GetFullName()
1191 end
1192 end
1193 gen = gen .. "\n}\n\n"
1194 end)
1195 then
1196 gen = gen .. "}\n-- Legacy tableToString failure! Unable to decompile."
1197 end
1198 end
1199 if not remote:IsDescendantOf(game) and not getnilrequired then
1200 gen = "function getNil(name,class) for _,v in pairs(getnilinstances())do if v.ClassName==class and v.Name==name then return v;end end end\n\n"
1201 .. gen
1202 end
1203 if remote:IsA("RemoteEvent") then
1204 gen = gen .. v2s(remote) .. ":FireServer(unpack(args))"
1205 elseif remote:IsA("RemoteFunction") then
1206 gen = gen .. v2s(remote) .. ":InvokeServer(unpack(args))"
1207 end
1208 else
1209 if remote:IsA("RemoteEvent") then
1210 gen = gen .. v2s(remote) .. ":FireServer()"
1211 elseif remote:IsA("RemoteFunction") then
1212 gen = gen .. v2s(remote) .. ":InvokeServer()"
1213 end
1214 end
1215 gen = gen
1216 prevTables = {}
1217 return gen
1218end
1219
1220--- value-to-string: value, string (out), level (indentation), parent table, var name, is from tovar
1221function v2s(v, l, p, n, vtv, i, pt, path, tables, tI)
1222 if not tI then
1223 tI = { 0 }
1224 else
1225 tI[1] += 1
1226 end
1227 if typeof(v) == "number" then
1228 if v == math.huge then
1229 return "math.huge"
1230 elseif tostring(v):match("nan") then
1231 return "0/0 --[[NaN]]"
1232 end
1233 return tostring(v)
1234 elseif typeof(v) == "boolean" then
1235 return tostring(v)
1236 elseif typeof(v) == "string" then
1237 return formatstr(v, l)
1238 elseif typeof(v) == "function" then
1239 return f2s(v)
1240 elseif typeof(v) == "table" then
1241 return t2s(v, l, p, n, vtv, i, pt, path, tables, tI)
1242 elseif typeof(v) == "Instance" then
1243 return i2p(v)
1244 elseif typeof(v) == "userdata" then
1245 return "newproxy(true)"
1246 elseif type(v) == "userdata" then
1247 return u2s(v)
1248 elseif type(v) == "vector" then
1249 return string.format("Vector3.new(%s, %s, %s)", v2s(v.X), v2s(v.Y), v2s(v.Z))
1250 else
1251 return "nil --[[" .. typeof(v) .. "]]"
1252 end
1253end
1254
1255--- value-to-variable
1256--- @param t any
1257function v2v(t)
1258 topstr = ""
1259 bottomstr = ""
1260 getnilrequired = false
1261 local ret = ""
1262 local count = 1
1263 for i, v in pairs(t) do
1264 if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
1265 ret = ret .. "local " .. i .. " = " .. v2s(v, nil, nil, i, true) .. "\n"
1266 elseif tostring(i):match("^[%a_]+[%w_]*$") then
1267 ret = ret
1268 .. "local "
1269 .. tostring(i):lower()
1270 .. "_"
1271 .. tostring(count)
1272 .. " = "
1273 .. v2s(v, nil, nil, tostring(i):lower() .. "_" .. tostring(count), true)
1274 .. "\n"
1275 else
1276 ret = ret
1277 .. "local "
1278 .. type(v)
1279 .. "_"
1280 .. tostring(count)
1281 .. " = "
1282 .. v2s(v, nil, nil, type(v) .. "_" .. tostring(count), true)
1283 .. "\n"
1284 end
1285 count = count + 1
1286 end
1287 if getnilrequired then
1288 topstr = "function getNil(name,class) for _,v in pairs(getnilinstances())do if v.ClassName==class and v.Name==name then return v;end end end\n"
1289 .. topstr
1290 end
1291 if #topstr > 0 then
1292 ret = topstr .. "\n" .. ret
1293 end
1294 if #bottomstr > 0 then
1295 ret = ret .. bottomstr
1296 end
1297 return ret
1298end
1299
1300--- table-to-string
1301--- @param t table
1302--- @param l number
1303--- @param p table
1304--- @param n string
1305--- @param vtv boolean
1306--- @param i any
1307--- @param pt table
1308--- @param path string
1309--- @param tables table
1310--- @param tI table
1311function t2s(t, l, p, n, vtv, i, pt, path, tables, tI)
1312 local globalIndex = table.find(getgenv(), t) -- checks if table is a global
1313 if type(globalIndex) == "string" then
1314 return globalIndex
1315 end
1316 if not tI then
1317 tI = { 0 }
1318 end
1319 if not path then -- sets path to empty string (so it doesn't have to manually provided every time)
1320 path = ""
1321 end
1322 if not l then -- sets the level to 0 (for indentation) and tables for logging tables it already serialized
1323 l = 0
1324 tables = {}
1325 end
1326 if not p then -- p is the previous table but doesn't really matter if it's the first
1327 p = t
1328 end
1329 for _, v in pairs(tables) do -- checks if the current table has been serialized before
1330 if n and rawequal(v, t) then
1331 bottomstr = bottomstr
1332 .. "\n"
1333 .. tostring(n)
1334 .. tostring(path)
1335 .. " = "
1336 .. tostring(n)
1337 .. tostring(({ v2p(v, p) })[2])
1338 return "{} --[[DUPLICATE]]"
1339 end
1340 end
1341 table.insert(tables, t) -- logs table to past tables
1342 local s = "{" -- start of serialization
1343 local size = 0
1344 l = l + indent -- set indentation level
1345 for k, v in pairs(t) do -- iterates over table
1346 size = size + 1 -- changes size for max limit
1347 if size > (_G.SimpleSpyMaxTableSize or 1000) then
1348 s = s
1349 .. "\n"
1350 .. string.rep(" ", l)
1351 .. "-- MAXIMUM TABLE SIZE REACHED, CHANGE '_G.SimpleSpyMaxTableSize' TO ADJUST MAXIMUM SIZE "
1352 break
1353 end
1354 if rawequal(k, t) then -- checks if the table being iterated over is being used as an index within itself (yay, lua)
1355 bottomstr = bottomstr
1356 .. "\n"
1357 .. tostring(n)
1358 .. tostring(path)
1359 .. "["
1360 .. tostring(n)
1361 .. tostring(path)
1362 .. "]"
1363 .. " = "
1364 .. (
1365 rawequal(v, k) and tostring(n) .. tostring(path)
1366 or v2s(v, l, p, n, vtv, k, t, path .. "[" .. tostring(n) .. tostring(path) .. "]", tables)
1367 )
1368 size -= 1
1369 continue
1370 end
1371 local currentPath = "" -- initializes the path of 'v' within 't'
1372 if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then -- cleanly handles table path generation (for the first half)
1373 currentPath = "." .. k
1374 else
1375 currentPath = "[" .. k2s(k, l, p, n, vtv, k, t, path .. currentPath, tables, tI) .. "]"
1376 end
1377 if size % 100 == 0 then
1378 scheduleWait()
1379 end
1380 -- actually serializes the member of the table
1381 s = s
1382 .. "\n"
1383 .. string.rep(" ", l)
1384 .. "["
1385 .. k2s(k, l, p, n, vtv, k, t, path .. currentPath, tables, tI)
1386 .. "] = "
1387 .. v2s(v, l, p, n, vtv, k, t, path .. currentPath, tables, tI)
1388 .. ","
1389 end
1390 if #s > 1 then -- removes the last comma because it looks nicer (no way to tell if it's done 'till it's done so...)
1391 s = s:sub(1, #s - 1)
1392 end
1393 if size > 0 then -- cleanly indents the last curly bracket
1394 s = s .. "\n" .. string.rep(" ", l - indent)
1395 end
1396 return s .. "}"
1397end
1398
1399--- key-to-string
1400function k2s(v, ...)
1401 if keyToString then
1402 if typeof(v) == "userdata" and getrawmetatable(v) then
1403 return string.format(
1404 '"<void> (%s)" --[[Potentially hidden data (tostring in SimpleSpy:HookRemote/GetRemoteFiredSignal at your own risk)]]',
1405 safetostring(v)
1406 )
1407 elseif typeof(v) == "userdata" then
1408 return string.format('"<void> (%s)"', safetostring(v))
1409 elseif type(v) == "userdata" and typeof(v) ~= "Instance" then
1410 return string.format('"<%s> (%s)"', typeof(v), tostring(v))
1411 elseif type(v) == "function" then
1412 return string.format('"<Function> (%s)"', tostring(v))
1413 end
1414 end
1415 return v2s(v, ...)
1416end
1417
1418--- function-to-string
1419function f2s(f)
1420 for k, x in pairs(getgenv()) do
1421 local isgucci, gpath
1422 if rawequal(x, f) then
1423 isgucci, gpath = true, ""
1424 elseif type(x) == "table" then
1425 isgucci, gpath = v2p(f, x)
1426 end
1427 if isgucci and type(k) ~= "function" then
1428 if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then
1429 return k .. gpath
1430 else
1431 return "getgenv()[" .. v2s(k) .. "]" .. gpath
1432 end
1433 end
1434 end
1435 if funcEnabled and debug.getinfo(f).name:match("^[%a_]+[%w_]*$") then
1436 return "function()end --[[" .. debug.getinfo(f).name .. "]]"
1437 end
1438 return "function()end --[[" .. tostring(f) .. "]]"
1439end
1440
1441--- instance-to-path
1442--- @param i userdata
1443function i2p(i)
1444 local player = getplayer(i)
1445 local parent = i
1446 local out = ""
1447 if parent == nil then
1448 return "nil"
1449 elseif player then
1450 while true do
1451 if parent and parent == player.Character then
1452 if player == Players.LocalPlayer then
1453 return 'game:GetService("Players").LocalPlayer.Character' .. out
1454 else
1455 return i2p(player) .. ".Character" .. out
1456 end
1457 else
1458 if parent.Name:match("[%a_]+[%w+]*") ~= parent.Name then
1459 out = ":FindFirstChild(" .. formatstr(parent.Name) .. ")" .. out
1460 else
1461 out = "." .. parent.Name .. out
1462 end
1463 end
1464 parent = parent.Parent
1465 end
1466 elseif parent ~= game then
1467 while true do
1468 if parent and parent.Parent == game then
1469 local service = game:FindService(parent.ClassName)
1470 if service then
1471 if parent.ClassName == "Workspace" then
1472 return "workspace" .. out
1473 else
1474 return 'game:GetService("' .. service.ClassName .. '")' .. out
1475 end
1476 else
1477 if parent.Name:match("[%a_]+[%w_]*") then
1478 return "game." .. parent.Name .. out
1479 else
1480 return "game:FindFirstChild(" .. formatstr(parent.Name) .. ")" .. out
1481 end
1482 end
1483 elseif parent.Parent == nil then
1484 getnilrequired = true
1485 return "getNil(" .. formatstr(parent.Name) .. ', "' .. parent.ClassName .. '")' .. out
1486 elseif parent == Players.LocalPlayer then
1487 out = ".LocalPlayer" .. out
1488 else
1489 if parent.Name:match("[%a_]+[%w_]*") ~= parent.Name then
1490 out = ":FindFirstChild(" .. formatstr(parent.Name) .. ")" .. out
1491 else
1492 out = "." .. parent.Name .. out
1493 end
1494 end
1495 parent = parent.Parent
1496 end
1497 else
1498 return "game"
1499 end
1500end
1501
1502--- userdata-to-string: userdata
1503--- @param u userdata
1504function u2s(u)
1505 if typeof(u) == "TweenInfo" then
1506 -- TweenInfo
1507 return "TweenInfo.new("
1508 .. tostring(u.Time)
1509 .. ", Enum.EasingStyle."
1510 .. tostring(u.EasingStyle)
1511 .. ", Enum.EasingDirection."
1512 .. tostring(u.EasingDirection)
1513 .. ", "
1514 .. tostring(u.RepeatCount)
1515 .. ", "
1516 .. tostring(u.Reverses)
1517 .. ", "
1518 .. tostring(u.DelayTime)
1519 .. ")"
1520 elseif typeof(u) == "Ray" then
1521 -- Ray
1522 return "Ray.new(" .. u2s(u.Origin) .. ", " .. u2s(u.Direction) .. ")"
1523 elseif typeof(u) == "NumberSequence" then
1524 -- NumberSequence
1525 local ret = "NumberSequence.new("
1526 for i, v in pairs(u.KeyPoints) do
1527 ret = ret .. tostring(v)
1528 if i < #u.Keypoints then
1529 ret = ret .. ", "
1530 end
1531 end
1532 return ret .. ")"
1533 elseif typeof(u) == "DockWidgetPluginGuiInfo" then
1534 -- DockWidgetPluginGuiInfo
1535 return "DockWidgetPluginGuiInfo.new(Enum.InitialDockState" .. tostring(u) .. ")"
1536 elseif typeof(u) == "ColorSequence" then
1537 -- ColorSequence
1538 local ret = "ColorSequence.new("
1539 for i, v in pairs(u.KeyPoints) do
1540 ret = ret .. "Color3.new(" .. tostring(v) .. ")"
1541 if i < #u.Keypoints then
1542 ret = ret .. ", "
1543 end
1544 end
1545 return ret .. ")"
1546 elseif typeof(u) == "BrickColor" then
1547 -- BrickColor
1548 return "BrickColor.new(" .. tostring(u.Number) .. ")"
1549 elseif typeof(u) == "NumberRange" then
1550 -- NumberRange
1551 return "NumberRange.new(" .. tostring(u.Min) .. ", " .. tostring(u.Max) .. ")"
1552 elseif typeof(u) == "Region3" then
1553 -- Region3
1554 local center = u.CFrame.Position
1555 local size = u.CFrame.Size
1556 local vector1 = center - size / 2
1557 local vector2 = center + size / 2
1558 return "Region3.new(" .. u2s(vector1) .. ", " .. u2s(vector2) .. ")"
1559 elseif typeof(u) == "Faces" then
1560 -- Faces
1561 local faces = {}
1562 if u.Top then
1563 table.insert(faces, "Enum.NormalId.Top")
1564 end
1565 if u.Bottom then
1566 table.insert(faces, "Enum.NormalId.Bottom")
1567 end
1568 if u.Left then
1569 table.insert(faces, "Enum.NormalId.Left")
1570 end
1571 if u.Right then
1572 table.insert(faces, "Enum.NormalId.Right")
1573 end
1574 if u.Back then
1575 table.insert(faces, "Enum.NormalId.Back")
1576 end
1577 if u.Front then
1578 table.insert(faces, "Enum.NormalId.Front")
1579 end
1580 return "Faces.new(" .. table.concat(faces, ", ") .. ")"
1581 elseif typeof(u) == "EnumItem" then
1582 return tostring(u)
1583 elseif typeof(u) == "Enums" then
1584 return "Enum"
1585 elseif typeof(u) == "Enum" then
1586 return "Enum." .. tostring(u)
1587 elseif typeof(u) == "RBXScriptSignal" then
1588 return "nil --[[RBXScriptSignal]]"
1589 elseif typeof(u) == "Vector3" then
1590 return string.format("Vector3.new(%s, %s, %s)", v2s(u.X), v2s(u.Y), v2s(u.Z))
1591 elseif typeof(u) == "CFrame" then
1592 local xAngle, yAngle, zAngle = u:ToEulerAnglesXYZ()
1593 return string.format(
1594 "CFrame.new(%s, %s, %s) * CFrame.Angles(%s, %s, %s)",
1595 v2s(u.X),
1596 v2s(u.Y),
1597 v2s(u.Z),
1598 v2s(xAngle),
1599 v2s(yAngle),
1600 v2s(zAngle)
1601 )
1602 elseif typeof(u) == "DockWidgetPluginGuiInfo" then
1603 return string.format(
1604 "DockWidgetPluginGuiInfo(%s, %s, %s, %s, %s, %s, %s)",
1605 "Enum.InitialDockState.Right",
1606 v2s(u.InitialEnabled),
1607 v2s(u.InitialEnabledShouldOverrideRestore),
1608 v2s(u.FloatingXSize),
1609 v2s(u.FloatingYSize),
1610 v2s(u.MinWidth),
1611 v2s(u.MinHeight)
1612 )
1613 elseif typeof(u) == "PathWaypoint" then
1614 return string.format("PathWaypoint.new(%s, %s)", v2s(u.Position), v2s(u.Action))
1615 elseif typeof(u) == "UDim" then
1616 return string.format("UDim.new(%s, %s)", v2s(u.Scale), v2s(u.Offset))
1617 elseif typeof(u) == "UDim2" then
1618 return string.format(
1619 "UDim2.new(%s, %s, %s, %s)",
1620 v2s(u.X.Scale),
1621 v2s(u.X.Offset),
1622 v2s(u.Y.Scale),
1623 v2s(u.Y.Offset)
1624 )
1625 elseif typeof(u) == "Rect" then
1626 return string.format("Rect.new(%s, %s)", v2s(u.Min), v2s(u.Max))
1627 else
1628 return string.format("nil --[[%s]]", typeof(u))
1629 end
1630end
1631
1632--- Gets the player an instance is descended from
1633function getplayer(instance)
1634 for _, v in pairs(Players:GetPlayers()) do
1635 if v.Character and (instance:IsDescendantOf(v.Character) or instance == v.Character) then
1636 return v
1637 end
1638 end
1639end
1640
1641--- value-to-path (in table)
1642function v2p(x, t, path, prev)
1643 if not path then
1644 path = ""
1645 end
1646 if not prev then
1647 prev = {}
1648 end
1649 if rawequal(x, t) then
1650 return true, ""
1651 end
1652 for i, v in pairs(t) do
1653 if rawequal(v, x) then
1654 if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
1655 return true, (path .. "." .. i)
1656 else
1657 return true, (path .. "[" .. v2s(i) .. "]")
1658 end
1659 end
1660 if type(v) == "table" then
1661 local duplicate = false
1662 for _, y in pairs(prev) do
1663 if rawequal(y, v) then
1664 duplicate = true
1665 end
1666 end
1667 if not duplicate then
1668 table.insert(prev, t)
1669 local found
1670 found, p = v2p(x, v, path, prev)
1671 if found then
1672 if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
1673 return true, "." .. i .. p
1674 else
1675 return true, "[" .. v2s(i) .. "]" .. p
1676 end
1677 end
1678 end
1679 end
1680 end
1681 return false, ""
1682end
1683
1684--- format s: string, byte encrypt (for weird symbols)
1685function formatstr(s, indentation)
1686 if not indentation then
1687 indentation = 0
1688 end
1689 local handled, reachedMax = handlespecials(s, indentation)
1690 return '"'
1691 .. handled
1692 .. '"'
1693 .. (
1694 reachedMax
1695 and " --[[ MAXIMUM STRING SIZE REACHED, CHANGE '_G.SimpleSpyMaxStringSize' TO ADJUST MAXIMUM SIZE ]]"
1696 or ""
1697 )
1698end
1699
1700--- Adds \'s to the text as a replacement to whitespace chars and other things because string.format can't yayeet
1701function handlespecials(value, indentation)
1702 local buildStr = {}
1703 local i = 1
1704 local char = string.sub(value, i, i)
1705 local indentStr
1706 while char ~= "" do
1707 if char == '"' then
1708 buildStr[i] = '\\"'
1709 elseif char == "\\" then
1710 buildStr[i] = "\\\\"
1711 elseif char == "\n" then
1712 buildStr[i] = "\\n"
1713 elseif char == "\t" then
1714 buildStr[i] = "\\t"
1715 elseif string.byte(char) > 126 or string.byte(char) < 32 then
1716 buildStr[i] = string.format("\\%d", string.byte(char))
1717 else
1718 buildStr[i] = char
1719 end
1720 i = i + 1
1721 char = string.sub(value, i, i)
1722 if i % 200 == 0 then
1723 indentStr = indentStr or string.rep(" ", indentation + indent)
1724 table.move({ '"\n', indentStr, '... "' }, 1, 3, i, buildStr)
1725 i += 3
1726 end
1727 end
1728 return table.concat(buildStr)
1729end
1730
1731-- safe (ish) tostring
1732function safetostring(v: any)
1733 if typeof(v) == "userdata" or type(v) == "table" then
1734 local mt = getrawmetatable(v)
1735 local badtostring = mt and rawget(mt, "__tostring")
1736 if mt and badtostring then
1737 rawset(mt, "__tostring", nil)
1738 local out = tostring(v)
1739 rawset(mt, "__tostring", badtostring)
1740 return out
1741 end
1742 end
1743 return tostring(v)
1744end
1745
1746--- finds script from 'src' from getinfo, returns nil if not found
1747--- @param src string
1748function getScriptFromSrc(src)
1749 local realPath
1750 local runningTest
1751 --- @type number
1752 local s, e
1753 local match = false
1754 if src:sub(1, 1) == "=" then
1755 realPath = game
1756 s = 2
1757 else
1758 runningTest = src:sub(2, e and e - 1 or -1)
1759 for _, v in pairs(getnilinstances()) do
1760 if v.Name == runningTest then
1761 realPath = v
1762 break
1763 end
1764 end
1765 s = #runningTest + 1
1766 end
1767 if realPath then
1768 e = src:sub(s, -1):find("%.")
1769 local i = 0
1770 repeat
1771 i += 1
1772 if not e then
1773 runningTest = src:sub(s, -1)
1774 local test = realPath.FindFirstChild(realPath, runningTest)
1775 if test then
1776 realPath = test
1777 end
1778 match = true
1779 else
1780 runningTest = src:sub(s, e)
1781 local test = realPath.FindFirstChild(realPath, runningTest)
1782 local yeOld = e
1783 if test then
1784 realPath = test
1785 s = e + 2
1786 e = src:sub(e + 2, -1):find("%.")
1787 e = e and e + yeOld or e
1788 else
1789 e = src:sub(e + 2, -1):find("%.")
1790 e = e and e + yeOld or e
1791 end
1792 end
1793 until match or i >= 50
1794 end
1795 return realPath
1796end
1797
1798--- schedules the provided function (and calls it with any args after)
1799function schedule(f, ...)
1800 table.insert(scheduled, { f, ... })
1801end
1802
1803--- yields the current thread until the scheduler gives the ok
1804function scheduleWait()
1805 local thread = coroutine.running()
1806 schedule(function()
1807 coroutine.resume(thread)
1808 end)
1809 coroutine.yield()
1810end
1811
1812--- the big (well tbh small now) boi task scheduler himself, handles p much anything as quicc as possible
1813function taskscheduler()
1814 if not toggle then
1815 scheduled = {}
1816 return
1817 end
1818 if #scheduled > 1000 then
1819 table.remove(scheduled, #scheduled)
1820 end
1821 if #scheduled > 0 then
1822 local currentf = scheduled[1]
1823 table.remove(scheduled, 1)
1824 if type(currentf) == "table" and type(currentf[1]) == "function" then
1825 pcall(unpack(currentf))
1826 end
1827 end
1828end
1829
1830--- Handles remote logs
1831function remoteHandler(hookfunction, methodName, remote, args, funcInfo, calling, returnValue)
1832 local validInstance, validClass = pcall(function()
1833 return remote:IsA("RemoteEvent") or remote:IsA("RemoteFunction")
1834 end)
1835 if validInstance and validClass then
1836 local func = funcInfo.func
1837 if not calling then
1838 _, calling = pcall(getScriptFromSrc, funcInfo.source)
1839 end
1840 coroutine.wrap(function()
1841 if remoteSignals[remote] then
1842 remoteSignals[remote]:Fire(args)
1843 end
1844 end)()
1845 if autoblock then
1846 if excluding[remote] then
1847 return
1848 end
1849 if not history[remote] then
1850 history[remote] = { badOccurances = 0, lastCall = tick() }
1851 end
1852 if tick() - history[remote].lastCall < 1 then
1853 history[remote].badOccurances += 1
1854 return
1855 else
1856 history[remote].badOccurances = 0
1857 end
1858 if history[remote].badOccurances > 3 then
1859 excluding[remote] = true
1860 return
1861 end
1862 history[remote].lastCall = tick()
1863 end
1864 local functionInfoStr
1865 local src
1866 if func and islclosure(func) then
1867 local functionInfo = {}
1868 functionInfo.info = funcInfo
1869 pcall(function()
1870 functionInfo.constants = debug.getconstants(func)
1871 end)
1872 pcall(function()
1873 functionInfoStr = v2v({ functionInfo = functionInfo })
1874 end)
1875 pcall(function()
1876 if type(calling) == "userdata" then
1877 src = calling
1878 end
1879 end)
1880 end
1881 if methodName:lower() == "fireserver" then
1882 newRemote(
1883 "event",
1884 remote.Name,
1885 args,
1886 remote,
1887 functionInfoStr,
1888 (blocklist[remote] or blocklist[remote.Name]),
1889 src
1890 )
1891 elseif methodName:lower() == "invokeserver" then
1892 newRemote(
1893 "function",
1894 remote.Name,
1895 args,
1896 remote,
1897 functionInfoStr,
1898 (blocklist[remote] or blocklist[remote.Name]),
1899 src,
1900 returnValue
1901 )
1902 end
1903 end
1904end
1905
1906--- Used for hookfunction
1907function hookRemote(remoteType, remote, ...)
1908 if typeof(remote) == "Instance" then
1909 local args = { ... }
1910 local validInstance, remoteName = pcall(function()
1911 return remote.Name
1912 end)
1913 if validInstance and not (blacklist[remote] or blacklist[remoteName]) then
1914 local funcInfo = {}
1915 local calling
1916 if funcEnabled then
1917 funcInfo = debug.getinfo(4) or funcInfo
1918 calling = useGetCallingScript and getcallingscript() or nil
1919 end
1920 if recordReturnValues and remoteType == "RemoteFunction" then
1921 local thread = coroutine.running()
1922 local args = { ... }
1923 task.defer(function()
1924 local returnValue
1925 if remoteHooks[remote] then
1926 args = { remoteHooks[remote](unpack(args)) }
1927 returnValue = originalFunction(remote, unpack(args))
1928 else
1929 returnValue = originalFunction(remote, unpack(args))
1930 end
1931 schedule(
1932 remoteHandler,
1933 true,
1934 remoteType == "RemoteEvent" and "fireserver" or "invokeserver",
1935 remote,
1936 args,
1937 funcInfo,
1938 calling,
1939 returnValue
1940 )
1941 if blocklist[remote] or blocklist[remoteName] then
1942 coroutine.resume(thread)
1943 else
1944 coroutine.resume(thread, unpack(returnValue))
1945 end
1946 end)
1947 else
1948 schedule(
1949 remoteHandler,
1950 true,
1951 remoteType == "RemoteEvent" and "fireserver" or "invokeserver",
1952 remote,
1953 args,
1954 funcInfo,
1955 calling
1956 )
1957 if blocklist[remote] or blocklist[remoteName] then
1958 return
1959 end
1960 end
1961 end
1962 end
1963 if recordReturnValues and remoteType == "RemoteFunction" then
1964 return coroutine.yield()
1965 elseif remoteType == "RemoteEvent" then
1966 if remoteHooks[remote] then
1967 return originalEvent(remote, remoteHooks[remote](...))
1968 end
1969 return originalEvent(remote, ...)
1970 else
1971 if remoteHooks[remote] then
1972 return originalFunction(remote, remoteHooks[remote](...))
1973 end
1974 return originalFunction(remote, ...)
1975 end
1976end
1977
1978local newnamecall = newcclosure(function(remote, ...)
1979 if typeof(remote) == "Instance" then
1980 local args = { ... }
1981 local methodName = getnamecallmethod()
1982 local validInstance, remoteName = pcall(function()
1983 return remote.Name
1984 end)
1985 if
1986 validInstance
1987 and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer")
1988 and not (blacklist[remote] or blacklist[remoteName])
1989 then
1990 local funcInfo = {}
1991 local calling
1992 if funcEnabled then
1993 funcInfo = debug.getinfo(3) or funcInfo
1994 calling = useGetCallingScript and getcallingscript() or nil
1995 end
1996 if recordReturnValues and (methodName == "InvokeServer" or methodName == "invokeServer") then
1997 local namecallThread = coroutine.running()
1998 local args = { ... }
1999 task.defer(function()
2000 local returnValue
2001 setnamecallmethod(methodName)
2002 if remoteHooks[remote] then
2003 args = { remoteHooks[remote](unpack(args)) }
2004 returnValue = { original(remote, unpack(args)) }
2005 else
2006 returnValue = { original(remote, unpack(args)) }
2007 end
2008 coroutine.resume(namecallThread, unpack(returnValue))
2009 coroutine.wrap(function()
2010 schedule(remoteHandler, false, methodName, remote, args, funcInfo, calling, returnValue)
2011 end)()
2012 end)
2013 else
2014 coroutine.wrap(function()
2015 schedule(remoteHandler, false, methodName, remote, args, funcInfo, calling)
2016 end)()
2017 end
2018 end
2019 if recordReturnValues and (methodName == "InvokeServer" or methodName == "invokeServer") then
2020 return coroutine.yield()
2021 elseif
2022 validInstance
2023 and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer")
2024 and (blocklist[remote] or blocklist[remoteName])
2025 then
2026 return nil
2027 elseif
2028 (not recordReturnValues or methodName ~= "InvokeServer" or methodName ~= "invokeServer")
2029 and validInstance
2030 and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer")
2031 and remoteHooks[remote]
2032 then
2033 return original(remote, remoteHooks[remote](...))
2034 else
2035 return original(remote, ...)
2036 end
2037 end
2038 return original(remote, ...)
2039end, original)
2040
2041local newFireServer = newcclosure(function(...)
2042 return hookRemote("RemoteEvent", ...)
2043end, originalEvent)
2044
2045local newInvokeServer = newcclosure(function(...)
2046 return hookRemote("RemoteFunction", ...)
2047end, originalFunction)
2048
2049--- Toggles on and off the remote spy
2050function toggleSpy()
2051 if not toggle then
2052 if hookmetamethod then
2053 local oldNamecall = hookmetamethod(game, "__namecall", newnamecall)
2054 original = original or function(...)
2055 return oldNamecall(...)
2056 end
2057 _G.OriginalNamecall = original
2058 else
2059 gm = gm or getrawmetatable(game)
2060 original = original or function(...)
2061 return gm.__namecall(...)
2062 end
2063 setreadonly(gm, false)
2064 if not original then
2065 warn("SimpleSpy: namecall method not found!")
2066 onToggleButtonClick()
2067 return
2068 end
2069 gm.__namecall = newnamecall
2070 setreadonly(gm, true)
2071 end
2072 originalEvent = hookfunction(remoteEvent.FireServer, newFireServer)
2073 originalFunction = hookfunction(remoteFunction.InvokeServer, newInvokeServer)
2074 else
2075 if hookmetamethod then
2076 if original then
2077 hookmetamethod(game, "__namecall", original)
2078 end
2079 else
2080 gm = gm or getrawmetatable(game)
2081 setreadonly(gm, false)
2082 gm.__namecall = original
2083 setreadonly(gm, true)
2084 end
2085 hookfunction(remoteEvent.FireServer, originalEvent)
2086 hookfunction(remoteFunction.InvokeServer, originalFunction)
2087 end
2088end
2089
2090--- Toggles between the two remotespy methods (hookfunction currently = disabled)
2091function toggleSpyMethod()
2092 toggleSpy()
2093 toggle = not toggle
2094end
2095
2096--- Shuts down the remote spy
2097function shutdown()
2098 if schedulerconnect then
2099 schedulerconnect:Disconnect()
2100 end
2101 for _, connection in pairs(connections) do
2102 coroutine.wrap(function()
2103 connection:Disconnect()
2104 end)()
2105 end
2106 SimpleSpy2:Destroy()
2107 hookfunction(remoteEvent.FireServer, originalEvent)
2108 hookfunction(remoteFunction.InvokeServer, originalFunction)
2109 if hookmetamethod then
2110 if original then
2111 hookmetamethod(game, "__namecall", original)
2112 end
2113 else
2114 gm = gm or getrawmetatable(game)
2115 setreadonly(gm, false)
2116 gm.__namecall = original
2117 setreadonly(gm, true)
2118 end
2119 _G.SimpleSpyExecuted = false
2120end
2121
2122-- main
2123if not _G.SimpleSpyExecuted then
2124 local succeeded, err = pcall(function()
2125 if not RunService:IsClient() then
2126 error("SimpleSpy cannot run on the server!")
2127 end
2128 if
2129 not hookfunction
2130 or not getrawmetatable
2131 or getrawmetatable and not getrawmetatable(game).__namecall
2132 or not setreadonly
2133 then
2134 local missing = {}
2135 if not hookfunction then
2136 table.insert(missing, "hookfunction")
2137 end
2138 if not getrawmetatable then
2139 table.insert(missing, "getrawmetatable")
2140 end
2141 if getrawmetatable and not getrawmetatable(game).__namecall then
2142 table.insert(missing, "getrawmetatable(game).__namecall")
2143 end
2144 if not setreadonly then
2145 table.insert(missing, "setreadonly")
2146 end
2147 shutdown()
2148 error(
2149 "This environment does not support method hooks!\n(Your exploit is not capable of running SimpleSpy)\nMissing: "
2150 .. table.concat(missing, ", ")
2151 )
2152 end
2153 _G.SimpleSpyShutdown = shutdown
2154 ContentProvider:PreloadAsync({
2155 "rbxassetid://6065821980",
2156 "rbxassetid://6065774948",
2157 "rbxassetid://6065821086",
2158 "rbxassetid://6065821596",
2159 ImageLabel,
2160 ImageLabel_2,
2161 ImageLabel_3,
2162 })
2163 -- if gethui then funcEnabled = false end
2164 onToggleButtonClick()
2165 RemoteTemplate.Parent = nil
2166 FunctionTemplate.Parent = nil
2167 codebox = Highlight.new(CodeBox)
2168 codebox:setRaw("")
2169 getgenv().SimpleSpy = SimpleSpy
2170 getgenv().getNil = function(name, class)
2171 for _, v in pairs(getnilinstances()) do
2172 if v.ClassName == class and v.Name == name then
2173 return v
2174 end
2175 end
2176 end
2177 TextLabel:GetPropertyChangedSignal("Text"):Connect(scaleToolTip)
2178 -- TopBar.InputBegan:Connect(onBarInput)
2179 MinimizeButton.MouseButton1Click:Connect(toggleMinimize)
2180 MaximizeButton.MouseButton1Click:Connect(toggleSideTray)
2181 Simple.MouseButton1Click:Connect(onToggleButtonClick)
2182 CloseButton.MouseEnter:Connect(onXButtonHover)
2183 CloseButton.MouseLeave:Connect(onXButtonUnhover)
2184 Simple.MouseEnter:Connect(onToggleButtonHover)
2185 Simple.MouseLeave:Connect(onToggleButtonUnhover)
2186 CloseButton.MouseButton1Click:Connect(shutdown)
2187 table.insert(connections, UserInputService.InputBegan:Connect(backgroundUserInput))
2188 connectResize()
2189 SimpleSpy2.Enabled = true
2190 coroutine.wrap(function()
2191 wait(1)
2192 onToggleButtonUnhover()
2193 end)()
2194 schedulerconnect = RunService.Heartbeat:Connect(taskscheduler)
2195 if syn and syn.protect_gui then
2196 pcall(syn.protect_gui, SimpleSpy2)
2197 end
2198 bringBackOnResize()
2199 SimpleSpy2.Parent = --[[gethui and gethui() or]]
2200 CoreGui
2201 _G.SimpleSpyExecuted = true
2202 if not Players.LocalPlayer then
2203 Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
2204 end
2205 Mouse = Players.LocalPlayer:GetMouse()
2206 oldIcon = Mouse.Icon
2207 table.insert(connections, Mouse.Move:Connect(mouseMoved))
2208 end)
2209 if not succeeded then
2210 warn(
2211 "A fatal error has occured, SimpleSpy was unable to launch properly.\nPlease DM this error message to @exx#9394:\n\n"
2212 .. tostring(err)
2213 )
2214 SimpleSpy2:Destroy()
2215 hookfunction(remoteEvent.FireServer, originalEvent)
2216 hookfunction(remoteFunction.InvokeServer, originalFunction)
2217 if hookmetamethod then
2218 if original then
2219 hookmetamethod(game, "__namecall", original)
2220 end
2221 else
2222 setreadonly(gm, false)
2223 gm.__namecall = original
2224 setreadonly(gm, true)
2225 end
2226 return
2227 end
2228else
2229 SimpleSpy2:Destroy()
2230 return
2231end
2232
2233----- ADD ONS ----- (easily add or remove additonal functionality to the RemoteSpy!)
2234--[[
2235 Some helpful things:
2236 - add your function in here, and create buttons for them through the 'newButton' function
2237 - the first argument provided is the TextButton the player clicks to run the function
2238 - generated scripts are generated when the namecall is initially fired and saved in remoteFrame objects
2239 - blacklisted remotes will be ignored directly in namecall (less lag)
2240 - the properties of a 'remoteFrame' object:
2241 {
2242 Name: (string) The name of the Remote
2243 GenScript: (string) The generated script that appears in the codebox (generated when namecall fired)
2244 Source: (Instance (LocalScript)) The script that fired/invoked the remote
2245 Remote: (Instance (RemoteEvent) | Instance (RemoteFunction)) The remote that was fired/invoked
2246 Log: (Instance (TextButton)) The button being used for the remote (same as 'selected.Log')
2247 }
2248 - globals list: (contact @exx#9394 for more information or if you have suggestions for more to be added)
2249 - closed: (boolean) whether or not the GUI is currently minimized
2250 - logs: (table[remoteFrame]) full of remoteFrame objects (properties listed above)
2251 - selected: (remoteFrame) the currently selected remoteFrame (properties listed above)
2252 - blacklist: (string[] | Instance[] (RemoteEvent) | Instance[] (RemoteFunction)) an array of blacklisted names and remotes
2253 - codebox: (Instance (TextBox)) the textbox that holds all the code- cleared often
2254]]
2255-- Copies the contents of the codebox
2256newButton("Copy Code", function()
2257 return "Click to copy code"
2258end, function()
2259 setclipboard(codebox:getString())
2260 TextLabel.Text = "Copied successfully!"
2261end)
2262
2263--- Copies the source script (that fired the remote)
2264newButton("Copy Remote", function()
2265 return "Click to copy the path of the remote"
2266end, function()
2267 if selected then
2268 setclipboard(v2s(selected.Remote.remote))
2269 TextLabel.Text = "Copied!"
2270 end
2271end)
2272
2273-- Executes the contents of the codebox through loadstring
2274newButton("Run Code", function()
2275 return "Click to execute code"
2276end, function()
2277 local orText = "Click to execute code"
2278 TextLabel.Text = "Executing..."
2279 local succeeded = pcall(function()
2280 return loadstring(codebox:getString())()
2281 end)
2282 if succeeded then
2283 TextLabel.Text = "Executed successfully!"
2284 else
2285 TextLabel.Text = "Execution error!"
2286 end
2287end)
2288
2289--- Gets the calling script (not super reliable but w/e)
2290newButton("Get Script", function()
2291 return "Click to copy calling script to clipboard\nWARNING: Not super reliable, nil == could not find"
2292end, function()
2293 if selected then
2294 setclipboard(SimpleSpy:ValueToString(selected.Source))
2295 TextLabel.Text = "Done!"
2296 end
2297end)
2298
2299--- Decompiles the script that fired the remote and puts it in the code box
2300newButton("Function Info", function()
2301 return "Click to view calling function information"
2302end, function()
2303 if selected then
2304 if selected.Function then
2305 codebox:setRaw(
2306 "-- Calling function info\n-- Generated by the SimpleSpy serializer\n\n" .. tostring(selected.Function)
2307 )
2308 end
2309 TextLabel.Text = "Done! Function info generated by the SimpleSpy Serializer."
2310 end
2311end)
2312
2313--- Clears the Remote logs
2314newButton("Clr Logs", function()
2315 return "Click to clear logs"
2316end, function()
2317 TextLabel.Text = "Clearing..."
2318 logs = {}
2319 for _, v in pairs(LogList:GetChildren()) do
2320 if not v:IsA("UIListLayout") then
2321 v:Destroy()
2322 end
2323 end
2324 codebox:setRaw("")
2325 selected = nil
2326 TextLabel.Text = "Logs cleared!"
2327end)
2328
2329--- Excludes the selected.Log Remote from the RemoteSpy
2330newButton("Exclude (i)", function()
2331 return "Click to exclude this Remote.\nExcluding a remote makes SimpleSpy ignore it, but it will continue to be usable."
2332end, function()
2333 if selected then
2334 blacklist[selected.Remote.remote] = true
2335 TextLabel.Text = "Excluded!"
2336 end
2337end)
2338
2339--- Excludes all Remotes that share the same name as the selected.Log remote from the RemoteSpy
2340newButton("Exclude (n)", function()
2341 return "Click to exclude all remotes with this name.\nExcluding a remote makes SimpleSpy ignore it, but it will continue to be usable."
2342end, function()
2343 if selected then
2344 blacklist[selected.Name] = true
2345 TextLabel.Text = "Excluded!"
2346 end
2347end)
2348
2349--- clears blacklist
2350newButton("Clr Blacklist", function()
2351 return "Click to clear the blacklist.\nExcluding a remote makes SimpleSpy ignore it, but it will continue to be usable."
2352end, function()
2353 blacklist = {}
2354 TextLabel.Text = "Blacklist cleared!"
2355end)
2356
2357--- Prevents the selected.Log Remote from firing the server (still logged)
2358newButton("Block (i)", function()
2359 return "Click to stop this remote from firing.\nBlocking a remote won't remove it from SimpleSpy logs, but it will not continue to fire the server."
2360end, function()
2361 if selected then
2362 if selected.Remote.remote then
2363 blocklist[selected.Remote.remote] = true
2364 TextLabel.Text = "Excluded!"
2365 else
2366 TextLabel.Text = "Error! Instance may no longer exist, try using Block (n)."
2367 end
2368 end
2369end)
2370
2371--- Prevents all remotes from firing that share the same name as the selected.Log remote from the RemoteSpy (still logged)
2372newButton("Block (n)", function()
2373 return "Click to stop remotes with this name from firing.\nBlocking a remote won't remove it from SimpleSpy logs, but it will not continue to fire the server."
2374end, function()
2375 if selected then
2376 blocklist[selected.Name] = true
2377 TextLabel.Text = "Excluded!"
2378 end
2379end)
2380
2381--- clears blacklist
2382newButton("Clr Blocklist", function()
2383 return "Click to stop blocking remotes.\nBlocking a remote won't remove it from SimpleSpy logs, but it will not continue to fire the server."
2384end, function()
2385 blocklist = {}
2386 TextLabel.Text = "Blocklist cleared!"
2387end)
2388
2389--- Attempts to decompile the source script
2390newButton("Decompile", function()
2391 return "Attempts to decompile source script\nWARNING: Not super reliable, nil == could not find"
2392end, function()
2393 if selected then
2394 if selected.Source then
2395 codebox:setRaw(decompile(selected.Source))
2396 TextLabel.Text = "Done!"
2397 else
2398 TextLabel.Text = "Source not found!"
2399 end
2400 end
2401end)
2402
2403newButton("Disable Info", function()
2404 return string.format(
2405 "[%s] Toggle function info (because it can cause lag in some games)",
2406 funcEnabled and "ENABLED" or "DISABLED"
2407 )
2408end, function()
2409 funcEnabled = not funcEnabled
2410 TextLabel.Text = string.format(
2411 "[%s] Toggle function info (because it can cause lag in some games)",
2412 funcEnabled and "ENABLED" or "DISABLED"
2413 )
2414end)
2415
2416newButton("Autoblock", function()
2417 return string.format(
2418 "[%s] [BETA] Intelligently detects and excludes spammy remote calls from logs",
2419 autoblock and "ENABLED" or "DISABLED"
2420 )
2421end, function()
2422 autoblock = not autoblock
2423 TextLabel.Text = string.format(
2424 "[%s] [BETA] Intelligently detects and excludes spammy remote calls from logs",
2425 autoblock and "ENABLED" or "DISABLED"
2426 )
2427 history = {}
2428 excluding = {}
2429end)
2430
2431newButton("CallingScript", function()
2432 return string.format(
2433 "[%s] [UNSAFE] Uses 'getcallingscript' to get calling script for Decompile and GetScript. Much more reliable, but opens up SimpleSpy to detection and/or instability.",
2434 useGetCallingScript and "ENABLED" or "DISABLED"
2435 )
2436end, function()
2437 useGetCallingScript = not useGetCallingScript
2438 TextLabel.Text = string.format(
2439 "[%s] [UNSAFE] Uses 'getcallingscript' to get calling script for Decompile and GetScript. Much more reliable, but opens up SimpleSpy to detection and/or instability.",
2440 useGetCallingScript and "ENABLED" or "DISABLED"
2441 )
2442end)
2443
2444newButton("KeyToString", function()
2445 return string.format(
2446 "[%s] [BETA] Uses an experimental new function to replicate Roblox's behavior when a non-primitive type is used as a key in a table. Still in development and may not properly reflect tostringed (empty) userdata.",
2447 keyToString and "ENABLED" or "DISABLED"
2448 )
2449end, function()
2450 keyToString = not keyToString
2451 TextLabel.Text = string.format(
2452 "[%s] [BETA] Uses an experimental new function to replicate Roblox's behavior when a non-primitive type is used as a key in a table. Still in development and may not properly reflect tostringed (empty) userdata.",
2453 keyToString and "ENABLED" or "DISABLED"
2454 )
2455end)
2456
2457newButton("ToggleReturnValues", function()
2458 return string.format(
2459 "[%s] [EXPERIMENTAL] Enables recording of return values for 'GetReturnValue'\n\nUse this method at your own risk, as it could be detectable.",
2460 recordReturnValues and "ENABLED" or "DISABLED"
2461 )
2462end, function()
2463 recordReturnValues = not recordReturnValues
2464 TextLabel.Text = string.format(
2465 "[%s] [EXPERIMENTAL] Enables recording of return values for 'GetReturnValue'\n\nUse this method at your own risk, as it could be detectable.",
2466 recordReturnValues and "ENABLED" or "DISABLED"
2467 )
2468end)
2469
2470newButton("GetReturnValue", function()
2471 return "[Experimental] If 'ReturnValues' is enabled, this will show the recorded return value for the RemoteFunction (if available)."
2472end, function()
2473 if selected then
2474 codebox:setRaw(SimpleSpy:ValueToVar(selected.ReturnValue, "returnValue"))
2475 end
2476end)