radio_tool 0.2.1
Loading...
Searching...
No Matches
tyt_fw_sgl.cpp
1
18#include <radio_tool/fw/tyt_fw_sgl.hpp>
19#include <radio_tool/util.hpp>
20
21#include <random>
22#include <iterator>
23
24using namespace radio_tool::fw;
25
26constexpr auto HeaderLen = 0x400u;
27constexpr auto Header1Len = 0x10u;
28constexpr auto Header2Len = 0x67u;
29
30constexpr auto BinaryLenOffset = 0x06;
31constexpr auto GroupOffset = 0x32;
32constexpr auto ModelOffset = GroupOffset + 0x10;
33constexpr auto VersionOffset = ModelOffset + 0x08;
34constexpr auto KeyOffset = 0x5f;
35
36auto TYTSGLFW::Read(const std::string& file) -> void
37{
38 auto hdr = ReadHeader(file);
39
40 for (const auto& cfg : tyt::config::sgl::All) {
41 if (cfg.header.radio_group == hdr.radio_group) {
42 config = new TYTSGLRadioConfig(cfg.radio_model, hdr, cfg.cipher, cfg.cipher_len, cfg.xor_offset);
43 break;
44 }
45 }
46
47 if (config == nullptr) {
48 std::stringstream msg;
49 msg << "Radio model '" << hdr.radio_group << "' not supported!";
50
51 throw std::runtime_error(msg.str());
52 }
53
54 auto i = std::ifstream(file, std::ios_base::binary);
55 if (i.is_open())
56 {
57 i.seekg(0x400, i.beg);
58 i.seekg(hdr.binary_offset, i.cur);
59
60 data.resize(hdr.length);
61 i.read((char*)data.data(), hdr.length);
62
63 memory_ranges.push_back(std::pair<uint32_t, uint32_t>(0, data.size()));
64 }
65 i.close();
66}
67
68auto TYTSGLFW::Write(const std::string& file) -> void
69{
70 if (config == nullptr) {
71 throw std::runtime_error("No header set, cannot write firmware");
72 }
73 if (config->header.length != data.size()) {
74 throw std::runtime_error("Binary size mismatch, size in header does not match actual binary length");
75 }
76
77 std::ofstream fout(file, std::ios_base::binary);
78 if (fout.is_open())
79 {
80 auto header = config->header.Serialize();
81 fout.write((char*)header.data(), header.size());
82 fout.write((char*)data.data(), data.size());
83 }
84 fout.close();
85}
86
87auto TYTSGLFW::ToString() const -> std::string
88{
89 if (config == nullptr) {
90 throw std::runtime_error("No header set, cannot print firmware info");
91 }
92
93 std::stringstream out;
94 out << "== TYT SGL Firmware == " << std::endl
95 << "Radio: " << config->radio_model << std::endl
96 << config->header.ToString() << std::endl
97 << "Data Segments: " << std::endl;
98 auto n = 0;
99 for (const auto& m : memory_ranges)
100 {
101 out << " " << n++ << ": Length=0x" << std::setfill('0') << std::setw(8) << std::hex << m.second
102 << std::endl;
103 }
104 return out.str();
105}
106
107auto TYTSGLFW::ReadHeader(const std::string& file) -> const SGLHeader
108{
109 std::ifstream i;
110 i.open(file, i.binary);
111 if (i.is_open())
112 {
113 uint8_t header1[Header1Len];
114 i.read((char*)header1, Header1Len);
115
116 if (!std::equal(header1, header1 + 4, tyt::config::sgl::Magic.begin()))
117 {
118 throw std::runtime_error("Invalid SGL header magic");
119 }
120
121 for (auto x = 4;x < Header1Len;x++) {
122 header1[x] ^= tyt::config::sgl::Magic[x % 4];
123 }
124
125 auto sgl_version = std::stoi((char*)header1 + 9);
126 if (sgl_version != 1) {
127 std::stringstream msg("Invalid SGL version: ");
128 msg << sgl_version;
129 throw std::runtime_error(msg.str());
130 }
131
132 auto header2_offset = *(uint16_t*)(header1 + 12);
133
134 i.seekg(header2_offset, i.beg);
135 uint8_t header2[Header2Len];
136 i.read((char*)header2, Header2Len);
137
138 auto h2_xor = header1 + 14;
139 for (auto x = 0;x < Header2Len;x++) {
140 header2[x] ^= h2_xor[x % 2];
141 }
142
143 auto binary_offset = header1[11];
144 auto len = *(uint32_t*)(header2 + BinaryLenOffset);
145 auto group = std::string(header2 + GroupOffset, header2 + GroupOffset + 0x10);
146 auto model = std::string(header2 + ModelOffset, header2 + ModelOffset + 0x08);
147 auto version = std::string(header2 + VersionOffset, header2 + VersionOffset + 0x08);
148 auto key = std::string(header2 + KeyOffset, header2 + KeyOffset + 0x08);
149
150 return SGLHeader(sgl_version, len, group, model, version, key, binary_offset, header2_offset);
151 }
152 else
153 {
154 throw std::runtime_error("Can't open firmware file");
155 }
156 i.close();
157}
158
159auto TYTSGLFW::SupportsFirmwareFile(const std::string& file) -> bool
160{
161 try {
162 auto header = ReadHeader(file);
163 if (header.length != 0)
164 {
165 return true;
166 }
167 }
168 catch (std::exception e) {
169 std::cerr << e.what() << std::endl;
170 }
171 return false;
172}
173
174auto TYTSGLFW::SupportsRadioModel(const std::string& model) -> bool
175{
176 for (const auto& mx : tyt::config::sgl::All)
177 {
178 if (mx.radio_model == model)
179 {
180 return true;
181 }
182 }
183 return false;
184}
185
186auto TYTSGLFW::GetRadioModel() const -> const std::string
187{
188 if (config == nullptr) {
189 throw std::runtime_error("Radio model not selected");
190 }
191 return config->radio_model;
192}
193
194auto TYTSGLFW::SetRadioModel(const std::string& model) -> void
195{
196 for (const auto& rg : tyt::config::sgl::All)
197 {
198 if (rg.radio_model == model)
199 {
200 config = new TYTSGLRadioConfig(rg.radio_model, rg.header.AsNew(data.size()), rg.cipher, rg.cipher_len, rg.xor_offset);
201 break;
202 }
203 }
204}
205
206auto TYTSGLFW::Decrypt() -> void
207{
208 auto cx = 0;
209 for (auto& dx : data)
210 {
211 dx = ~(((dx << 3) & 0b11111000) | ((dx >> 5) & 0b00000111));
212 dx = dx ^ config->cipher[(config->xor_offset + cx++) % config->cipher_len];
213 }
214}
215
216auto TYTSGLFW::Encrypt() -> void
217{
218 // before encrypting make sure the data is the correct length
219 // if too short add padding
220 // if too big? ...official firmware writing tool wont work probably
221 // padding is normally handled by FirmwareSupport::AppendSegment
222 if (data.size() < config->header.length)
223 {
224 std::fill_n(std::back_inserter(data), config->header.length - data.size(), 0xff);
225 }
226
227 auto cx = 0;
228 for (auto& dx : data)
229 {
230 dx = dx ^ config->cipher[(config->xor_offset + cx++) % config->cipher_len];
231 dx = ~(((dx >> 3) & 0b00011111) | ((dx << 5) & 0b11100000));
232 }
233}
234
235auto TYTSGLFW::IsCompatible(const FirmwareSupport* other) const -> bool
236{
237 if (typeid(other) != typeid(TYTSGLFW)) {
238 return false;
239 }
240
241 if (config == nullptr) {
242 throw std::runtime_error("Header is not loaded, cannot compare firmware support");
243 }
244
245 auto fw = dynamic_cast<const TYTSGLFW*>(other);
246 return fw->config->header.IsCompatible(config->header)
247 && fw->config->radio_model == config->radio_model;
248}
249
250auto SGLHeader::ToString() const -> std::string
251{
252 std::stringstream ss;
253
254 ss << "SGL version: " << sgl_version << std::endl
255 << "Length: " << length << std::endl
256 << "Radio Group: " << radio_group << std::endl
257 << "Model: " << radio_model << std::endl
258 << "Protocol Version: " << protocol_version << std::endl
259 << "Key: " << model_key << std::endl
260 << "Binary Offset: 0x400 + 0x" << std::hex << std::setw(2) << std::setfill('0') << (int)binary_offset;
261
262 return ss.str();
263}
264
265auto SGLHeader::AsNew(const uint32_t& binary_len) const -> const SGLHeader
266{
267 std::default_random_engine eng;
268
269 // model key
270 std::uniform_int_distribution<uint16_t> model_key_dist('!', '}');
271 auto new_model_key = std::vector<uint8_t>{
272 (uint8_t)model_key[0], // D
273 (uint8_t)model_key[1], // V
274 (uint8_t)model_key[2], // X
275 (uint8_t)model_key[3], // X
276 (uint8_t)model_key_dist(eng),
277 (uint8_t)model_key_dist(eng),
278 (uint8_t)model_key_dist(eng),
279 (uint8_t)model_key_dist(eng)
280 };
281
282 // binary offset
283 std::uniform_int_distribution<uint16_t> binary_offset_dist(0, 0x80);
284 auto new_binary_offset = binary_offset_dist(eng);
285
286 // h2 offset
287 std::uniform_int_distribution<uint16_t> h2_offset_dist(0x1e, 0x100);
288 auto new_h2_offset = h2_offset_dist(eng);
289
290 return SGLHeader(sgl_version, binary_len, radio_group, radio_model, protocol_version, std::string(new_model_key.begin(), new_model_key.end()), new_binary_offset, new_h2_offset);
291}
292
293auto SGLHeader::Serialize(bool encrypt) const -> std::vector<uint8_t>
294{
295 auto header = std::vector<uint8_t>(HeaderLen + binary_offset);
296
297 // SGL!
298 std::copy(tyt::config::sgl::Magic.begin(), tyt::config::sgl::Magic.end(), header.begin());
299
300 // ENV001
301 std::stringstream ss_version;
302 ss_version << "ENCV" << std::setw(3) << std::setfill('0') << sgl_version;
303 auto version = ss_version.str();
304 std::copy(version.begin(), version.end(), header.begin() + 4);
305
306 // binary_offset
307 *(header.data() + 11) = binary_offset;
308
309 // h2 offset
310 *(uint16_t*)(header.data() + 12) = header2_offset;
311
312 // h2 xor key
313 auto h2_key_offset = 14;
314 std::default_random_engine eng;
315 std::uniform_int_distribution<uint16_t> dist(0, 0xffff);
316 auto key = dist(eng);
317 *(uint16_t*)(header.data() + h2_key_offset) = key;
318
319 auto h2 = header.data() + header2_offset;
320 *h2 = 0x02;
321 *(h2 + 1) = 0x10;
322
323 // binary length
324 *(uint32_t*)(h2 + BinaryLenOffset) = length;
325
326 std::copy(radio_group.begin(), radio_group.end(), h2 + GroupOffset);
327 std::copy(radio_model.begin(), radio_model.end(), h2 + ModelOffset);
328 std::copy(protocol_version.begin(), protocol_version.end(), h2 + VersionOffset);
329 std::copy(model_key.begin(), model_key.end(), h2 + KeyOffset);
330
331 if (encrypt) {
332 // encrypt header 2
333 auto h2_key = (uint8_t*)(header.data() + h2_key_offset);
334 for (auto h2x = 0;h2x < Header2Len;h2x++) {
335 *(h2 + h2x) ^= h2_key[h2x % 2];
336 }
337
338 // encrypt header 1
339 for (auto h1x = 4;h1x < Header1Len;h1x++) {
340 *(header.data() + h1x) ^= tyt::config::sgl::Magic[h1x % 4];
341 }
342 }
343
344 return header;
345}
346
347auto SGLHeader::IsCompatible(const SGLHeader& other) const -> bool
348{
349 return other.length == length
350 && other.sgl_version == sgl_version
351 && other.protocol_version == protocol_version
352 && std::equal(other.model_key.begin(), other.model_key.begin() + 4, model_key.begin())
353 && other.radio_group == radio_group
354 && other.radio_model == radio_model;
355}
std::vector< std::pair< uint32_t, uint32_t > > memory_ranges
Definition: fw.hpp:172
auto IsCompatible(const SGLHeader &other) const -> bool
Definition: tyt_fw_sgl.cpp:347
auto AsNew(const uint32_t &binary_len) const -> const SGLHeader
Definition: tyt_fw_sgl.cpp:265
auto SetRadioModel(const std::string &) -> void override
Definition: tyt_fw_sgl.cpp:194
static auto SupportsRadioModel(const std::string &model) -> bool
Definition: tyt_fw_sgl.cpp:174
auto IsCompatible(const FirmwareSupport *Other) const -> bool override
Definition: tyt_fw_sgl.cpp:235
auto GetRadioModel() const -> const std::string override
Definition: tyt_fw_sgl.cpp:186
auto Decrypt() -> void override
Definition: tyt_fw_sgl.cpp:206
auto Read(const std::string &file) -> void override
Definition: tyt_fw_sgl.cpp:36
static auto ReadHeader(const std::string &file) -> const SGLHeader
Definition: tyt_fw_sgl.cpp:107
auto Write(const std::string &file) -> void override
Definition: tyt_fw_sgl.cpp:68
static auto SupportsFirmwareFile(const std::string &file) -> bool
Definition: tyt_fw_sgl.cpp:159
auto ToString() const -> std::string override
Definition: tyt_fw_sgl.cpp:87
auto Encrypt() -> void override
Definition: tyt_fw_sgl.cpp:216