Coder Social home page Coder Social logo

Comments (10)

Helios-vmg avatar Helios-vmg commented on August 22, 2024

Hm... Do you have any hooks, and if so, does the crash go away if you take them out?

from deviare2.

bo3b avatar bo3b commented on August 22, 2024

Thank you for the response.

No, no hooks, not even setting up a hook handler. Just the launch. Simplifying down to the bare minimum, here is the entire app:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Nektra.Deviare2;


namespace InvisibleWalls
{
    class Program
    {
        static NktSpyMgr _spyMgr;
        static NktProcess _gameProcess;

        static void Main(string[] args)
        {
            int hresult;
            object continueevent;

            // Setup and Init the primary Deviare interface of the SpyMgr.  This is
            // declared static because we only ever want one.
            _spyMgr = new NktSpyMgr();
            hresult = _spyMgr.Initialize();
            if (hresult != 0)
                throw new Exception("Deviare initialization error.");

            // Launch the game, but suspended, so we can hook our first call and be certain to catch it.
            _gameProcess = _spyMgr.CreateProcess(@"G:\Games\limbo\limbo.exe", false, out continueevent);
            if (_gameProcess == null)
                throw new Exception("Game launch failed.");

            while (true)
            {
                System.Threading.Thread.Sleep(1000);
            }
        }
    }
}

There are no errors returned, it winds up in the while(true) loop, but the launched sub-process/game crashes with that error above.

The sequence of assembly code right at the crash is:

004F2B6F  int         3  
004F2B70  push        esi  
004F2B71  mov         esi,dword ptr [esp+8] 
004F2B75  cmp         byte ptr [esi],0    <<< PC
004F2B78  je          004F2BC9  
004F2B7A  lea         ebx,[ebx]  

With:
ESP 0018FAC4
ESI 00000000
0x0018FAC4 88 d8 08 02 61 62 4f 00 00 00 00 00 0f 00 00 00 ˆØ..abO.........

Which suggests that the stack is corrupted some how.


Initially Win10 x64. Rebooted to test. Still crashes.

Also tested on Win7 x64. Still crashes. Stack crawl is slightly different:

limbo.exe!004f2b75() Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for limbo.exe]
limbo.exe!004f6261() Unknown
limbo.exe!0048c06d() Unknown
[External Code]
limbo.exe!0044fd9a() Unknown
limbo.exe!004457ac() Unknown
[External Code]
limbo.exe!0079e549() Unknown
limbo.exe!00478878() Unknown
limbo.exe!00739980() Unknown
[External Code]


If I pause the app, and use the ResumeProcess to allow me to attach the debugger to Limbo as well as to the launching app (InvisibleWalls), then here is the entire Output window under Windows 7.

'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities.Sync\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Debugger.Runtime\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Debugger.Runtime.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'T:\Users\bo3b\Documents\Code\InvisibleWalls\Debug\InvisibleWalls.vshost.exe'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Data.DataSetExtensions\v4.0_4.0.0.0__b77a5c561934e089\System.Data.DataSetExtensions.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.CSharp\v4.0_4.0.0.0__b03f5f7f11d50a3a\Microsoft.CSharp.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
The thread 0xc3c has exited with code 259 (0x103).
The thread 0x36c has exited with code 259 (0x103).
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'T:\Users\bo3b\Documents\Code\InvisibleWalls\Debug\InvisibleWalls.exe'. Symbols loaded.
'InvisibleWalls.vshost.exe' (CLR v4.0.30319: InvisibleWalls.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'limbo.exe' (Win32): Loaded 'G:\Games\LIMBO\limbo.exe'. Cannot find or open the PDB file.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\ntdll.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\kernel32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\KernelBase.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\user32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\gdi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\lpk.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\usp10.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\msvcrt.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\advapi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\sechost.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\rpcrt4.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\sspicli.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\cryptbase.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\comdlg32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\shlwapi.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\winsxs\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.7601.18837_none_ec86b8d6858ec0bc\comctl32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\shell32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\dinput8.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\d3d9.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\version.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\d3d8thk.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\dwmapi.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\D3DX9_43.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\dbghelp.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\xinput1_3.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\setupapi.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\cfgmgr32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\oleaut32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\ole32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\devobj.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\imm32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\msctf.dll'. Symbols loaded.
The thread 0x1268 has exited with code 0 (0x0).
The thread 0x1728 has exited with code 259 (0x103).
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\uxtheme.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\nvd3dum.dll'. Cannot find or open the PDB file.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\winmm.dll'. Symbols loaded.
The thread 0xe80 has exited with code 0 (0x0).
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\shell32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\shell32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\user32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\user32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\advapi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\advapi32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\powrprof.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Program Files (x86)\NVIDIA Corporation\3D Vision\nvSCPAPI.dll'. Cannot find or open the PDB file.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\psapi.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\advapi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\advapi32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\nvapi.dll'. Cannot find or open the PDB file.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\advapi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\advapi32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\gdi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\gdi32.dll'
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\nvapi.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\nvapi.dll'. Cannot find or open the PDB file.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\advapi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\advapi32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\gdi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\gdi32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\gdi32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\gdi32.dll'
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\wintrust.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\crypt32.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\msasn1.dll'. Symbols loaded.
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\ole32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\ole32.dll'
The thread 0x1194 has exited with code 0 (0x0).
The thread 0x132c has exited with code 0 (0x0).
The thread 0x1768 has exited with code 0 (0x0).
The thread 0x1178 has exited with code 0 (0x0).
The thread 0x1164 has exited with code 0 (0x0).
The thread 0x6e0 has exited with code 0 (0x0).
The thread 0x15bc has exited with code 0 (0x0).
'limbo.exe' (Win32): Loaded 'C:\Windows\SysWOW64\user32.dll'. Symbols loaded.
'limbo.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\user32.dll'
The thread 0x440 has exited with code 0 (0x0).
The thread 0xde8 has exited with code 0 (0x0).
The thread 0x1260 has exited with code 0 (0x0).
The thread 0xdf8 has exited with code 0 (0x0).
The thread 0x16d4 has exited with code 0 (0x0).
The thread 0xf98 has exited with code 0 (0x0).
The thread 0x1410 has exited with code 0 (0x0).
The thread 0xec8 has exited with code 0 (0x0).
The thread 0xdf4 has exited with code 0 (0x0).
First-chance exception at 0x004F2B75 in limbo.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x004F2B75 in limbo.exe: 0xC0000005: Access violation reading location 0x00000000.

The program '[200] limbo.exe' has exited with code 0 (0x0).
The program '[4332] InvisibleWalls.vshost.exe' has exited with code -1 (0xffffffff).

I'm out of ideas. Any suggestions to debug it?

from deviare2.

scnale avatar scnale commented on August 22, 2024

When debugging an issue like this, it helps to build the entire Deviare2 solution in the Debug configuration. The Deviare Agent prints many log messages (e.g. describing the initialization process) in the debug output when built that way.

from deviare2.

bo3b avatar bo3b commented on August 22, 2024

It seemed really odd that it would crash with such a simple app, so I also changed the code to try launching the app using the Process class and ProcessStartInfo. Notably- that also crashed.

So that says that the crash was unrelated to Deviare, it was just something to do with the CreateProcess from the C# app to the game.


Digging and digging, I found the problem. The Working Directory.

The Working Directory by default is going to be the launching C# app, not the game location. The game is mad about that, and blows up. To be more exact, the UXTheme manager is mad about that, and blows up.

When I set the ProcessStartInfo.WorkingDirectory to the game directory, the crash is resolved.

However, I need to be able to use _spyMgr.CreateProcess because I need to start the game suspended so that I can be certain to hook dx9 initialization, and to avoid conflicts with other hooking overlays. Process.Start does not allow me to create the process suspended.


Not sure what the best answer for Deviare would be then. Currently there is no way to pass the WorkingDirectory to _spyMgr.CreateProcess, even though at the root it will call CreateProcess with Null for the WD.

I did find an OK workaround for the problem, which is to SetCurrentDirectory at the C# level, before launching. It cannot be done at DLLMain for a plugin, that looks to be too late.

         Directory.SetCurrentDirectory(@"G:\Games\limbo\");

I cannot be sure this won't cause problems with Deviare itself though, because the Deviare DLLs, DB, and Agent are all in the C# launcher folder. I want to avoid modifying the game directory itself, if possible.

from deviare2.

Helios-vmg avatar Helios-vmg commented on August 22, 2024

Yes, that's a bit of a missing feature. Years ago when I was maintaining SpyStudio I implemented an "execution parameters" feature that used spy manager's CreateProcessWithLogon() (which eventually used CreateProcessWithLogonW()), which let you pass command line arguments and run as a different user. Setting the WD for the hookee was discussed, but eventually forgotten about.

If you're brave enough, doing the modification to pass down a working directory to CreateProcessWithLogonW() from the C# API shouldn't be too difficult, I'd wager.

By the way, the hookee crashes when you call _spyMgr.ResumeProcess(), right? If so, another workaround might be to change the working directory of the hookee while it's suspended, which I believe should be possible, since it's just part of the process' environment.

from deviare2.

mxmauro avatar mxmauro commented on August 22, 2024

@bo3b Deviare dlls are loaded from the path where spymgr is initialized, they are not affected by your call to Directory.SetCurrentDirectory. Also you can modify the AgentPath property before initializing SpyMgr to set a new path.

We wanted to keep the methods as simple as possible. Although in native CreateProcessXXX calls, you can pass a starting working directory, it does not differ from doing it outside.

from deviare2.

bo3b avatar bo3b commented on August 22, 2024

Thank you for that extra detail. It seems to run fine, and it's helpful to know that you expect no problems from using SetCurrentDirectory.

@Helios-vmg I tried changing the working directory right before the ResumeProcess, but it would still wind up with the wrong directory at DLLMain in the plugin, and for the game. For anybody who might lookup this thread in the future, the SetCurrentDirectory needs to be done before the _SpyMgr.CreateProcess.

@mxmauro Good to know that the AgentPath can be changed to point to a different directory to find the Deviare DLLs.

Thanks for the help.

from deviare2.

bo3b avatar bo3b commented on August 22, 2024

One last thought here. Since the AgentPath is used for the DLLs, then there is no reason not to set the Working Directory to the path specified by the CreateProcess.

At the call the SpyMgr makes to CreateProcessW, it would be worth considering whether it makes more sense to specify the path directory instead of NULL. This would be the 90% case, because 90% of the time, people like me are going to want it to match a launch from Explorer. That also would match the normal behavior of Process.Start from C#, where WD cannot be specified at launch.

The drawback of specifying it there, would be that for anyone that wants a different WD would need to launch a different way.

from deviare2.

bo3b avatar bo3b commented on August 22, 2024

Another related problem that I'll note, in case someone runs across this while searching. Of course, this was probably already answered on the still dead forums. :->

Once you set the Working Directory using SetCurrentDirectory, that will fix the problem with games, and is clearly the right choice for a CreateProcess.

However, once that is set, then the launching app is no longer the Working Directory, which means that any LoadCustomDLL will not find the DLL specified, unless you use a full path description. Agent.Path is empty and not usable, so best bet is to use Environment.CurrentDirectory.

It seems to be possible to load the custom DLL, but it is not fully functional. CallCustomAPI will be broken, unless the full path is specified, or you leave it at default Working Directory.

from deviare2.

mxmauro avatar mxmauro commented on August 22, 2024

AgentPath must be set before initialization. About loading custome dlls, it is recommended to always use the full path because the application can change the working folder or dll loading order outside of your control.

from deviare2.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.