After updating my version of SWAT4 to the latest patch from Vivendi, I soon discovered that the game was phoning home to grab posters to place in the enviroment of the game's levels. Not only did it do this for every level that was played, but it also informed the advertisers of how long each poster was viewed, and by which gamer. Expect to see this kind of advertising and brand placement becoming standard fare in the very near future.
While playing the latest version of SWAT4 with a friend, we noticed something was rather different. The particular level we were playing was modelled on a random gas station and convieince store.
In the game, the player can enter the store, and is immersed in a realistic environment, including goods for sale on the shelves, and posters advertising fake products on the walls. Until we updated the game to the latest 1.1 patch release.
With the new patch in place, we played the level again. This time, we noticed the posters were actually advertising real products. One told me of a new show on a sci-fi channel, and the other about a new brand of soft drink. We noticed these posters throughout the level. Then I remembered in the patch readme file, the one line that read:
* Added Massive Streaming Ad SupportWith this information, we could only wonder what 'massive ad streaming support' was. Obviously it was adding adverts to the game we had already paid $50 for, but we wanted to know how, and what information was it sending to the servers? With these questions in mind, we loaded up our favourite packet dumper.
With the packet dumper running, we ran the game again, and loaded up the level. We walked around the level to find the posters had changed -- some had changed position, and completely new ones had also appeared. We quit the game to see what the packet dumper had revealed.
We had some idea of what to expect, a request for some images, and that request fulfilled. The first transaction was a DNS lookup for 'madserver.net'. Seconds after, the first contact was made with madserver's servers.
A HTTP POST was sent to an endpoint called 'locateService'. This sent the name of the game we were playing, as well a version number. This version number is probably the version of the advertising software and not the game, as we were playing SWAT4, 1.1, as confirmed in the version string of the game executable.
In response, the server sent an XML response with a list of servers our client was going to contact during the course of the game.
POST http://38.119.38.151/adsrv/locateService HTTP/1.1
User-Agent: Adclient/1.0 Massive Inc.
Accept-Encoding: deflate
Proxy-Connection: Keep-Alive
Content-Type: text/xml
Host: 38.119.38.151
Content-Length: 109
<locateServiceRequest>
<skuName>swat_4_pc_na</skuName>
<version>1.9.176.000000</version>
</locateServiceRequest>
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Sat, 02 Jul 2005 23:46:05 GMT
Server: Apache-Coyote/1.1
fd
<?xml version="1.0" encoding="UTF-8"?>
<locateServiceResponse>
<service name="AdServer">ad.madserver.net</service>
<service name="ImpressionServer">imp.madserver.net</service>
<service name="MediaServer">media.madserver.net</service>
</locateServiceResponse>
0
The most interesting part of this response was probably the server entitled 'ImpressionServer'. The next action from the client was to register some kind of 'session' with the madserver. Our game client sent the version and game name (again), as well as a signature and some long string called etoken.
The sig was 32 characters long, typically an MD5 hash. The etoken looked base64 encoded. After reversing the base64, we were left with a binary string. In hex, it was 256 chars in length. Taking a rough guess, this is probably a SHA2-256 hash, or any other hash that is the same length. This is a fairly tough hash for whatever data our client is sending. Being a one-way hash, there is no easy way to find the contents of the message.
The madserver responds with another md5 signature, and a couple of ids. A session id, and more alarmingly, a gamer id. We can only theorise that the session id maps to an actual game being played, and the gamer id represents each gamer in that session. This way multi-player games should be handled quite sufficiently.
POST http://38.119.38.151/adsrv/openSession HTTP/1.1
User-Agent: Adclient/1.0 Massive Inc.
Accept-Encoding: deflate
Proxy-Connection: Keep-Alive
Content-Type: text/xml
Host: 38.119.38.151
Content-Length: 333
<openSessionRequest sig="78ac07a38f33274078f90c1673e4ce99">
<version>1.9.176.000000</version>
<skuName>swat_4_pc_na</skuName>
<etoken>Fi8QvnWYFL3sR92vLDqC34Rcb1NQRtkSwxY6esKzuM+QowZFvj1nvr/SNmBWJ/11JK+e8GFrEF0ZjO2Fv2NKFr+vGujewvSyHj6PAFZLuw+fILRYeTLSxtrB7TpjqiMVYLxrRnBaTiN8Jvi1NLfONCzjXI25laocQgIb1shN2P0=</etoken>
</openSessionRequest>
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Sat, 02 Jul 2005 23:46:11 GMT
Server: Apache-Coyote/1.1
d4
<?xml version="1.0" encoding="UTF-8"?>
<openSessionResponse sig="c4389d1c0d1ac5a8c031869cca52e5c9">
<sessionId>5244540</sessionId>
<gamerId>5097365</gamerId>
<startTime>1120347971857</startTime>
</openSessionResponse>
0
Now that our game had started a session with the madserver, we were expecting things to tail off a bit. Maybe it was just going to download some images, and put them in whatever pre-defined places on the map. But things went differently.
The next endpoint 'enterZone' was contacted by our game client. Another md5 signature was sent, along with the session and gamer id, as well as the timestamp. The new thing here was the name of level we were playing. The response from the server this time was quite large. In XML, it sent our client 3 major data structures:
The MediaList was a bunch of media file references. Each one had an id, name, size and checksum, the latter supposedly used for validation after downloading the media file from the server. An url was also provided detailing where to fetch the file. And the final thing, was a 'crex' entry. The crex entry isnt quite so obvious in terms of its purpose. It seems to define what the minimum rendered size of the advert can be in the game, as well as how long it must appear in the game for. The minimum angle might be what the least acceptable angle for display in the game is, assuming a game that does not give the player full control over position in the game, such as a racing game.
The MediaTarget structure contained names of possible places in the game that posters could be mounted, as well as what poster to mount. This kind of goes against the theory of the 'crex' entry mentioned earlier, as the game is given a more 'static' idea about where to place adverts.
MediaZones seem to define what adverts should be used in the current 'zone'. In this case the zone seems to be the entire level of the game, but in larger games such as MMORPGS, these zones could refer to particular areas of a level or map.
POST http://38.119.38.151/adsrv/enterZone HTTP/1.1
User-Agent: Adclient/1.0 Massive Inc.
Accept-Encoding: deflate
Proxy-Connection: Keep-Alive
Content-Type: text/xml
Host: 38.119.38.151
Content-Length: 208
<enterZoneRequest sig="64735e9d50787816840f5a700bc82db0">
<sessionId>5244540</sessionId>
<gamerId>5097365</gamerId>
<zoneName>SP-ConvenienceStore</zoneName>
<timestamp>1120347971857</timestamp>
</enterZoneRequest>
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Sat, 02 Jul 2005 23:46:11 GMT
Server: Apache-Coyote/1.1
1644
<?xml version="1.0" encoding="UTF-8"?>
<enterZoneResponse sig="4e94632c2cca5333434670c7fd4400d4">
<mediaList>
<media>
<id>4089</id>
<name>Coke_c_128x256.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>344d99d07bda2ceecf79720d59d58ae9</md5sum>
<urlpath>/187/1631/Coke_c_128x256.dds</urlpath>
<crex>
<id>1631</id>
<minSize>100</minSize>
<minDuration>5000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
<media>
<id>4327</id>
<name>SWAT_GameFly_256x128.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>5bf9e25025e46a5d78b08f0b9d3c0dd7</md5sum>
<urlpath>/190/1686/SWAT_GameFly_256x128.dds</urlpath>
<crex>
<id>1686</id>
<minSize>100</minSize>
<minDuration>5000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
<media>
<id>4323</id>
<name>SWAT_GameFly_128x256.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>07af9f6b96e3574aadcb71943850862d</md5sum>
<urlpath>/190/1685/SWAT_GameFly_128x256.dds</urlpath>
<crex>
<id>1685</id>
<minSize>100</minSize>
<minDuration>5000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
<media>
<id>4186</id>
<name>sodasign_coke_J_256x128.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>c58a7ca686852e03cb17f055a5ec4634</md5sum>
<urlpath>/187/1658/sodasign_coke_J_256x128.dds</urlpath>
<crex>
<id>1658</id>
<minSize>100</minSize>
<minDuration>5000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
<media>
<id>3807</id>
<name>Galactica_ad1_128x256.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>b9c87a44dc2408f1ddfe825c65a178a5</md5sum>
<urlpath>/165/1528/Galactica_ad1_128x256.dds</urlpath>
<crex>
<id>1528</id>
<minSize>100</minSize>
<minDuration>8000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
<media>
<id>4228</id>
<name>sodamachine_coke3_256x128.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>416404258674b64b219e2f5583663584</md5sum>
<urlpath>/187/1657/sodamachine_coke3_256x128.dds</urlpath>
<crex>
<id>1657</id>
<minSize>100</minSize>
<minDuration>5000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
<media>
<id>4300</id>
<name>Tripping_1x2_128x256.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>c45ec9cce80827c863bf7df83097b954</md5sum>
<urlpath>/188/1679/Tripping_1x2_128x256.dds</urlpath>
<crex>
<id>1679</id>
<minSize>100</minSize>
<minDuration>5000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
<media>
<id>4310</id>
<name>Tripping_2x1_256x128.dds</name>
<mimeType>image/dxt3</mimeType>
<size>43856</size>
<md5sum>a274293441f1d54b19dc81361412ce65</md5sum>
<urlpath>/188/1681/Tripping_2x1_256x128.dds</urlpath>
<crex>
<id>1681</id>
<minSize>100</minSize>
<minDuration>3000</minDuration>
<minAngle>50</minAngle>
</crex>
</media>
</mediaList>
<mediaTargets>
<mediaTarget>
<id>800</id>
<name>poster15</name>
<mediaRef id="4323"/>
</mediaTarget>
<mediaTarget>
<id>794</id>
<name>poster9</name>
<mediaRef id="4310"/>
</mediaTarget>
<mediaTarget>
<id>793</id>
<name>poster8</name>
<mediaRef id="4300"/>
</mediaTarget>
<mediaTarget>
<id>799</id>
<name>poster14</name>
<mediaRef id="4300"/>
</mediaTarget>
<mediaTarget>
<id>791</id>
<name>poster6</name>
<mediaRef id="4300"/>
</mediaTarget>
<mediaTarget>
<id>804</id>
<name>poster19</name>
<mediaRef id="4327"/>
</mediaTarget>
<mediaTarget>
<id>803</id>
<name>poster18</name>
</mediaTarget>
<mediaTarget>
<id>783</id>
<name>cereal1</name>
</mediaTarget>
<mediaTarget>
<id>808</id>
<name>sodasign3</name>
</mediaTarget>
<mediaTarget>
<id>788</id>
<name>poster3</name>
<mediaRef id="4323"/>
</mediaTarget>
<mediaTarget>
<id>796</id>
<name>poster11</name>
</mediaTarget>
<mediaTarget>
<id>786</id>
<name>poster1</name>
<mediaRef id="4300"/>
</mediaTarget>
<mediaTarget>
<id>809</id>
<name>sodasign4</name>
<mediaRef id="4228"/>
</mediaTarget>
<mediaTarget>
<id>784</id>
<name>cereal2</name>
</mediaTarget>
<mediaTarget>
<id>790</id>
<name>poster5</name>
<mediaRef id="4323"/>
</mediaTarget>
<mediaTarget>
<id>789</id>
<name>poster4</name>
<mediaRef id="3807"/>
</mediaTarget>
<mediaTarget>
<id>805</id>
<name>sodamachine1</name>
<mediaRef id="4228"/>
</mediaTarget>
<mediaTarget>
<id>806</id>
<name>sodasign1</name>
<mediaRef id="4186"/>
</mediaTarget>
<mediaTarget>
<id>801</id>
<name>poster16</name>
<mediaRef id="4300"/>
</mediaTarget>
<mediaTarget>
<id>787</id>
<name>poster2</name>
<mediaRef id="4323"/>
</mediaTarget>
<mediaTarget>
<id>811</id>
<name>sodasign6</name>
</mediaTarget>
<mediaTarget>
<id>792</id>
<name>poster7</name>
<mediaRef id="4089"/>
</mediaTarget>
<mediaTarget>
<id>797</id>
<name>poster12</name>
</mediaTarget>
<mediaTarget>
<id>810</id>
<name>sodasign5</name>
<mediaRef id="4186"/>
</mediaTarget>
<mediaTarget>
<id>785</id>
<name>cereal3</name>
</mediaTarget>
<mediaTarget>
<id>795</id>
<name>poster10</name>
</mediaTarget>
<mediaTarget>
<id>798</id>
<name>poster13</name>
<mediaRef id="4310"/>
</mediaTarget>
<mediaTarget>
<id>802</id>
<name>poster17</name>
</mediaTarget>
<mediaTarget>
<id>807</id>
<name>sodasign2</name>
<mediaRef id="4186"/>
</mediaTarget>
</mediaTargets>
<mediaZones>
<mediaZone>
<id>70</id>
<name>SP-ConvenienceStore</name>
<mediaTargetRef id="800"/>
<mediaTargetRef id="794"/>
<mediaTargetRef id="793"/>
<mediaTargetRef id="799"/>
<mediaTargetRef id="791"/>
<mediaTargetRef id="804"/>
<mediaTargetRef id="803"/>
<mediaTargetRef id="783"/>
<mediaTargetRef id="808"/>
<mediaTargetRef id="788"/>
<mediaTargetRef id="796"/>
<mediaTargetRef id="786"/>
<mediaTargetRef id="809"/>
<mediaTargetRef id="784"/>
<mediaTargetRef id="790"/>
<mediaTargetRef id="789"/>
<mediaTargetRef id="805"/>
<mediaTargetRef id="806"/>
<mediaTargetRef id="801"/>
<mediaTargetRef id="787"/>
<mediaTargetRef id="811"/>
<mediaTargetRef id="792"/>
<mediaTargetRef id="797"/>
<mediaTargetRef id="810"/>
<mediaTargetRef id="785"/>
<mediaTargetRef id="795"/>
<mediaTargetRef id="798"/>
<mediaTargetRef id="802"/>
<mediaTargetRef id="807"/>
</mediaZone>
</mediaZones>
</enterZoneResponse>
0
After this point, the game client fetches all of the textures listed from the images server. Our client fetched DDS files, which are game textures used by DirectX. If you don't want to see the adverts, look away now.
The rest of the adverts that were downloaded for this single level are here.
The client requests to fetch these images are shown here.
Everything now plays along with little noise coming from the madservers. Until we exit the game. A single HTTP request is sent by our game client to signify that the game has ended. A timestamp and our session/gamer ids are sent. This sort of information gives the advertisers a more complete idea of how long we play, and at what times of the day, and enough information for them to calculate any patterns. They could even determine what levels are more popular and maybe charge more for advertisers to get advertising space in these levels.
POST http://38.119.38.151/adsrv/closeSession HTTP/1.1
User-Agent: Adclient/1.0 Massive Inc.
Accept-Encoding: deflate
Proxy-Connection: Keep-Alive
Content-Type: text/xml
Host: 38.119.38.151
Content-Length: 174
<closeSessionRequest sig="eea7756a7fd93649ec8f4898ce9a0303">
<sessionId>5244540</sessionId>
<gamerId>5097365</gamerId>
<timestamp>1120348101661</timestamp>
</closeSessionRequest>
HTTP/1.1 200 OK
Content-Length: 0
Date: Sat, 02 Jul 2005 23:48:21 GMT
Server: Apache-Coyote/1.1
The most shocking part was next. The client contacted madserver to tell the advertisers how long the gamer spent with each advert in their view. This is mapped to the gamer id, so they know which player in the game saw the advert, and when, for how long, and from how far away (by virtue of the size attribute). Even the average viewing angle is passed back.
POST http://38.119.38.152/impsrv/impressionUpdate HTTP/1.1
User-Agent: Adclient/1.0 Massive Inc.
Accept-Encoding: deflate
Proxy-Connection: Keep-Alive
Content-Type: text/xml
Host: 38.119.38.152
Content-Length: 2542
<impressionUpdate sig="0a40e36280df5fd26a60728163da5598">
<sessionId>5244540</sessionId>
<gamerId>5097365</gamerId>
<timestamp>1120348100907</timestamp>
<impressionRecords>
<impressionRecord type="view">
<timestamp>1120347977797</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>1060</duration>
<avgSize>88</avgSize>
<avgAngle>0.963</avgAngle>
</impressionRecord>
<impressionRecord type="impression">
<timestamp>1120347980104</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>792</duration>
<avgSize>117</avgSize>
<avgAngle>0.905</avgAngle>
</impressionRecord>
<impressionRecord type="view">
<timestamp>1120347981673</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>1430</duration>
<avgSize>128</avgSize>
<avgAngle>0.22</avgAngle>
</impressionRecord>
<impressionRecord type="impression">
<timestamp>1120347983346</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>1418</duration>
<avgSize>170</avgSize>
<avgAngle>0.945</avgAngle>
</impressionRecord>
<impressionRecord type="impression">
<timestamp>1120347987230</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>3105</duration>
<avgSize>408</avgSize>
<avgAngle>0.967</avgAngle>
</impressionRecord>
<impressionRecord type="view">
<timestamp>1120347987944</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>597</duration>
<avgSize>804</avgSize>
<avgAngle>0.494</avgAngle>
</impressionRecord>
<impressionRecord type="impression">
<timestamp>1120347993557</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>5485</duration>
<avgSize>1315</avgSize>
<avgAngle>0.957</avgAngle>
</impressionRecord>
<impressionRecord type="view">
<timestamp>1120348076355</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>374</duration>
<avgSize>1678</avgSize>
<avgAngle>9.71e-002</avgAngle>
</impressionRecord>
<impressionRecord type="impression">
<timestamp>1120348076754</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>270</duration>
<avgSize>1824</avgSize>
<avgAngle>0.892</avgAngle>
</impressionRecord>
<impressionRecord type="view">
<timestamp>1120348077560</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>670</duration>
<avgSize>2816</avgSize>
<avgAngle>0.164</avgAngle>
</impressionRecord>
<impressionRecord type="impression">
<timestamp>1120348078043</timestamp>
<crexId>1685</crexId>
<inventoryID>800</inventoryID>
<duration>363</duration>
<avgSize>1371</avgSize>
<avgAngle>0.84</avgAngle>
</impressionRecord>
</impressionRecords>
</impressionUpdate>
If this stuff is just first generation, then who knows how invasive and/or detailed this technology could become. It should be made clear that this advertising format doesnt just simply mean putting posters on the walls of levels, but also objects, such as vending machines in the game could be branded by advertisers. For further reading, check out http://www.massiveincorporated.com/. Also take note of the plethora of game publishers they have already signed up, as displayed at the base of their website.
In order to prevent this 'functionality', the server can be prevented from being contacted by placing the following lines in either /etc/hosts on UNIX, or %WINDIR%\system32\drivers\etc\hosts on Windows.
127.0.0.1 madserver.net
127.0.0.1 ad.madserver.net
127.0.0.1 imp.madserver.net
127.0.0.1 media.madserver.net