├── .gitignore ├── LICENSE ├── README.md ├── dtx ├── decoder.go ├── decoder_test.go ├── dtxprimitivedictionary.go └── fixtures │ ├── notifyOfPublishedCapabilites │ └── requestChannelWithCode ├── go.mod ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | payload_dump.* 2 | .vscode 3 | *.dump 4 | dump.txt 5 | .DS_Store 6 | *.[56789ao] 7 | *.a[56789o] 8 | *.so 9 | *.pyc 10 | ._* 11 | .nfs.* 12 | [56789a].out 13 | *~ 14 | *.orig 15 | *.rej 16 | *.exe 17 | .*.swp 18 | core 19 | *.cgo*.go 20 | *.cgo*.c 21 | _cgo_* 22 | 23 | _obj 24 | _test 25 | _testmain.go 26 | *.tmp 27 | /VERSION.cache 28 | /bin/ 29 | /build.out 30 | /doc/articles/wiki/*.bin 31 | /goinstall.log 32 | /last-change 33 | /misc/cgo/life/run.out 34 | /misc/cgo/stdio/run.out 35 | /misc/cgo/testso/main 36 | /pkg/ 37 | /src/*.*/ 38 | /src/cmd/cgo/zdefaultcc.go 39 | /src/cmd/dist/dist 40 | /src/cmd/go/internal/cfg/zdefaultcc.go 41 | /src/cmd/go/internal/cfg/zosarch.go 42 | /src/cmd/internal/objabi/zbootstrap.go 43 | /src/go/build/zcgo.go 44 | /src/go/doc/headscan 45 | /src/runtime/internal/sys/zversion.go 46 | /src/unicode/maketables 47 | /test.out 48 | /test/garbage/*.out 49 | /test/pass.out 50 | /test/run.out 51 | /test/times.out 52 | 53 | # This file includes artifacts of Go build that should not be checked in. 54 | # For files created by specific development environment (e.g. editor), 55 | # use alternative ways to exclude files from git. 56 | # For example, set up .git/info/exclude or use a global .gitignore. 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 danielpaulus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated in favor of https://github.com/danielpaulus/go-ios which has a fully working DTX implementation now 2 | 3 | 4 | # dtx_codec 5 | A golang based Apple DTX implementation. So you can run on real iOS devices: XCUITests, get CPU metrics, launch and kill apps from any OS without the need for expensive Apple hardware :-) 6 | 7 | Will be added to go-ios eventually. 8 | Use https://github.com/danielpaulus/ios_simulator_dtx_dump to get a dump of DTX messages to test the decoder with. 9 | 10 | Done: 11 | - Basic Decoder, fully decoding DTX messages and dump them 12 | 13 | Check out this example method call, which the device sends to us to tell us about the `blaUITests.blaUITests` testcase finishing: 14 | ``` 15 | i1038.0e c1 t:rpc_asking_reply mlen:25357 aux_len25162 paylen179 16 | auxheader:BufSiz:25584 Unknown:0 AuxSiz:25146 Unknown2:0 17 | aux:[{t:binary, v:["blaUITests.blaUITests"]}, 18 | {t:binary, v:["blaUITests.blaUITests"]}, 19 | {t:binary, v:["blaUITests.blaUITests"]}, 20 | ] 21 | payload: "_XCT_testCase:method:didFinishActivity:" 22 | ``` 23 | 24 | Todo: 25 | - Basic Encoder, re-encode DTX so you can control stuff 26 | - Fix a few unknown things for real devices (I am using Simulator output to develop before switching to devices) 27 | - Integrate into go-ios 28 | 29 | Check out my nskeyedarchiver implementation here: https://github.com/danielpaulus/nskeyedarchiver 30 | -------------------------------------------------------------------------------- /dtx/decoder.go: -------------------------------------------------------------------------------- 1 | package dtx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/json" 7 | "fmt" 8 | 9 | "github.com/danielpaulus/nskeyedarchiver" 10 | ) 11 | 12 | type DtxMessage struct { 13 | Fragments uint16 14 | FragmentIndex uint16 15 | MessageLength int 16 | Identifier int 17 | ConversationIndex int 18 | ChannelCode int 19 | ExpectsReply bool 20 | PayloadHeader DtxPayloadHeader 21 | Payload []interface{} 22 | AuxiliaryHeader AuxiliaryHeader 23 | Auxiliary DtxPrimitiveDictionary 24 | rawBytes []byte 25 | fragmentBytes []byte 26 | } 27 | 28 | //16 Bytes 29 | type DtxPayloadHeader struct { 30 | MessageType int 31 | AuxiliaryLength int 32 | TotalPayloadLength int 33 | Flags int 34 | } 35 | 36 | //This header can actually be completely ignored. We do not need to care about the buffer size 37 | //And we already know the AuxiliarySize. The other two ints seem to be always 0 anyway. Could 38 | //also be that Buffer and Aux Size are Uint64 39 | type AuxiliaryHeader struct { 40 | BufferSize uint32 41 | Unknown uint32 42 | AuxiliarySize uint32 43 | Unknown2 uint32 44 | } 45 | 46 | func (a AuxiliaryHeader) String() string { 47 | return fmt.Sprintf("BufSiz:%d Unknown:%d AuxSiz:%d Unknown2:%d", a.BufferSize, a.Unknown, a.AuxiliarySize, a.Unknown2) 48 | } 49 | 50 | func (d DtxMessage) String() string { 51 | var e = "" 52 | if d.ExpectsReply { 53 | e = "e" 54 | } 55 | msgtype := fmt.Sprintf("Unknown:%d", d.PayloadHeader.MessageType) 56 | if knowntype, ok := messageTypeLookup[d.PayloadHeader.MessageType]; ok { 57 | msgtype = knowntype 58 | } 59 | 60 | return fmt.Sprintf("i%d.%d%s c%d t:%s mlen:%d aux_len%d paylen%d", d.Identifier, d.ConversationIndex, e, d.ChannelCode, msgtype, 61 | d.MessageLength, d.PayloadHeader.AuxiliaryLength, d.PayloadLength()) 62 | } 63 | 64 | func (d DtxMessage) StringDebug() string { 65 | if Ack == d.PayloadHeader.MessageType { 66 | return d.String() 67 | } 68 | payload := "none" 69 | if d.HasPayload() { 70 | b, _ := json.Marshal(d.Payload[0]) 71 | payload = string(b) 72 | } 73 | if d.HasAuxiliary() { 74 | return fmt.Sprintf("auxheader:%s\naux:%s\npayload: %s \nrawbytes:%x", d.AuxiliaryHeader, d.Auxiliary, payload, d.rawBytes) 75 | } 76 | return fmt.Sprintf("no aux,payload: %s \nrawbytes:%x", payload, d.rawBytes) 77 | } 78 | func (d DtxMessage) parsePayloadBytes(messageBytes []byte) ([]interface{}, error) { 79 | offset := 0 80 | if d.HasAuxiliary() && d.HasPayload() { 81 | offset = 48 + d.PayloadHeader.AuxiliaryLength 82 | } 83 | if !d.HasAuxiliary() && d.HasPayload() { 84 | offset = 48 85 | } 86 | 87 | return nskeyedarchiver.Unarchive(messageBytes[offset:]) 88 | } 89 | 90 | func (d DtxMessage) PayloadLength() int { 91 | return d.PayloadHeader.TotalPayloadLength - d.PayloadHeader.AuxiliaryLength 92 | } 93 | 94 | func (d DtxMessage) HasAuxiliary() bool { 95 | return d.PayloadHeader.AuxiliaryLength > 0 96 | } 97 | 98 | func (d DtxMessage) HasPayload() bool { 99 | return d.PayloadLength() > 0 100 | } 101 | 102 | const ( 103 | MethodInvocationWithExpectedReply = 0x3 104 | MethodinvocationWithoutExpectedReply = 0x2 105 | Ack = 0x0 106 | ) 107 | 108 | var messageTypeLookup = map[int]string{ 109 | MethodInvocationWithExpectedReply: `rpc_void`, 110 | MethodinvocationWithoutExpectedReply: `rpc_asking_reply`, 111 | Ack: `Ack`, 112 | } 113 | 114 | const ( 115 | DtxMessageMagic uint32 = 0x795B3D1F 116 | DtxHeaderLength uint32 = 32 117 | DtxReservedBits uint32 = 0x0 118 | ) 119 | 120 | //This message is only 32 bytes long 121 | func (d DtxMessage) IsFirstFragment() bool { 122 | return d.Fragments > 1 && d.FragmentIndex == 0 123 | } 124 | 125 | func (d DtxMessage) IsLastFragment() bool { 126 | return d.Fragments-d.FragmentIndex == 1 127 | } 128 | 129 | func (d DtxMessage) IsFragment() bool { 130 | return d.Fragments > 1 131 | } 132 | 133 | //Indicates whether the message you call this on, is the first part of a fragmented message, and if otherMessage is a subsequent fragment 134 | func (d DtxMessage) MessageIsFirstFragmentFor(otherMessage DtxMessage) bool { 135 | if !d.IsFirstFragment() { 136 | panic("Illegal state") 137 | } 138 | return d.Identifier == otherMessage.Identifier && d.Fragments == otherMessage.Fragments && otherMessage.FragmentIndex > 0 139 | } 140 | 141 | func Decode(messageBytes []byte) (DtxMessage, []byte, error) { 142 | 143 | if binary.BigEndian.Uint32(messageBytes) != DtxMessageMagic { 144 | return DtxMessage{}, make([]byte, 0), fmt.Errorf("Wrong Magic: %x", messageBytes[0:4]) 145 | } 146 | if binary.LittleEndian.Uint32(messageBytes[4:]) != DtxHeaderLength { 147 | return DtxMessage{}, make([]byte, 0), fmt.Errorf("Incorrect Header length, should be 32: %x", messageBytes[4:8]) 148 | } 149 | result := DtxMessage{} 150 | result.FragmentIndex = binary.LittleEndian.Uint16(messageBytes[8:]) 151 | result.Fragments = binary.LittleEndian.Uint16(messageBytes[10:]) 152 | result.MessageLength = int(binary.LittleEndian.Uint32(messageBytes[12:])) 153 | result.Identifier = int(binary.LittleEndian.Uint32(messageBytes[16:])) 154 | result.ConversationIndex = int(binary.LittleEndian.Uint32(messageBytes[20:])) 155 | result.ChannelCode = int(binary.LittleEndian.Uint32(messageBytes[24:])) 156 | 157 | result.ExpectsReply = binary.LittleEndian.Uint32(messageBytes[28:]) == uint32(1) 158 | 159 | if result.IsFirstFragment() { 160 | return result, messageBytes[32:], nil 161 | } 162 | if result.IsFragment() { 163 | result.fragmentBytes = messageBytes[32 : result.MessageLength+32] 164 | return result, messageBytes[result.MessageLength+32:], nil 165 | } 166 | ph, err := parsePayloadHeader(messageBytes[32:48]) 167 | if err != nil { 168 | return DtxMessage{}, make([]byte, 0), err 169 | } 170 | result.PayloadHeader = ph 171 | 172 | if result.HasAuxiliary() { 173 | header, err := parseAuxiliaryHeader(messageBytes[48:64]) 174 | if err != nil { 175 | return DtxMessage{}, make([]byte, 0), err 176 | } 177 | result.AuxiliaryHeader = header 178 | auxBytes := messageBytes[64 : 48+result.PayloadHeader.AuxiliaryLength] 179 | result.Auxiliary = decodeAuxiliary(auxBytes) 180 | } 181 | 182 | totalMessageLength := result.MessageLength + int(DtxHeaderLength) 183 | result.rawBytes = messageBytes[:totalMessageLength] 184 | if result.HasPayload() { 185 | payload, err := result.parsePayloadBytes(result.rawBytes) 186 | if err != nil { 187 | return DtxMessage{}, make([]byte, 0), err 188 | } 189 | result.Payload = payload 190 | } 191 | 192 | remainingBytes := messageBytes[totalMessageLength:] 193 | return result, remainingBytes, nil 194 | } 195 | 196 | func parseAuxiliaryHeader(headerBytes []byte) (AuxiliaryHeader, error) { 197 | r := bytes.NewReader(headerBytes) 198 | var result AuxiliaryHeader 199 | err := binary.Read(r, binary.LittleEndian, &result) 200 | if err != nil { 201 | return result, err 202 | } 203 | return result, nil 204 | } 205 | 206 | func parsePayloadHeader(messageBytes []byte) (DtxPayloadHeader, error) { 207 | result := DtxPayloadHeader{} 208 | result.MessageType = int(binary.LittleEndian.Uint32(messageBytes)) 209 | result.AuxiliaryLength = int(binary.LittleEndian.Uint32(messageBytes[4:])) 210 | result.TotalPayloadLength = int(binary.LittleEndian.Uint32(messageBytes[8:])) 211 | result.Flags = int(binary.LittleEndian.Uint32(messageBytes[12:])) 212 | 213 | return result, nil 214 | } 215 | -------------------------------------------------------------------------------- /dtx/decoder_test.go: -------------------------------------------------------------------------------- 1 | package dtx_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "testing" 7 | 8 | "github.com/danielpaulus/dtx_codec/dtx" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestDecoder(t *testing.T) { 14 | dat, err := ioutil.ReadFile("fixtures/notifyOfPublishedCapabilites") 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | msg, remainingBytes, err := dtx.Decode(dat) 19 | if assert.NoError(t, err) { 20 | assert.Equal(t, 0, len(remainingBytes)) 21 | assert.Equal(t, msg.Fragments, uint16(1)) 22 | assert.Equal(t, msg.FragmentIndex, uint16(0)) 23 | assert.Equal(t, msg.MessageLength, 612) 24 | assert.Equal(t, 0, msg.ChannelCode) 25 | assert.Equal(t, false, msg.ExpectsReply) 26 | assert.Equal(t, 2, msg.Identifier) 27 | assert.Equal(t, 0, msg.ChannelCode) 28 | 29 | assert.Equal(t, 2, msg.PayloadHeader.MessageType) 30 | assert.Equal(t, 425, msg.PayloadHeader.AuxiliaryLength) 31 | assert.Equal(t, 596, msg.PayloadHeader.TotalPayloadLength) 32 | assert.Equal(t, 0, msg.PayloadHeader.Flags) 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /dtx/dtxprimitivedictionary.go: -------------------------------------------------------------------------------- 1 | package dtx 2 | 3 | import ( 4 | "container/list" 5 | "encoding/binary" 6 | "encoding/json" 7 | "fmt" 8 | "log" 9 | 10 | archiver "github.com/danielpaulus/nskeyedarchiver" 11 | ) 12 | 13 | // That is by far the weirdest concept I have ever seen. 14 | // Looking at disassembled code you can see this is a custom dictionary type 15 | // only used for DTX. In practice however, the keys are always null and the 16 | // values are used as a simple array containing the method arguments for the 17 | // method this message is invoking. (The payload object usually contains method names or returnvalues) 18 | type DtxPrimitiveDictionary struct { 19 | keyValuePairs *list.List 20 | values []interface{} 21 | valueTypes []uint32 22 | } 23 | 24 | type DtxPrimitiveKeyValuePair struct { 25 | keyType uint32 26 | key interface{} 27 | valueType uint32 28 | value interface{} 29 | } 30 | 31 | func (d DtxPrimitiveDictionary) String() string { 32 | result := "[" 33 | for i, v := range d.valueTypes { 34 | var prettyString []byte 35 | if v == bytearray { 36 | bytes := d.values[i].([]byte) 37 | prettyString = bytes 38 | msg, err := archiver.Unarchive(bytes) 39 | if err == nil { 40 | prettyString, _ = json.Marshal(msg) 41 | } 42 | result += fmt.Sprintf("{t:%s, v:%s},\n", toString(v), prettyString) 43 | continue 44 | } 45 | if v == t_uint32 { 46 | result += fmt.Sprintf("{t:%s, v:%d},\n", toString(v), d.values[i]) 47 | continue 48 | } 49 | result += fmt.Sprintf("{t:%s, v:%s},\n", toString(v), d.values[i]) 50 | } 51 | result += "]" 52 | return result 53 | } 54 | 55 | func decodeAuxiliary(auxBytes []byte) DtxPrimitiveDictionary { 56 | result := DtxPrimitiveDictionary{} 57 | result.keyValuePairs = list.New() 58 | for { 59 | keyType, key, remainingBytes := readEntry(auxBytes) 60 | auxBytes = remainingBytes 61 | valueType, value, remainingBytes := readEntry(auxBytes) 62 | auxBytes = remainingBytes 63 | pair := DtxPrimitiveKeyValuePair{keyType, key, valueType, value} 64 | result.keyValuePairs.PushBack(pair) 65 | if len(auxBytes) == 0 { 66 | break 67 | } 68 | } 69 | 70 | size := result.keyValuePairs.Len() 71 | 72 | result.valueTypes = make([]uint32, size) 73 | result.values = make([]interface{}, size) 74 | 75 | e := result.keyValuePairs.Front() 76 | for i := 0; i < size; i++ { 77 | result.valueTypes[i] = e.Value.(DtxPrimitiveKeyValuePair).valueType 78 | result.values[i] = e.Value.(DtxPrimitiveKeyValuePair).value 79 | } 80 | 81 | return result 82 | } 83 | 84 | func readEntry(auxBytes []byte) (uint32, interface{}, []byte) { 85 | readType := binary.LittleEndian.Uint32(auxBytes) 86 | if readType == null { 87 | return null, nil, auxBytes[4:] 88 | } 89 | if readType == t_uint32 { 90 | return t_uint32, auxBytes[4:8], auxBytes[8:] 91 | } 92 | if hasLength(readType) { 93 | length := binary.LittleEndian.Uint32(auxBytes[4:]) 94 | data := auxBytes[8 : 8+length] 95 | return readType, data, auxBytes[8+length:] 96 | } 97 | log.Fatalf("Unknown DtxPrimitiveDictionaryType: %d rawbytes:%x", readType, auxBytes) 98 | return 0, nil, nil 99 | } 100 | 101 | const ( 102 | null uint32 = 0x0A 103 | bytearray uint32 = 0x02 104 | t_uint32 uint32 = 0x03 105 | ) 106 | 107 | func toString(t uint32) string { 108 | switch t { 109 | case null: 110 | return "null" 111 | case bytearray: 112 | return "binary" 113 | case t_uint32: 114 | return "uint32" 115 | default: 116 | return "unknown" 117 | } 118 | } 119 | 120 | func hasLength(typeCode uint32) bool { 121 | return typeCode == bytearray 122 | } 123 | -------------------------------------------------------------------------------- /dtx/fixtures/notifyOfPublishedCapabilites: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielpaulus/dtx_codec/acb1afa52b164ebba5eeb7098a280d2af4ac329c/dtx/fixtures/notifyOfPublishedCapabilites -------------------------------------------------------------------------------- /dtx/fixtures/requestChannelWithCode: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielpaulus/dtx_codec/acb1afa52b164ebba5eeb7098a280d2af4ac329c/dtx/fixtures/requestChannelWithCode -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/danielpaulus/dtx_codec 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/danielpaulus/nskeyedarchiver v0.0.0-20200518100002-1651d009ef53 7 | github.com/stretchr/testify v1.5.1 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/danielpaulus/nskeyedarchiver v0.0.0-20200518063719-eabac822a96f h1:L3693/kJhq11/Ol2kMr+dSk6jSPFeGCzV+lA2xR13ac= 2 | github.com/danielpaulus/nskeyedarchiver v0.0.0-20200518065257-be0de79f85fa h1:F74p9CrXZTUcevzBtUEmVIHSGxZm3CPp6hIHknJrGFk= 3 | github.com/danielpaulus/nskeyedarchiver v0.0.0-20200518065257-be0de79f85fa/go.mod h1:zvDpcBmH5jI9J3kL600SL6mg3GZDwwPHhmQsKuEDy9k= 4 | github.com/danielpaulus/nskeyedarchiver v0.0.0-20200518100002-1651d009ef53 h1:+t1ZsmQi6cnmaTGY5oE1wLhdTPhC0FWnaTKq0zsMYtw= 5 | github.com/danielpaulus/nskeyedarchiver v0.0.0-20200518100002-1651d009ef53/go.mod h1:zvDpcBmH5jI9J3kL600SL6mg3GZDwwPHhmQsKuEDy9k= 6 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 9 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 10 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 11 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 12 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 13 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 17 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 18 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 20 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 21 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 22 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 23 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 24 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 25 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 26 | howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 h1:AQkaJpH+/FmqRjmXZPELom5zIERYZfwTjnHpfoVMQEc= 27 | howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= 28 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "os" 7 | 8 | "github.com/danielpaulus/dtx_codec/dtx" 9 | ) 10 | 11 | func main() { 12 | dat, err := ioutil.ReadFile("dtx/fixtures/conn-in6.dump") 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | f, err := os.Create("dump.txt") 17 | if err != nil { 18 | log.Fatal("couldnt create file") 19 | } 20 | defer f.Close() 21 | 22 | payloadDumpFile, err2 := os.Create("payload_dump.json") 23 | if err2 != nil { 24 | log.Fatal("couldnt create file") 25 | } 26 | defer payloadDumpFile.Close() 27 | 28 | remaining := 1 29 | payloadDumpFile.Write([]byte("[")) 30 | for remaining != 0 { 31 | msg, remainingBytes, err := dtx.Decode(dat) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | remaining = len(remainingBytes) 36 | dat = remainingBytes 37 | f.Write([]byte(msg.String())) 38 | f.Write([]byte("\n")) 39 | f.Write([]byte(msg.StringDebug())) 40 | f.Write([]byte("\n\n")) 41 | 42 | } 43 | payloadDumpFile.Write([]byte("]")) 44 | } 45 | --------------------------------------------------------------------------------