dissect.cobaltstrike 1.2.2.dev2__py3-none-any.whl → 1.2.2.dev3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,24 @@
1
- # file generated by setuptools-scm
1
+ # file generated by vcs-versioning
2
2
  # don't change, don't track in version control
3
+ from __future__ import annotations
3
4
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
-
6
- TYPE_CHECKING = False
7
- if TYPE_CHECKING:
8
- from typing import Tuple
9
- from typing import Union
10
-
11
- VERSION_TUPLE = Tuple[Union[int, str], ...]
12
- else:
13
- VERSION_TUPLE = object
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
14
13
 
15
14
  version: str
16
15
  __version__: str
17
- __version_tuple__: VERSION_TUPLE
18
- version_tuple: VERSION_TUPLE
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '1.2.2.dev3'
22
+ __version_tuple__ = version_tuple = (1, 2, 2, 'dev3')
19
23
 
20
- __version__ = version = '1.2.2.dev2'
21
- __version_tuple__ = version_tuple = (1, 2, 2, 'dev2')
24
+ __commit_id__ = commit_id = None
@@ -369,7 +369,7 @@ def iter_beacon_config_blocks(
369
369
  # Try XorEncoded files first as they are more common
370
370
  if not found and xordecode:
371
371
  try:
372
- fxor = cast(BinaryIO, XorEncodedFile.from_file(fobj))
372
+ fxor = cast("BinaryIO", XorEncodedFile.from_file(fobj))
373
373
  for xorkey in xor_keys:
374
374
  for config_block in find_beacon_config_bytes(fxor, xorkey):
375
375
  found = True
@@ -23,9 +23,10 @@ import logging
23
23
  from dataclasses import dataclass
24
24
  from typing import TYPE_CHECKING, BinaryIO
25
25
 
26
- from dissect.cobaltstrike.utils import grouper, u32be, xor
27
26
  from dissect.cstruct import cstruct
28
27
 
28
+ from dissect.cobaltstrike.utils import grouper, u32be, xor
29
+
29
30
  if TYPE_CHECKING:
30
31
  from collections.abc import Iterator
31
32
 
@@ -172,7 +173,6 @@ def payload_checksum(data: bytes) -> int:
172
173
 
173
174
  def iter_guardrail_configs_with_beacon(fh: BinaryIO) -> Iterator[GuardrailMetadata]:
174
175
  for grconfig in iter_guardrail_configs(fh):
175
-
176
176
  # Unmask the beacon config, static single byte xor key should be 0x2E unless modified beacon
177
177
  # The beacon config is still masked with the environmental key
178
178
  grconfig.beacon_xor_key = b"\x2e" # we currently only support the XOR default key
@@ -8,6 +8,10 @@ import io
8
8
  import logging
9
9
  from typing import BinaryIO, Optional, Tuple
10
10
 
11
+ from dissect.executable import PE
12
+ from dissect.executable.pe.c_pe import c_pe
13
+ from dissect.util.stream import OverlayStream, RangeStream
14
+
11
15
  from dissect import cstruct
12
16
 
13
17
  logger = logging.getLogger(__name__)
@@ -174,6 +178,39 @@ pestruct.load(PE_DEF)
174
178
  DOSHEADER_X64 = bytes.fromhex("554889e54881")
175
179
  DOSHEADER_X86 = bytes.fromhex("e8000000005b")
176
180
 
181
+ NT_SIGNATURE = c_pe.IMAGE_NT_SIGNATURE.to_bytes(4, "little")
182
+ OS2_SIGNATURE = c_pe.IMAGE_OS2_SIGNATURE.to_bytes(4, "little")
183
+ IMAGE_DOS_SIGNATURE = c_pe.IMAGE_DOS_SIGNATURE.to_bytes(2, "little")
184
+
185
+
186
+ class LenientPE(PE):
187
+ """Wrapper around :class:`dissect.executable.PE` that is lenient towards invalid PE signatures.
188
+
189
+ This is accomplished by using an OverlayStream to patch the PE signature when it's invalid.
190
+
191
+ It currently overlays the following fixes:
192
+
193
+ - Invalid PE signature (not "PE\0\0")
194
+ - Invalid MZ signature (not "MZ")
195
+ """
196
+
197
+ def __init__(self, fh: BinaryIO) -> None:
198
+ fh.seek(0)
199
+ mz_header = c_pe.IMAGE_DOS_HEADER(fh)
200
+ overlay = OverlayStream(fh, size=fh.seek(0, io.SEEK_END))
201
+ if mz_header.e_magic != c_pe.IMAGE_DOS_SIGNATURE:
202
+ logger.debug("Patching invalid MZ signature: %r -> %r", mz_header.e_magic, IMAGE_DOS_SIGNATURE)
203
+ overlay.add(0, IMAGE_DOS_SIGNATURE)
204
+ fh = overlay
205
+
206
+ fh.seek(mz_header.e_lfanew, io.SEEK_SET)
207
+ signature = fh.read(4)
208
+ if signature not in (NT_SIGNATURE, OS2_SIGNATURE):
209
+ logger.debug("Patching invalid PE signature: %r -> %r", signature, NT_SIGNATURE)
210
+ overlay.add(mz_header.e_lfanew, NT_SIGNATURE)
211
+ fh = overlay
212
+ super().__init__(fh)
213
+
177
214
 
178
215
  def find_mz_offset(fh: BinaryIO, start_offset: int = 0, maxrange: int = 1024) -> Optional[int]:
179
216
  """Find and return the start offset of a valid IMAGE_DOS_HEADER or ``None`` if it cannot be found.
@@ -226,35 +263,15 @@ def find_compile_stamps(
226
263
  Tuple with ``(IMAGE_FILE_HEADER.TimeDateStamp, IMAGE_EXPORT_DIRECTORY.TimeDateStamp)``.
227
264
  Either tuple values can be ``None`` if it's not found.
228
265
  """
229
- mz_offset = find_mz_offset(fh, start_offset=start_offset, maxrange=maxrange)
230
- if mz_offset is None:
231
- return (None, None)
232
-
233
266
  compile_stamp = None
234
267
  export_stamp = None
235
- fh.seek(mz_offset)
236
- mz = pestruct.IMAGE_DOS_HEADER(fh)
237
- fh.seek(mz.e_lfanew + mz_offset)
238
- signature = pestruct.uint32(fh).to_bytes(4, "little")
239
- logger.debug("PE signature: %r", signature)
240
- image = pestruct.IMAGE_FILE_HEADER(fh)
241
- compile_stamp = image.TimeDateStamp
242
- if image.Machine == pestruct.IMAGE_FILE_MACHINE_AMD64:
243
- optional_header = pestruct.IMAGE_OPTIONAL_HEADER64(fh)
244
- else:
245
- optional_header = pestruct.IMAGE_OPTIONAL_HEADER(fh)
246
- export_dd = optional_header.DataDirectory[pestruct.IMAGE_DIRECTORY_ENTRY_EXPORT]
247
- sections = [pestruct.IMAGE_SECTION_HEADER(fh) for _ in range(image.NumberOfSections)]
248
- ds = None
249
- for section in sections:
250
- if section.VirtualAddress <= export_dd.VirtualAddress < (section.VirtualAddress + section.VirtualSize):
251
- ds = section
252
- break
253
- if ds is not None:
254
- offset = export_dd.VirtualAddress - ds.VirtualAddress + ds.PointerToRawData + mz_offset
255
- fh.seek(offset)
256
- export_dir = pestruct.IMAGE_EXPORT_DIRECTORY(fh)
257
- export_stamp = export_dir.TimeDateStamp
268
+
269
+ mz_offset = find_mz_offset(fh, start_offset=start_offset, maxrange=maxrange)
270
+ if mz_offset is not None:
271
+ pe = LenientPE(RangeStream(fh, offset=mz_offset, size=None))
272
+ compile_stamp = int(pe.timestamp.timestamp())
273
+ export_stamp = int(pe.exports.timestamp.timestamp()) if pe.exports else None
274
+
258
275
  return (compile_stamp, export_stamp)
259
276
 
260
277
 
@@ -118,7 +118,7 @@ class XorEncodedFile(io.RawIOBase):
118
118
  logger.debug(f"Found common nonce offset: {offset} ({count})")
119
119
  found_nonce_offset = offset
120
120
  xf = cls(fh, nonce_offset=found_nonce_offset)
121
- if pe.find_mz_offset(cast(BinaryIO, xf)) is not None:
121
+ if pe.find_mz_offset(cast("BinaryIO", xf)) is not None:
122
122
  xf.seek(0)
123
123
  return xf
124
124
  raise ValueError(f"MZ header not found for: {fh}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cobaltstrike
3
- Version: 1.2.2.dev2
3
+ Version: 1.2.2.dev3
4
4
  Summary: a Python library for dissecting Cobalt Strike related data
5
5
  Project-URL: Homepage, https://github.com/fox-it/dissect.cobaltstrike
6
6
  Project-URL: Documentation, https://dissect-cobaltstrike.readthedocs.io/
@@ -19,17 +19,19 @@ Classifier: Operating System :: MacOS :: MacOS X
19
19
  Classifier: Operating System :: Microsoft :: Windows
20
20
  Classifier: Operating System :: POSIX
21
21
  Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Programming Language :: Python :: 3.9
23
22
  Classifier: Programming Language :: Python :: 3.10
24
23
  Classifier: Programming Language :: Python :: 3.11
25
24
  Classifier: Programming Language :: Python :: 3.12
26
25
  Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3.14
27
27
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
28
28
  Classifier: Topic :: Security
29
29
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
30
30
  Classifier: Topic :: Utilities
31
- Requires-Python: >=3.9
32
- Requires-Dist: dissect-cstruct>=4.6.dev
31
+ Requires-Python: >=3.10
32
+ Requires-Dist: dissect-cstruct>=4.7
33
+ Requires-Dist: dissect-executable>=1.11
34
+ Requires-Dist: dissect-util>=3.24
33
35
  Requires-Dist: lark
34
36
  Provides-Extra: c2
35
37
  Requires-Dist: flow-record; extra == 'c2'
@@ -99,7 +101,7 @@ If you want to install the latest pre-release version, use the ``--pre`` flag::
99
101
 
100
102
  $ pip install --pre dissect.cobaltstrike
101
103
 
102
- **dissect.cobaltstrike** requires Python 3.9 or later.
104
+ **dissect.cobaltstrike** requires Python 3.10 or later.
103
105
 
104
106
  Documentation
105
107
  -------------
@@ -6,7 +6,7 @@ docs/codespell-ignore-words.txt,sha256=R-rpUcpRDSYatkyoNGLtCBjB6jsZ2OQED-aDBtwUD
6
6
  docs/conf.py,sha256=r4z3EF5svm0-1RzL2VfCAydvuAzdpxw0yZsTNMS_lok,3209
7
7
  docs/examples.rst,sha256=Qw58xLGIaAeUfvexKzriU_KZH1UJkrLEW3SGLlSS4ZM,17947
8
8
  docs/index.rst,sha256=kFNp5D-xPdXII1YJhmKbcCFMxLnsfXkg6y41UvPcS6w,1452
9
- docs/installation.rst,sha256=4831YRv1atkMtBLoyzx5XySznLDERrNWgi5FPI2GzxQ,2737
9
+ docs/installation.rst,sha256=vwJGWr-CY8Ygw9HZcBB2uz--F4ti-NU9bOMVZdJ21HY,3048
10
10
  docs/license.rst,sha256=vqdBO50raQicLlpFkAOTiBXFGOS9Pqa5i1m0O0Hjrxw,51
11
11
  docs/_ext/generate_beacon_version_csv.py,sha256=Fd4iwsGOY2E6RhXmPoS_eW0iZUeq6FRxY-YXQEZULfU,1020
12
12
  docs/images/beacon-client.png,sha256=zWGE7pqVWkanQL-InqVhtTJfTspYj5lejyJqe161T0o,125032
@@ -32,9 +32,9 @@ docs/tutorials/decrypt_cobaltstrike_pcaps.rst,sha256=5UcotEi5NV4SFfrdOb0jUNEe3Tf
32
32
  docs/tutorials/index.rst,sha256=Pf7J869qAYdDcwGqpwmrvPxyygrFe0S13lr2yKrnw7c,185
33
33
  docs/tutorials/minimal_beacon_client.rst,sha256=Y0EGR4pMI6Heg6Iux740EM2Pn-iViIcub0UBU5r0tAg,15999
34
34
  tests/conftest.py,sha256=TiTzz35pXHXMr_XbaZhZogyhPjjIbstCBoSU_YJAdK8,3031
35
- tests/test_beacon.py,sha256=4iBhFAA-ysRtOE9LbDGKf_6E57dkcyhvN4xnElCLPzg,13518
35
+ tests/test_beacon.py,sha256=HxG-z13jaedG2Ql1K7awvXI8acitg_vV8TT7-s_FT_I,13505
36
36
  tests/test_c2.py,sha256=6d4YyjSGdIZuVx77Uo3zln-l6H9ZNL4dLr-gfJOS7Nk,16498
37
- tests/test_c2profile.py,sha256=SVLE0oV2wRMOmLipHaxASepcfaL0ShfKILskrNRlXkY,13466
37
+ tests/test_c2profile.py,sha256=ChRbE9wys6eRQIgSu3vrfGQT9LXnoynZ-YMuJOi6iXI,13466
38
38
  tests/test_client.py,sha256=FrUKF1VJ5MZUWJwosbLsorncOTEHfMTxUp0FfDulBQc,9597
39
39
  tests/test_guardrails.py,sha256=GHTSjPiccmYbRLVdM-ywTYHOErq8AdVTt3_p8v8IfiA,2435
40
40
  tests/test_pe.py,sha256=RKpNAyI5EgkeCdpJN1fCj5KdW1AFacN7dSTqwpix0dc,1525
@@ -42,22 +42,22 @@ tests/test_utils.py,sha256=eW6q0_H4FbhpEwWeZo-VPkXe-yVRA-dqUZ1WVN8ioEo,3338
42
42
  tests/test_version.py,sha256=tTFOOeC-sIMOrAFpwgvneXQIWw3PdADv4X1kVzelmNM,3020
43
43
  tests/test_xordecode.py,sha256=0Bl86caRm56_Du7JZnCLLV1voev7xto4JKBfeWGVavI,1749
44
44
  dissect/cobaltstrike/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- dissect/cobaltstrike/_version.py,sha256=OCgz16bnwObLiXNdg7N1Y99-53IX5ZXcvHJ7ECPLWzg,524
45
+ dissect/cobaltstrike/_version.py,sha256=OT805r5ytBBbFm-nzh0_TFyltiw5O101etZdrZ3nz44,533
46
46
  dissect/cobaltstrike/artifact.py,sha256=NPeDMWh8wToae6h3MhZqFIq3QT4zXIZn5xWzeKFQPdk,4087
47
- dissect/cobaltstrike/beacon.py,sha256=WuDFanCMApJPLzioZxqwMFLbW-v-xouPyzgtHQgR27E,43270
47
+ dissect/cobaltstrike/beacon.py,sha256=NJCuyZMX_MIGSaj_RgzDZvNk5b-xyU3crjmPomZ4oR0,43272
48
48
  dissect/cobaltstrike/c2.py,sha256=gQbwAjZYwd1GdX6Tb1pz807c9ykD-DlNjC1_lWATo50,25285
49
49
  dissect/cobaltstrike/c2profile.lark,sha256=WHieIq07d03qntTrqkYw-I3p-_qznBE6SwegDnrSFdI,12408
50
50
  dissect/cobaltstrike/c2profile.py,sha256=SVRQS4cxm-rhkiF-NQfgNspxZojW63lkKTgDUDBZ9Co,32678
51
51
  dissect/cobaltstrike/c_c2.py,sha256=1Z791qjUEqgSNyZHmxtNQz78S7FG3VGwWXfshE7kalc,5284
52
52
  dissect/cobaltstrike/client.py,sha256=McxoKHwbSp5V-OD5o6lySna6sYzUPM5F10npJdL_6X8,28327
53
- dissect/cobaltstrike/guardrails.py,sha256=YBi-Wt5Kh3tTXRTAnR8MN67nxNtfph_i26q4COMeIv4,7152
53
+ dissect/cobaltstrike/guardrails.py,sha256=QdpaHsXX8YqN-tNzMyMpeEjScQSmNt0Wp4zxyAtOrOk,7152
54
54
  dissect/cobaltstrike/pcap.py,sha256=Q7tUn5yBHPVVnw_ys3lNA-YYeMi3vi_Lj61XHBSthlU,15314
55
- dissect/cobaltstrike/pe.py,sha256=bEdasnP5BIOEjoFKebtbpvl5Zd6NlwkbqoBsyOesHhI,13968
55
+ dissect/cobaltstrike/pe.py,sha256=L555QiSQIjqpfWQ68hBwZJ90_0fqI8DqzZeR7UXzq38,14510
56
56
  dissect/cobaltstrike/utils.py,sha256=o5w5PdpTPZUaKlgegqi-50VjRyIcat7m2Yp3fQiM1pQ,8976
57
57
  dissect/cobaltstrike/version.py,sha256=axzv9UNg9fBYLg1wsl6JOj62RmYGO3OBeSIM6zNFYSk,6748
58
- dissect/cobaltstrike/xordecode.py,sha256=A_1wTiYYc6DEVrKwfiBJZuQkCZS_FyzMPMfApzxXgCQ,8838
59
- dissect_cobaltstrike-1.2.2.dev2.dist-info/METADATA,sha256=G7pZMQyVkizrAiUrMHUfJtMz8Zey5zyWsqzhax5XAxo,8913
60
- dissect_cobaltstrike-1.2.2.dev2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
61
- dissect_cobaltstrike-1.2.2.dev2.dist-info/entry_points.txt,sha256=xf-wJSa9XS2vurnI0aGRFLdUY3qwXD7iWWFBxKp6Lx4,320
62
- dissect_cobaltstrike-1.2.2.dev2.dist-info/licenses/LICENSE,sha256=HwS45y5toEVmu15ev5771OiuMXCittB6HXO6BZXNBLA,1074
63
- dissect_cobaltstrike-1.2.2.dev2.dist-info/RECORD,,
58
+ dissect/cobaltstrike/xordecode.py,sha256=e-QJ5KnO0PFE5Mjdt37ModfTDzJWd1_EYmKCcoXPSPA,8840
59
+ dissect_cobaltstrike-1.2.2.dev3.dist-info/METADATA,sha256=mA2f6CSWi2mj7KwhcPot3Ri-OLWFgu6gkAzw2WmLgOo,8986
60
+ dissect_cobaltstrike-1.2.2.dev3.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
61
+ dissect_cobaltstrike-1.2.2.dev3.dist-info/entry_points.txt,sha256=xf-wJSa9XS2vurnI0aGRFLdUY3qwXD7iWWFBxKp6Lx4,320
62
+ dissect_cobaltstrike-1.2.2.dev3.dist-info/licenses/LICENSE,sha256=HwS45y5toEVmu15ev5771OiuMXCittB6HXO6BZXNBLA,1074
63
+ dissect_cobaltstrike-1.2.2.dev3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.31.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
docs/installation.rst CHANGED
@@ -7,12 +7,16 @@ The easiest way to install ``dissect.cobaltstrike`` is to use **pip**:
7
7
 
8
8
  $ pip install dissect.cobaltstrike
9
9
 
10
- Python 3.9 or higher is required and it has the following dependencies:
10
+ Python 3.10 or higher is required and it has the following dependencies:
11
11
 
12
12
  * dissect.cstruct_ - for structure parsing
13
+ * dissect.executable_ - for PE parsing
14
+ * dissect.util_ - for useful file stream functions
13
15
  * lark_ - for parsing malleable c2 profiles
14
16
 
15
17
  .. _dissect.cstruct: https://github.com/fox-it/dissect.cstruct
18
+ .. _dissect.executable: https://github.com/fox-it/dissect.executable
19
+ .. _dissect.util: https://github.com/fox-it/dissect.util
16
20
  .. _lark: https://github.com/lark-parser/lark
17
21
 
18
22
  The following pip `extras` flavours are provided as well:
@@ -79,12 +83,18 @@ run tests with `checksum8` in the name including verbose and stdout logging:
79
83
  Linting
80
84
  -------
81
85
 
82
- For linting (black and flake8):
86
+ For linting, run:
83
87
 
84
88
  .. code-block:: shell
85
89
 
86
90
  $ tox -e lint
87
91
 
92
+ If there are any linting issues you can try to "fix" them using:
93
+
94
+ .. code-block:: shell
95
+
96
+ $ tox -e fix
97
+
88
98
  Documentation
89
99
  -------------
90
100
 
tests/test_beacon.py CHANGED
@@ -130,9 +130,7 @@ def test_setting_useragent_edgecase():
130
130
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
131
131
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
132
132
  00 00 00 00 00 00
133
- """.replace(
134
- "\n", ""
135
- )
133
+ """.replace("\n", "")
136
134
  config = beacon.BeaconConfig(bytes.fromhex(data))
137
135
 
138
136
  ua = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)" # noqa: E501
@@ -290,7 +288,7 @@ def test_main(capfdbinary, request, fixture, options, ret, stdout, stderr):
290
288
  def test_beacon_public_key(beacon_x86_file):
291
289
  bconfig = beacon.BeaconConfig.from_file(beacon_x86_file)
292
290
  assert bconfig.public_key == bytes.fromhex(
293
- "30819f300d06092a864886f70d010101050003818d0030818902818100c9bc2d82418688a2e4f8d645ca54dacce652ef725189444fa2def7acfaf0b40000d45933eceb80e7fc1e1e2a540a3e96c2ffc22026369d0ff07da59898f1752593876f69a80b763042abbb92c52f8a85556c5f8e8b052060231684e007a866fc010f69f4c1d79e236b1b90cbc3861bf9b3a366cf5dac02d39519dafc717dece50203010001" # noqa: 501
291
+ "30819f300d06092a864886f70d010101050003818d0030818902818100c9bc2d82418688a2e4f8d645ca54dacce652ef725189444fa2def7acfaf0b40000d45933eceb80e7fc1e1e2a540a3e96c2ffc22026369d0ff07da59898f1752593876f69a80b763042abbb92c52f8a85556c5f8e8b052060231684e007a866fc010f69f4c1d79e236b1b90cbc3861bf9b3a366cf5dac02d39519dafc717dece50203010001" # noqa: E501
294
292
  )
295
293
  assert hashlib.sha256(bconfig.public_key).hexdigest() == bconfig.settings["SETTING_PUBKEY"]
296
294
 
tests/test_c2profile.py CHANGED
@@ -62,7 +62,7 @@ def test_c2profile_generator():
62
62
 
63
63
 
64
64
  def test_value_to_string():
65
- val = b"\xCA\xFE\xBA\xBE"
65
+ val = b"\xca\xfe\xba\xbe"
66
66
  assert c2profile.value_to_string(val) == '"\\xca\\xfe\\xba\\xbe"'
67
67
  val = b"\x00\x22\x27"
68
68
  assert c2profile.value_to_string(val) == '"\\x00\\"\'"'