Login in to Steam - Programmatically ?

oldfart

Member
Hi guys,

I have written a multi-user, 2 x part Steam Account manager and the only fly in the ointment is the login to steam.

I am aware of (and currently use) the command line launcher - but even with the new "securer" email method (finally implemented by Valve) I'm loathe to let passwords free to the "wild".

If anyone can help I would appreciate it.


I write mostly in VS C++ (client PC application part) plus I have used on the web site part:- Apache, MySQL, PHP, SSL, HTTP.

I have just made the finishing touches to a final class that shuts down Steam (and it's applications) "nicely" - prior to swapping to another account.

I will also post some screen shots if there is any interest!

Basically an SSL website allows users to login and register their accounts/games (I have a few kids with about 12 x Steam accounts in all).

A Win32 executable (client/server) is then downloaded.

The (self updating) exe also accesses the SSL Site and allows account information to be downloaded and stored on a local PC.

Additionally, this exe is used to kick off any of the many accounts and games - it will stop and start Steam as required.

It's almost like a net cafe :D

cheers
AnOldBloke
 
Hi Erix920,

Here is an Image of the basic design
overview.gif


The Web Server acts as both a server and a client
Update_Client = Server for Client Update
Local_Booking_Validation = Client for User Validation etc
User_IF_Website = Web Page Server

The PC "Client" acts in a reciprocal way
Remote_Booking_Validation = Server to Validate User request details
ditto = Client for request Updates and Upgrades
ditto = Steam Launcher and Monitor

It's for the Steam Launcher part that I'd like to avoid using ..

Code:
BOOL CVNetCafeDlg::LaunchSteam(LPCTSTR pszSteamDir, LPCTSTR pszLaunchPath, LPCTSTR pszUserName, LPCTSTR pszPassword, int nGuid)
{
	CString	csCommandLine;
	BOOL	fRC	= FALSE;
	CString	csTemp;

	csCommandLine	= pszLaunchPath;
	csCommandLine	+= _STEAM_LOGIN;	//" -login ";
	csCommandLine	+= pszUserName;
	csCommandLine	+= " ";
	csCommandLine	+= pszPassword;
	csCommandLine	+= _STEAM_SILENT;	//" -silent";
	csCommandLine	+= _STEAM_DEV;		//" -dev";

	if	( nGuid >= 0 )
	{
		csTemp.Format( "%s %d ", _STEAM_APPLAUNCH, nGuid );
//		csCommandLine	+= _STEAM_APPLAUNCH;
//		csCommandLine	+= " ";
		csCommandLine	+= csTemp;
	}

	STARTUPINFO			si = {0};
	PROCESS_INFORMATION	pi = {0};

	// Try to load our process
	fRC = CreateProcess(NULL,		//pszExe,			//STEAM_EXE,		//NULL, 
						(char *)(LPCTSTR)csCommandLine,	//pszCommand,		//STEAM_PARAMS,	//exeString, 
						NULL, 
						NULL, 
						FALSE, 
						0,	//CREATE_SUSPENDED, 
						NULL, 
						(LPCTSTR)pszSteamDir,	//pszDirectory,	//STEAM_DIR,		//workingDir, 
						&si, 
						&pi);
	if	(!fRC)
	{
		DWORD	dwError	= GetLastError();
		dwError	= dwError;
		MessageBox( _STEAM_NOT_LOADING, "Error", MB_ICONERROR);
	}

	return	fRC;
}

I am a programmer of advanced age and many years experience - but I always willing to learn.

cheers
AnOldBloke
 
Sorry for leaving your thread, I have a pretty bad memory.

So what you want to do is make a website that has usernames and passwords of accounts. From there you can select a game and then it will send you a program that you can start Steam and login to that account securely with no risk of exposing the account details such as user pass and email?

I'm sure you know but doing something like this would be very difficult to secure. Only way I can see this is if the client sent the login request and then another proprietary server was logged in and authenticated by Steam. This proprietary server would act as a pass through (like a VPN I suppose) thus making it so the client never receives the account information. If you could pass the login packets from Steam's authentication servers to your client you could possibly make a VERY secure cafe like system. A system such as this would be incredibly valuable as right now there is no reasonable way to do this.
 
Hi Erix920,

I have already implemented and tested the infrastructure that you have described (with the exception of the VPN) - it has been set up and working for a couple of months now - if you are interested I'll send you web the address.

The 2nd part is a Win32 exe which can do pretty much do - what a Virus is capable of. Thus for you to feel safe in using it I'd. have to supply you with the project. :D VS6 C++. (Excerpt below)

Several of my clan mates have tested my initial release using our clan "Library Game Accounts".

I will follow up the VPN aspects you have suggested because my next line of enquiry would be looking into how MicroSloth implements it's RDP.

So far I've found :-
Injecting into Steam just makes it abort, SendInput and other Windoze message just appear to be ignored (WM_CLOSE excepted when logging in).


cheers

vnetcafe106.jpg
 
Hi Erix920,

I haven't looked into Steam at all other than this 1 x specific area. I haven't tried snooping any of their login TCP/IP packets or other communications.

My prior interest was in interfacing and access to Steam powered game servers. I have written a few packages that control and interrogate both HLDS and SRCDS servers using both Windows and PHP based code.

I have also written quite a few web pages (for our forums) that allow me to kick and ban players as well as alter and alternate map cycles etc.

We (I) have an extensive DBase of Steam Ids, IPs comments etc that I initially started about 5 x years ago - by parsing the log files generated by HL1 games ;)

So I am not much help with your request - but I may look into it after I finish this current project!

cheers
AnOldBloke
 
Sounds good :), like I said if you need any
help let me know. Also if you didn't know you can login to Steam using API calls to steam.dll. This way you don't have to use -login or deal with the GUi. If you're interested I'll get you the link to the topic that shows all the things you can API calls. Sorry if I'm insulting your intelligence, I don't really know where you stand with this stuff.
 
Also if you didn't know you can login to Steam using API calls to steam.dll. This way you don't have to use -login or deal with the GUi. If you're interested I'll get you the link to the topic that shows all the things you can API calls.

Bloody beauty!! (An old Aussie expression ;)).

That is exactly what I am looking for!! I eagerly await your link!

I have replied to your PM with a couple of websites that I'm involved with but won't post here in the 'open" ;)

cheers
AnOldBloke
 
Erix920 you're a "gem" :D

That worked like a beaut and I just programmatically logged in and kicked off "Killing floor" using a test harness!!

I have converted the supplied C#/VB to VC++ and included it below for anyone who is interested - thanks again - I really appreciate your help!

cheers
AnOldBloke

Code:
typedef int		(*SteamStartEngine)( TSteamError &rtSteamErr );
typedef	int		(*SteamShutdownEngine)(TSteamError &rtSteamErr );
typedef	int		(*SteamStartup)(UINT uUsingMask, TSteamError &rtSteamErr );
typedef	UINT	(*SteamLogin)( char *pszUsername, char* pszPassword, int isSecureComputer, TSteamError &rtSteamErr);
typedef	int		(*SteamIsLoggedIn)( int &rnIsLoggedIn, TSteamError &rtSteamErr );
typedef	UINT	(*SteamSetUser)( char *pszUsername, int &rnUserSet, TSteamError &rtSteamErr);

typedef	UINT	(*SteamLogout)( TSteamError &rtSteamErr );
typedef	int		(*SteamCleanup)( TSteamError &rtSteamErr );

typedef	int		(*SteamProcessCall)(UINT nSteamHandle, TSteamProgress &rtProgress, TSteamError &rtSteamErr );
typedef	int		(*SteamIsAppSubscribed)(UINT uAppID, UINT &ruIsAppSubscribed, UINT &ruIsAppReady, TSteamError &rtSteamErr);
typedef	UINT	(*SteamLaunchApp)(UINT uAppId, UINT uOpt, char *pszArgs, TSteamError &rtSteamErr);

static	SteamStartEngine		pfnSteamStartEngine;
static	SteamStartup			pfnSteamStartup;
static	SteamLogin				pfnSteamLogin;
static	SteamIsLoggedIn			pfnSteamIsLoggedIn;
static	SteamSetUser			pfnSteamSetUser;

static	SteamLogout				pfnSteamLogout;
static	SteamCleanup			pfnSteamCleanup;
static	SteamShutdownEngine		pfnSteamShutdownEngine;

static	SteamProcessCall		pfnSteamProcessCall;
static	SteamIsAppSubscribed	pfnSteamIsAppSubscribed;
static	SteamLaunchApp			pfnSteamLaunchApp;

//extern int SteamStartEngine(TSteamError &rtSteamErr);

void CSteamTesterDlg::OnButtonLogin() 
{
	CString	csLaunchPath;
	CString	csSteamDir;
	CString	csTemp;

	TSteamError		tSteamError;
	TSteamProgress	tSteamProgress;
	UINT			hSteam;
	UINT			uStartOptions	= 15;	// 0x000F
	int				nIsLoggedIn;
	int				nUserSet;
	int				nRet;

	CString	csCommandLine;
	BOOL	fRC	= FALSE;

	UpdateData( TRUE );

	HINSTANCE	hInstSteam;
	DWORD	dwError;

	if	( m_ptSteamWatch->IsSteamPathReady() )
	{
		int	nCount	= m_ptSteamWatch->GetSteamPathCount();
		if	( nCount )
		{
			STEAM_PATH_DATA	tData;
			m_ptSteamWatch->GetSteamPathData( 0, tData );
			csLaunchPath	= tData.csLaunchPath;
			csSteamDir	= tData.csSteamDir;
		}
	}
	csTemp	= csSteamDir;
	csTemp	+= "steam.dll";

	hInstSteam	= LoadLibrary( csTemp );

	if (!hInstSteam)
	{
		dwError	= GetLastError();
		//m_sCriticalSection.Unlock();
		//if (!m_bFailureSent)
		//{
		//	m_bFailureSent=TRUE;
		//	DoLayerCallback(LAYERCALLBACK_LAYERSPECIFIC, SSL_FAILURE, SSL_FAILURE_LOADDLLS);
		//}
		return;
	}

 
	pfnSteamStartEngine		= (SteamStartEngine)GetProcAddress( hInstSteam, "SteamStartEngine" );
	pfnSteamStartup			= (SteamStartup)GetProcAddress( hInstSteam, "SteamStartup" );
	pfnSteamLogin			= (SteamLogin)GetProcAddress( hInstSteam, "SteamLogin" );
	pfnSteamIsLoggedIn		= (SteamIsLoggedIn)GetProcAddress( hInstSteam, "SteamIsLoggedIn" );
	pfnSteamSetUser			= (SteamSetUser)GetProcAddress( hInstSteam, "SteamSetUser" );

	pfnSteamLogout			= (SteamLogout)GetProcAddress( hInstSteam, "SteamLogout" );
	pfnSteamCleanup			= (SteamCleanup)GetProcAddress( hInstSteam, "SteamCleanup" );
	pfnSteamShutdownEngine	= (SteamShutdownEngine)GetProcAddress( hInstSteam, "SteamShutdownEngine" );

	pfnSteamProcessCall		= (SteamProcessCall)GetProcAddress( hInstSteam, "SteamProcessCall" );
	pfnSteamIsAppSubscribed	= (SteamIsAppSubscribed)GetProcAddress( hInstSteam, "SteamIsAppSubscribed" );
	pfnSteamLaunchApp		= (SteamLaunchApp)GetProcAddress( hInstSteam, "SteamLaunchApp" );

	//	Start up
	dwError	= pfnSteamStartEngine( tSteamError );
	dwError	= pfnSteamStartup( uStartOptions, tSteamError );


	//	Login
	hSteam	= pfnSteamLogin( (char *)(LPCTSTR)m_csUsername, (char *)(LPCTSTR)m_csPassword, 1, tSteamError );
	tSteamProgress.Valid	= 1;
	while ( pfnSteamProcessCall( hSteam, tSteamProgress, tSteamError ) == 0)
	{
		Sleep( 100 );	//Application.DoEvents();
	}

	//	Check if logged in
	nIsLoggedIn	= 0;
	nRet	= pfnSteamIsLoggedIn( nIsLoggedIn, tSteamError );
	
	if	( nIsLoggedIn )
	{
		nUserSet	= 0;
		hSteam	= pfnSteamSetUser((char *)(LPCTSTR)m_csUsername, nUserSet, tSteamError );
		tSteamProgress.Valid	= 1;
		while ( pfnSteamProcessCall( hSteam, tSteamProgress, tSteamError ) == 0)
		{
			Sleep( 100 );	//Application.DoEvents();
		}
		if	( nUserSet )
		{
			//	sHandle = SteamLaunchApp(AppID, 0, sArgs, SErr)
			hSteam	= pfnSteamLaunchApp( 1250, 0, "-console", tSteamError );
			tSteamProgress.Valid	= 1;
			while ( pfnSteamProcessCall( hSteam, tSteamProgress, tSteamError ) == 0)
			{
				Sleep( 100 );	//Application.DoEvents();
			}
			TRACE0( "Terror\n" );
		}
	}

	//	Logout
	tSteamProgress.Valid	= 1;
	hSteam	= pfnSteamLogout( tSteamError );
	while ( pfnSteamProcessCall( hSteam, tSteamProgress, tSteamError ) == 0)
	{
		Sleep( 100 );	//Application.DoEvents();
	}


	//	Shut down
	dwError	= pfnSteamCleanup( tSteamError );
	dwError	= pfnSteamShutdownEngine( tSteamError );

	if	(hInstSteam)
	{
		FreeLibrary(hInstSteam);
	}

	hInstSteam	= NULL;
 
Glad I could help :).

Hi Erix920,

I haven't forgotten you guys or the help you have given me so here is a link to the WAD forums (to know us is to hate us ;) ).

And I will be back to offer my assistance (whatever) as soon as this settles :roll:

Link: We Always Dominate Clan Forums
cheers
AnOldBloke

P.S. I am not sure about your attitude to publishing projects or adding your logo to them. But either way please let me know!

cheers and thanks again! "viva la revolution!"

Erix920 - I am happy to release my Beta project to you for evaluation. (Oops - I forgot to add Project includes source code :oops: )
 
For those that may be interested in the progress of this project - I've attached a few screenies ..

Login dialog box ..
(The "Use Local content" check box will attempt to read the local dbase.
On Validation failure the app will attempt to contact the web database via SSL).

vnetcafe112.jpg



A test account containing "real steam" accounts.
Double Left click on an Account will launch the account similarly for dbl clicking on a game.
Right click will allow you to modify/delete an account from the registry.

vnetcafe113.jpg


Adding an new account to the local (registry based) dbase ..
(The information is encrypted and stored in the registry).

vnetcafe114.jpg



Terminating steam/steam apps prior to launching a new steam instance ..

vnetcafe115.jpg
 
You can use something like this to get games from the account programmatically so the user doesn't have to pick and choose themselves.

Code:
    Public Shared Sub queryAccountInfo() ' 4
        Try
            Dim SErr As New TSteamError()
            games = ""
            Dim iBuffSize As UInteger = 512
            Dim iSteamReturn As Integer = 0

            Dim AppStats As New TSteamAppStats()

            iSteamReturn = SteamGetAppStats(AppStats, SErr)

            Dim SubscriptionStats As New TSteamSubscriptionStats()

            iSteamReturn = SteamGetSubscriptionStats(SubscriptionStats, SErr)

            If SubscriptionStats.uNumSubscriptions > 0 Then
                Dim RawSubscriptionIDs As UInteger()
                RawSubscriptionIDs = New UInteger(SubscriptionStats.uNumSubscriptions - 1) {}
                iSteamReturn = SteamGetSubscriptionIds(RawSubscriptionIDs, SubscriptionStats.uNumSubscriptions, SErr)

                Dim tAppIDs = New UInteger(AppStats.uNumApps) {} 'set the array to the maximum amount of Steam(Apps)

                ' Here we are creating a pointer to the tAppIDs array and allocating the correct amount of memory for manual marshaling
                Dim arraytype As Object = tAppIDs.GetValue(0)
                Dim arraysize As Integer = Marshal.SizeOf(arraytype)
                Dim buffer As Integer = arraysize * tAppIDs.Length
                Dim intPtr As IntPtr = Marshal.AllocHGlobal(buffer)

                Dim isSubSubscribed As UInteger = 0
                Dim isSubReady As UInteger = 0

                For Each RawSubscriptionID As UInteger In RawSubscriptionIDs
                    iSteamReturn = SteamIsSubscribed(RawSubscriptionID, isSubSubscribed, isSubReady, SErr)

                    If isSubSubscribed = 1 Then

                        Dim SubNFO As New TSteamSubscription()
                        SubNFO.Name = New String(" "c, 255)
                        SubNFO.MaxNameChars = 255
                        SubNFO.AppIDs = intPtr
                        SubNFO.MaxAppIDs = AppStats.uNumApps

                        iSteamReturn = SteamEnumerateSubscription(RawSubscriptionID, SubNFO, SErr)

                        If Not RawSubscriptionID = 0 Then
                            If Not SubNFO.Name = "" Then
                                If Not games = "" Then
                                    games = games & "," & SubNFO.Name
                                Else
                                    games = SubNFO.Name
                                End If
                            End If
                        End If

                        Dim ofs As Integer = 0
                        Dim uLoop As Integer
                        For uLoop = 0 To SubNFO.NumApps - 1 'populate our app array from marshaled memory
                            tAppIDs(uLoop) = Marshal.ReadInt32(intPtr, ofs)
                            ofs = ofs + arraysize
                        Next

                        For uLoop = 0 To SubNFO.NumApps - 1 'Clear the relevant array indexes for the next subscription
                            tAppIDs(uLoop) = 0
                        Next

                    End If
                Next
                Marshal.FreeHGlobal(intPtr) 'free the marshaled memory
            End If
        Catch ex As Exception
            MsgBox("Error at 4: " & ex.Message)
        End Try

    End Sub

Hope this helps :).
 
Thanks for that Erix920, I have that on my list after spotting in that code from the link you sent me.

My SSL server "spat the dummy" last night and I'll be rebuilding a new one this evening so the SSL side of my site will be out until then.

mmm ... I may just implement that code now while I wait (my son has to return me 2 PC's that he borrowed - 1 x of which will have to replace my Fujitsu/Siemens server until I can replace it :( ).

cheers
AnOldBloke
 
If this data can be stored in a database (account info user/pass), would other people be able to login with that account without being able to capture the pass. You can always validate the email so that only the true owner can change the PW. There's always that one "trusted" person who decides to go change all the passwords.
 
Hi Erix920,

This project has a number of scenarios ..

Accounts for Loan
1. Our clan has a number of accounts that we loan to our members so that we can trial new games etc.
2. To borrow (or book) a game from a "loan" Steam account - a "Fake" username and password are generated and emailed to the borrowing member.
3. The member logs in to the Web server using SSL thru' the client exe - with the "Fake" username etc.
4. The server (realising it is a valid borrow/booking) will then transmit the account's real username/password thru' to the client exe which will now kick off steam and the App - reasonably securely (thanks to your link ;) ).

Users Accounts
1. I have told our members that I don't recommend they put info about their "normal" steam accounts into the web site (Database).
2. Clan Members may donate old/unused accounts for other members to "borrow" - that is what we intend to keep on the Web based database.
3. I have recommended that users keep their account information in the their local (encrypted) database.

Future hopes ..
Peer-to-Peer SSL links directly between members (friends etc) allowing them to borrow/lend games.

Something else that may be of interest is that because these are legit accounts - Steam allows for the download of the associated GCF files ... if you get my drift. ;)

I hope that overview explains it better.

As soon as I get my SSL server up and running again I'll be starting work on the Peer-to-Peer part of the project as the other parts appear to be working and about to be Beta tested by other clan members.

Suggestions about protecting legit account info would be beneficial, as I don't have too much experience in that area. :eek:

cheers
AnOldBloke
 
I will ask the more involved Steam related people to see if they can come up with a solution or already have one.

Edit:

I asked steamCooker over at rin he said this.

hi afaik it's not possible as the auth ticket is bound to an IP
and the login packet is based on IP and timestamp

but maybe there's a way i don't know

I guess the accounts will just have to be verified so the passwords can't be changed.
 
Hi guys,

FWIW - I have discovered (the hard way of course :eek: ) that in order to get

Code:
SteamEnumerateSubscription( UINT SubscriptionID, 
			TSteamSubscription &rtSubscription, 
			TSteamError &rtSteamError);
to work properly the size of the TSteamSubscription must be increased by at least 0x120 bytes - see below ..

Code:
//	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
struct TSteamSubscription
{
    LPCTSTR	pszName;
	UINT	uMaxNameChars;
	UINT	*puAppIDs;
//	UINT	puAppIDs;
	UINT	uMaxAppIDs;
	UINT	uID;
	UINT	uNumApps;
	UINT	uDurationInSeconds;
	UINT	uCostInCents;
	UINT	uIsAutoRenewing;
	//	Requires approx additional 0x120 bytes
	UINT	auFiller[ 128 ];
};

If any of you have been experiencing weird behavior then IMO it looks like Steam have altered their dlls.

cheers
AnOldBloke
 
Back
Top