- Apr 25, 2015
- 1,845
- 2
- 2,199
- 327
A better option may be found here https://r4p3.net/threads/c-ts3hook.5671/#post-50075
---
The following information is mostly from: http://www.blizzhackers.cc/viewtopic.php?p=4597974
Edit your config.lua file, at the bottom where you see:
Replace that if statement with:
...
Good freaking luck, I hope you can figure something out and share it here lol --- I am working with it to try to figure out what I can do.
---
- Download this archive, extract it somewhere: https://r4p3.github.io/zips/yumBBQ3.0.3.zip
- Launch the 32-bit TeamSpeak 3 client.
- Execute START.bat as an administrator.
- Find the TeamSpeak 3 process ID, type it in and press enter.
- Connect to a server, watch the command-line console populate with data.
The following information is mostly from: http://www.blizzhackers.cc/viewtopic.php?p=4597974
Edit your config.lua file, at the bottom where you see:
Code:
if recv > 0 then
printf("[RECV] 0x%X", recv);
hexdump(buf, recv);
end
Code:
if recv > 0xF then
dbg.mbpAdd(buf + 0xF, function(bp) bp.print(); bp.del(); end, 1, dbg.MBP_WRITE);
end
This automatically installs a one-shot memory breakpoint when data is received from network. On trigger it will execute two things: `dbg.print()` and `bp.del()`. The `dbg.print()` will print out the cuurent context and disassembly when the memory breakpoint fires. The `bp.del()` will delete the memory breakpoint again, so it behaves in a one shot manner… As easy as pie, isn’t it?
Now, when connected we attach our code again and click around the channels to trigger a network read that is larger than the small keepalives. Eventually we will see the decoding function context print that we just installed automatically … :]
Yay, now we see theres a little loop that is writing to our buffer… it jumps back to 0x009037D0 on a certain loop condition. lets see the whole loop. just typedbg.asm(0x009037D0, 7) into the Lua console and we will get:
Code:
0x009037D0 8b3c02 mov edi, [edx+eax]
0x009037D3 33bc0e94000000 xor edi, [esi+ecx+0x94]
0x009037DA 83c104 add ecx, 0x4
0x009037DD 8938 mov [eax], edi
0x009037DF 83c004 add eax, 0x4
0x009037E2 3b4e04 cmp ecx, [esi+0x4]
0x009037E5 7ce9 jl 0x9037d0
The XOR operation at 0x009037D3 is also a very good hint that this is part of a decryption/encryption routine. Pretty straight forward this little snippet does the following things:
Sure there is more to understand about the streamsource and maybe the initial key, but for the time being that’s it. Lets just try to get the data displayed decoded as it passes through…
- load the next DWORD to EDI (from the buffer pointer EDX)
- decode the data using XOR and a stream source object or class (ECX+0×94)
- increment the streamsource ECX by 0×4 (sizeof(DWORD))
- write out the decrypted data to the original buffer (we catched that instruction earlier)
- also increment output pointer EAX (used in the last instruction)
- compare abort condition and repeat until finished
STEP 3 – Dumping the decoded data
When we look closer at the previous Context print (last console screenshot) we notice a Code reference in the current Stack
"0x00903894 : CODE 0x00400000 ts3client_win32.exe+0x00503894()". Code references in the stack are most likely return addresses of function calls. Lets investigate that by installing a debug codecave: codecave.debug(0x00903894)
… the decrypted stuff!! The following entry in the stack should be the buffer size (0×21). When we look at the previous match, we can see that this cave is also executed for encrypting things (the yellow mark in the screenshot).
Now we can make a suitable cave that dumps us any data: post-decrypted when received and pre-encryption when send. Lets reload our console so codecaves and BPs are cleared. Maybe you also want to remove the hexdumps and auto breakpoint thing set in Step1. To find the entrypoint into the crypt function we can query the “return address – 0×5″. In x86 assembler the returns address always points to the CALL+0×5 bytes, because thats the size of the call instruction itself. So we can examine the function call to the crypt function to get the entrypoint:
dbg.asm(0x00903894 - 0x5) -> "call dword 0x903640".
What we did here is to determine the first instruction of the encryption/decryption function. This can also be done with OllyDbg by just looking there. The next versions of mmBBQ will eventually include more disassembly functions.
Now lets place a debug cave at there: codecave.debug(0x903640)
In the same way we detected the return address before we now see two calls to this address. The one we already knew for decrypting 0x00903894 and the call for encrypting data 0x0090F81B.
Okay, now we just make Lua codecaves there, one that is executed on return (decryption) and one that is executed on call (encryption). We can differ them by looking at the return address. The approach is the same in the hooking of WSARevcFrom in Step1.
Code:
local function decrpyt_hook(context)
local ret = context.arg32(0, "uint32_t");
if ret == 0x00903894 then
printf("RECV_CRYPT: %s", str(context.arg32(2, "char*"), context.arg32(3, "int")));
end
end
codecave.inject(nil, 0x903640, decrpyt_hook, codecave.INTERCEPT_RETURN)
local function encrypt_hook(context)
local ret = context.arg32(0, "uint32_t");
if ret == 0x0090F81B then
printf("SEND_CRYPT: %s", str(context.arg32(2, "char*"), context.arg32(3, "int")));
end
end
codecave.inject(nil, 0x903640, encrypt_hook, codecave.INTERCEPT_PRE)
Doing so will result in the fully decrypted control port protocol. So we are finished here. Funny thing is that ts3 uses an ASCII plain text protocol which is not very common. Maybe its easier to debug. I learned to design protocols by sending codes and compressed data with little overhead.
Code:
SEND_CRYPT: clientinitiv alpha=lk34sdLIASjdka== omega=LIJd5ldk2LJDjalK1UaF411oon\/V82tqmaM0p8dr6qKmQp9RiK+sCID7
W98OrGCKD+c8erzHWyAaASsxc3l2kkl\/TGxQbV3 ip=123.123.123.123
RECV_CRYPT: initivexpand alpha=lk34sdLIASjdka== beta=wp82a901Q05O8qg== omega=LIJd5ldk2LJDjalK1UaF411oon\/Afh\/GOSJLsAwdV
e\/BUbnFL0sYc49hSfCIDB\/37L0EZb0WaI2pC4FAd5gbvzgp2ZjsSrvnOSASMav
SEND_CRYPT: clientinit client_nickname=will client_version=3.0.8.1\s[Build:\s1343657352] client_platform=Windows client_
input_hardware=1 client_output_hardware=1 client_default_channel client_default_channel_password client_server_password=
fHzk+43klKq2SnfCS1B3r+HCakI= client_key_offset=291 client_nickname_phonetic client_default_token hwid=198b8902374b0973c1
8as097aft3,01h4m5cmpw8qytc83
RECV_CRYPT: initserver virtualserver_name=FooBarServer virtualserver_welcomemessage=Welcome\sto\sTeamSpeak,\sc
heck\s[URL]www.teamspeak.com[\/URL]\sfor\slatest\sinformation virtualserver_platform=Linux virtualserver_version=3.0.5\s
[Build:\s1335655378] virtualserver_maxclients=32 virtualserver_created=1337381436 virtualserver_codec_encryption_mode=0
virtualserver_hostmessage virtualserver_hostmessage_mode=0 virtualserver_default_server_group=8 virtualserver_default_ch
annel_group=8 virtual
RECV_CRYPT: server_hostbanner_url virtualserver_hostbanner_gfx_url virtualserver_hostbanner_gfx_interval=0 virtualserver
_priority_speaker_dimm_modificator=-18.0000 virtualserver_id=1 virtualserver_hostbutton_tooltip virtualserver_hostbutton
_url virtualserver_hostbutton_gfx_url virtualserver_name_phonetic virtualserver_icon_id=0 virtualserver_ip virtualserver
_ask_for_privilegekey=0 virtualserver_hostbanner_mode=0 acn=will aclid=1 pv=6 lt=0 client_talk_power=75 client_needed_se
rverquery_view_power=
RECV_CRYPT: channellist cid=4 cpid=0 channel_name=#1337 channel_topic channel_codec=2 channel_codec_quality=7 channe
l_maxclients=-1 channel_maxfamilyclients=-1 channel_order=0 channel_flag_permanent=1 channel_flag_semi_permanent=0 chann
el_flag_default=1 channel_flag_password=0 channel_codec_latency_factor=1 channel_codec_is_unencrypted=1 channel_flag_max
clients_unlimited=1 channel_flag_maxfamilyclients_unlimited=1 channel_flag_maxfamilyclients_inherited=1 channel_needed_t
alk_power=0 channel_n
RECV_CRYPT: ame_phonetic channel_icon_id=0
...
SEND_CRYPT: channelgetdescription cid=4 return_code=1:d
RECV_CRYPT: notifychanneledited cid=4 channel_description reasonid=9
RECV_CRYPT: error id=0 msg=ok return_code=1:d
SEND_CRYPT: clientmove cid=4 cpw clid=0 return_code=1:e
RECV_CRYPT: notifyclientmoved ctid=4 reasonid=0 clid=4
RECV_CRYPT: notifyclientchannelgroupchanged invokerid=0 invokername=Server cgid=8 cid=4 clid=4 cgi=4
RECV_CRYPT: error id=0 msg=ok return_code=1:e
SEND_CRYPT: connectioninfoautoupdate connection_server2client_packetloss_speech=0.0000 connection_server2client_packetloss_keepalive=0.0000 connection_server2client_packetloss_control=0.0000 connection_server2client_packetloss_total=0.0000
SEND_CRYPT: channelgetdescription cid=22 return_code=1:f
RECV_CRYPT: notifychanneledited cid=22 channel_description reasonid=9
RECV_CRYPT: error id=0 msg=ok return_code=1:f
SEND_CRYPT: clientmove cid=22 cpw clid=0 return_code=1:g
RECV_CRYPT: notifyclientmoved ctid=22 reasonid=0 clid=4
RECV_CRYPT: error id=0 msg=ok return_code=1:g
RECV_CRYPT: notifyclientchannelgroupchanged invokerid=0 invokername=Server cgid=8 cid=22 clid=4 cgi=22
...
Good freaking luck, I hope you can figure something out and share it here lol --- I am working with it to try to figure out what I can do.
Last edited: