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