Developers.Hash.Guid.Use
GUID Hunt
Find out where, how, and when the Shareaza 2.0 code makes GUIDs.
Here are parts of the Windows platform that deal with GUIDs. GUID is the type that holds a GUID value. [1] makes a GUID [2] expresses a GUID in base 32 text
GUID types
In Windows, the type that holds a GUID is called GUID. Shareaza defines its own type, named GGUID, in StdAfx.h:
<source lang="c"> typedef union { BYTE n[16]; BYTE b[16]; DWORD w[4]; } GGUID; </source>
The code uses union to look at the same memory different ways. You can access the 16 bytes of a GUID individually with n or b, or as an array of 4 DWORDs with w.
A GUID takes up 16 bytes, which is 128 bits, and can be expressed with 32 hexadecimal characters. Both GUID and GGUID are just 16 bytes of memory:
<source lang="c"> // Find the size of the Windows and Shareaza GUID types DWORD size; size = sizeof(GUID); // Windows GUID type, 16 bytes size = sizeof(GGUID); // Shareaza GGUID type, also 16 bytes </source>
CGProfile::Create
First, look at the top of GProfile.cpp.
<source lang="c"> CGProfile MyProfile; </source>
You can put a breakpoint on this line of code. It gets executed when Shareaza runs. It creates a single global CGProfile object named MyProfile.
Next, look at the top of GProfile.h.
<source lang="c"> class CGProfile : public CComObject { // Construction public: CGProfile(); virtual ~CGProfile();
// Attributes public: GGUID GUID; </source>
The CGProfile class defines a member GGUID named GUID. The name GUID doesn't have the usual m_ prefix, but it's a member variable just like those that do.
When Shareaza runs, the complete name of the GGUID is MyProfile.GUID.
The method CGProfile::Create gets called when Shareaza runs. It happens during the splash screen, before the main window is displayed. It doesn't get called again as the program runs, but it does get called each time you start the program.
<source lang="c"> void CGProfile::Create() { Clear();
CoCreateGuid( (::GUID*)&GUID );
srand( GetTickCount() );
for ( int nByte = 0 ; nByte < 16 ; nByte++ ) GUID.n[ nByte ] += rand();
wchar_t szGUID[39]; szGUID[ StringFromGUID2( *(::GUID*)&GUID, szGUID, 39 ) - 2 ] = 0;
m_pXML = new CXMLElement( NULL, _T("gProfile") ); m_pXML->AddAttribute( _T("xmlns"), xmlns );
CXMLElement* pGnutella = m_pXML->AddElement( _T("gnutella") ); pGnutella->AddAttribute( _T("guid"), (CString)&szGUID[1] ); } </source>
Create is making the GUID Shareaza will use to identify itself on the Gnutella network. It's making it in the member variable MyProfile.GUID
First, the method calls CoCreateGuid to have Windows make a new GUID and store it in MyProfile.GUID. Both the Windows GUID type and the member variable have the same name, GUID. In a method, the code has to use the double colons with ::GUID to specify the Windows type, and not the member variable.
This should be enough, but the method makes some changes to the GUID after that. It uses the C function rand, which returns a random number in the range 0x7FFF. The for statement loops 16 times to add the result of rand to each byte in the 16-byte GUID. The biggest number a byte can hold is 0xFF, so adding rand overflows the byte several times until it comes back around on a new random value.
- Why is it using rand? You're not supposed to edit GUIDs, as it could erase their guaranteed uniqueness
Create calls StringFromGUID2 to express the GUID as text. Here's a sample of what the GUID looks like as this method runs.
<source lang="c"> From CoCreateGuid ac3f3bb2f1f89443ab8f34928c817b87 After rand d134b6e87f94b6f9a374241d861c556e From StringFromGUID2 "{E8B634D1-947F-F9B6-A374-241D861C556E"
Same as after rand d134b6e8 7f94 b6f9 a374 241d861c556e
</source>
Spinning the GUID with rand makes it completely different. StringFromGUID2 expresses it as base 16 text and puts in brackets and hyphens. It doesn't change the GUID, even though it might look that way at first. It expresses the order of the bytes in each part of the GUID in reverse order. For instance, the first byte of the GUID in memory is e8. That E8 appears at the end of the first chunk of text from StringFromGUID2.
- The code may have a bug - there is a missing } at the end of the text
CNetwork::CreateID
This method gets called all the time as Shareaza runs. Each Gnutella packet Shareaza sends has a unique GUID, and this is where they are made. It most commonly gets called from CG1Packet::New. CreateID doesn't use CoCreateGuid at all. It makes GUIDs by itself, from scratch.
<source lang="c"> void CNetwork::CreateID(GGUID* pID) { CSingleLock pLock( &m_pSection, TRUE );
*pID = MyProfile.GUID;
DWORD *pNum = (DWORD*)pID; pNum[0] += GetTickCount(); pNum[1] += ( m_nSequence++ ); pNum[2] += rand() * rand(); pNum[3] += rand() * rand(); } </source>
CreateID starts by copying the GUID that Create made. Then, it edits each 4-byte DWORD chunk of it.
To the first DWORD, CreateID adds the result of GetTickCount. This Windows function returns the number of milliseconds the computer has been running since the user turned it on. By itself, this is not enough to guarantee a unique result. If Shareaza calls CreateID twice within the same millisecond, GetTickCount will return the same result twice. This is why the second DWORD is raised by the value m_nSequence, which is then incremented.
The last two DWORD values are spun with two rand values multipled together.
- Why is it doing this? Again, wouldn't just calling CoCreateGUID be easier and safer?