Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Wireshark 4.2 and recent Spotify #12

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dissect/dissect.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -eux

exec wireshark-gtk $1 \
exec wireshark $1 \
-X lua_script:protobuf_dissector/protobuf.lua \
-X lua_script:spotify.lua \
-X lua_script:mercury.lua \
Expand Down
4 changes: 2 additions & 2 deletions dissect/protobuf_dissector/modules/ast/ast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ if not _G['protbuf_dissector'] then return end


-- make sure wireshark is new enough
if not GRegex then
return nil, "Wireshark is too old: no GRegex library"
if not rex_pcre2 then
return nil, "Wireshark is too old: no rex_pcre2 library"
end


Expand Down
4 changes: 2 additions & 2 deletions dissect/protobuf_dissector/modules/compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ if not _G['protbuf_dissector'] then return end


-- make sure wireshark is new enough
if not GRegex then
return nil, "Wireshark is too old: no GRegex library"
if not rex_pcre2 then
return nil, "Wireshark is too old: no rex_pcre2 library"
end


Expand Down
4 changes: 2 additions & 2 deletions dissect/protobuf_dissector/modules/front_end/cursor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ function Cursor:nextLine()
end


local wspace_rgx = GRegex.new("^([ \t]++)|^(\n)", "s")
local chunk_rgx = GRegex.new("^([^\n]++)|^(\n)", "s")
local wspace_rgx = rex_pcre2.new("^([ \t]++)|^(\n)", "s")
local chunk_rgx = rex_pcre2.new("^([^\n]++)|^(\n)", "s")
local len = string.len
local sub = string.sub

Expand Down
6 changes: 3 additions & 3 deletions dissect/protobuf_dissector/modules/front_end/file_reader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ if not _G['protbuf_dissector'] then return end


-- make sure wireshark is new enough
if not GRegex then
return nil, "Wireshark is too old: no GRegex library"
if not rex_pcre2 then
return nil, "Wireshark is too old: no rex_pcre2 library"
end


Expand All @@ -46,7 +46,7 @@ end
function FileReader:read(name)
end

local filepath_rgx = GRegex.new("^(.*)([^/\\\\]+)$", "U")
local filepath_rgx = rex_pcre2.new("^(.*)([^/\\\\]+)$", "U")


--------------------------------------------------------------------------------
Expand Down
14 changes: 7 additions & 7 deletions dissect/protobuf_dissector/modules/front_end/lexer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ if not _G['protbuf_dissector'] then return end


-- make sure wireshark is new enough
if not GRegex then
return nil, "Wireshark is too old: no GRegex library"
if not rex_pcre2 then
return nil, "Wireshark is too old: no rex_pcre2 library"
end

local Settings = require "settings"
Expand Down Expand Up @@ -83,7 +83,7 @@ function Lexer:generateVariableMatchPatterns()
for _, token in ipairs(self.Syntax.token_info.VARIABLE) do
if not token.value then
dprint2("creating new variable regex for: ", token.ttype)
tbl[token.ttype] = GRegex.new("^" .. token.pattern .. "$")
tbl[token.ttype] = rex_pcre2.new("^" .. token.pattern .. "$")
end
end
self.variable_token_regexes = tbl
Expand All @@ -97,7 +97,7 @@ function Lexer:generateStringMatchPatterns()
for _, token in ipairs(self.Syntax.token_info[category]) do
if not token.value then
dprint2("creating new string regex for: ", token.ttype)
tbl[token.ttype] = GRegex.new("^" .. token.pattern .. "$")
tbl[token.ttype] = rex_pcre2.new("^" .. token.pattern .. "$")
end
end
end
Expand All @@ -112,7 +112,7 @@ function Lexer:generateSkipPatterns()
for _, token in ipairs(self.Syntax.token_info[category]) do
if token.skip then
dprint2("creating new skip regex for: ", token.ttype)
tbl[token.ttype] = GRegex.new(token.skip, token.skip_flags)
tbl[token.ttype] = rex_pcre2.new(token.skip, token.skip_flags)
if not tbl[token.ttype] then
error("Could not compile regex for: " .. token.ttype)
end
Expand Down Expand Up @@ -173,7 +173,7 @@ function Lexer:compilePatterns()
local t = {}
for ttype, str in pairs(self.pattern_strings) do
-- Gregex will raise a Lua error if this doesn't succeed
t[ttype] = GRegex.new(str)
t[ttype] = rex_pcre2.new(str)
end
self.pattern = t
end
Expand Down Expand Up @@ -226,7 +226,7 @@ function Lexer:tokenize(chunk, filename)
-- normally we'd use Gregex.gmatch() in a for-loop to do this, but we need to
-- skip quoted strings inside the for-loop, and that can't be done with gmatch;
-- so we're going to do it the slower way by creating lots of substrings :(
-- also, GRegex.match() doesn't consider subsequent iterations to match a
-- also, rex_pcre2.match() doesn't consider subsequent iterations to match a
-- pattern with a "^" anchor, which is unfortunate, so by creating substrings
-- we get to use "^" to prevent skipping unmatched words/tokens

Expand Down
4 changes: 2 additions & 2 deletions dissect/protobuf_dissector/modules/front_end/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ if not _G['protbuf_dissector'] then return end


-- make sure wireshark is new enough
if not GRegex then
return nil, "Wireshark is too old: no GRegex library"
if not rex_pcre2 then
return nil, "Wireshark is too old: no rex_pcre2 library"
end


Expand Down
4 changes: 2 additions & 2 deletions dissect/protobuf_dissector/modules/front_end/token.lua
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ function Token:isNativeType()
end


local hex_rgx = GRegex.new("^0[xX]([a-fA-F0-9]+)$")
local oct_rgx = GRegex.new("^0([0-7]+)$")
local hex_rgx = rex_pcre2.new("^0[xX]([a-fA-F0-9]+)$")
local oct_rgx = rex_pcre2.new("^0([0-7]+)$")


function Token:convertToNumber()
Expand Down
10 changes: 8 additions & 2 deletions dissect/protobuf_dissector/modules/prefs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
if not _G['protbuf_dissector'] then return end


-- make sure wireshark is new enough
if not rex_pcre2 then
return nil, "Wireshark is too old: no rex_pcre2 library"
end


local Settings = require "settings"
local dprint = Settings.dprint
local dprint2 = Settings.dprint2
Expand All @@ -24,10 +30,10 @@ local derror = Settings.derror
local Prefs = {}


local range_rgx = GRegex.new("([0-9]+)\\s*(?:-\\s*([0-9]+))?")
local range_rgx = rex_pcre2.new("([0-9]+)\\s*(?:-\\s*([0-9]+))?")
local function getRange(range)
local t = {}
for first, second in GRegex.gmatch(range, range_rgx) do
for first, second in rex_pcre2.gmatch(range, range_rgx) do
if first then
first = tonumber(first)
if second then
Expand Down
4 changes: 2 additions & 2 deletions dissect/protobuf_dissector/modules/syntax.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ if not _G['protbuf_dissector'] then return end


-- make sure wireshark is new enough
if not GRegex then
error("Wireshark is too old: no GRegex library - upgrade to version 1.12 or higher.")
if not rex_pcre2 then
error("Wireshark is too old: no rex_pcre2 library - upgrade to version 1.12 or higher.")
end


Expand Down
3 changes: 2 additions & 1 deletion dissect/protobuf_dissector/protobuf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ _G['protbuf_dissector'] = {


-- help wireshark find our modules
package.prepend_path("modules")
-- package.prepend_path("modules")
package.path = __DIR__ .. __DIR_SEPARATOR__ .. "modules" .. __DIR_SEPARATOR__ .. "?.lua;" .. package.path


-- load our settings
Expand Down
44 changes: 39 additions & 5 deletions dissect/spotify.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,50 @@ function spotify.dissector(buffer, pinfo, tree)
subtree:add(f.cmd, cmd)
subtree:add(f.length, length)

if cmd:uint() == 0xab then
if false then
elseif cmd:uint() == 0x02 then
pinfo.cols.info = "SecretBlock"
elseif cmd:uint() == 0x04 then
pinfo.cols.info = "Ping: " .. payload(0, 4):uint()
elseif cmd:uint() == 0x08 then
pinfo.cols.info = "StreamChunk"
elseif cmd:uint() == 0x09 then
pinfo.cols.info = "StreamChunkRes"
elseif cmd:uint() == 0x0a then
pinfo.cols.info = "ChannelError"
elseif cmd:uint() == 0x0b then
pinfo.cols.info = "ChannelAbort"
elseif cmd:uint() == 0x0c then
pinfo.cols.info = "RequestKey: " .. payload()
elseif cmd:uint() == 0x0d then
pinfo.cols.info = "AesKey: " .. payload()
elseif cmd:uint() == 0x0e then
pinfo.cols.info = "AesKeyError"
elseif cmd:uint() == 0x19 then
pinfo.cols.info = "Image"
elseif cmd:uint() == 0x1b then
pinfo.cols.info = "CountryCode: " .. payload()
elseif cmd:uint() == 0x49 then
pinfo.cols.info = "Pong"
elseif cmd:uint() == 0x4a then
pinfo.cols.info = "PongAck"
elseif cmd:uint() == 0x4b then
pinfo.cols.info = "Pause"
-- elseif cmd:uint() == 0x50 then
-- pinfo.cols.info = "ProductInfo"
elseif cmd:uint() == 0x69 then
pinfo.cols.info = "LegacyWelcome"
elseif cmd:uint() == 0x76 then
pinfo.cols.info = "LicenseVersion"
elseif cmd:uint() == 0xab then
DissectorTable.get("protobuf"):try("ClientResponseEncrypted", payload, pinfo, tree)
pinfo.cols.info = "Login"
elseif cmd:uint() == 0xac then
DissectorTable.get("protobuf"):try("APWelcome", payload, pinfo, tree)
pinfo.cols.info = "APWelcome"
elseif cmd:uint() == 0xad then
DissectorTable.get("protobuf"):try("APLoginFailed", payload, pinfo, tree)
elseif cmd:uint() == 0x0c then
pinfo.cols.info = "Req AudioKey: " .. payload()
elseif cmd:uint() == 0x0d then
pinfo.cols.info = "Got AudioKey: " .. payload()
pinfo.cols.info = "APLoginFailed"
else
DissectorTable.get("spotify.cmd"):try(cmd:uint(), payload, pinfo, tree)
end
Expand Down
66 changes: 46 additions & 20 deletions dump/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <pthread.h>
#include <dlfcn.h>
#include <assert.h>
#include <arpa/inet.h>

#ifdef __APPLE__
#include <mach-o/dyld.h>
Expand All @@ -24,23 +25,48 @@
#include "pcap.h"
#include "shn.h"

static int init_pcap_file();
static void patch_function(void *src, const void *dst);
static void *memrmem(const void *haystack, size_t haystack_size,
const void *needle, size_t needle_size);

#define DIRECTION_SEND 0
#define DIRECTION_RECV 1

static int dump_fd;
static int dump_fd = -1;

/*
* The capture file is opened lazily just before the first write. This prevents
* writing empty pcap files which contain only the header from subprocesses
* that don't handle the shn en/decryption.
*/
static int init_pcap_file() {
if (dump_fd == -1) {
const size_t FNAME_CAP = 64;
char fname[FNAME_CAP];
pid_t pid = getpid();
snprintf(fname, FNAME_CAP, "dump-%ld.pcap", (long) pid);

dump_fd = open(fname, O_CREAT | O_RDWR | O_TRUNC, 0644);

pcap_write_header(dump_fd, PCAP_DLT_USER0);
}

return dump_fd;
}

static void my_shn_encrypt(shn_ctx * c, UCHAR * buf, int nbytes) {
struct timeval tv;
gettimeofday(&tv, NULL);
pcap_write_packet_header(dump_fd, &tv, 1 + nbytes);
int fd = init_pcap_file();

if (fd > 0) {
struct timeval tv;
gettimeofday(&tv, NULL);
pcap_write_packet_header(fd, &tv, 1 + nbytes);

uint8_t direction = DIRECTION_SEND;
write(dump_fd, &direction, 1);
write(dump_fd, buf, nbytes);
uint8_t direction = DIRECTION_SEND;
write(fd, &direction, 1);
write(fd, buf, nbytes);
}

shn_encrypt(c, buf, nbytes);
}
Expand All @@ -58,14 +84,17 @@ static void my_shn_decrypt(shn_ctx * c, UCHAR * buf, int nbytes) {
memcpy(&header, buf, 3);
} else {
if (nbytes == ntohs(header.length)) {
struct timeval tv;
gettimeofday(&tv, NULL);
pcap_write_packet_header(dump_fd, &tv, 4 + nbytes);

uint8_t direction = DIRECTION_RECV;
write(dump_fd, &direction, 1);
write(dump_fd, &header, 3);
write(dump_fd, buf, nbytes);
int fd = init_pcap_file();
if (fd > 0) {
struct timeval tv;
gettimeofday(&tv, NULL);
pcap_write_packet_header(fd, &tv, 4 + nbytes);

uint8_t direction = DIRECTION_RECV;
write(fd, &direction, 1);
write(fd, &header, 3);
write(fd, buf, nbytes);
}
}

header.cmd = 0;
Expand Down Expand Up @@ -103,11 +132,8 @@ static void find_shn_heuristic(void *text_start, size_t text_size, void **p_shn_
}

static void patch_shn(void) {
dump_fd = open("dump.pcap", O_CREAT | O_RDWR | O_TRUNC, 0644);

pcap_write_header(dump_fd, PCAP_DLT_USER0);

printf("Patching ...\n");
pid_t pid = getpid();
printf("Patching ... (PID = %ld)\n", (long) pid);

size_t text_size = 0;
void *text_start = NULL;
Expand Down