├── .github
└── main.png
├── recordandtrace
├── vncrec.go
├── qemu.go
├── job.go
├── disk.go
├── gdb.go
└── main.go
├── README.md
├── jobserver
├── makedb.go
└── main.go
├── int20h-gdb-trace
└── main.go
├── remotegdb
└── net.go
├── webui
└── main.go
└── benx86
└── main.go
/.github/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benjojo/dive-into-dos/HEAD/.github/main.png
--------------------------------------------------------------------------------
/recordandtrace/vncrec.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/rand"
5 | "fmt"
6 | "os/exec"
7 | )
8 |
9 | type vncRecorder struct {
10 | p *exec.Cmd
11 | filename string
12 | }
13 |
14 | func startVNCRecording(vncport int) *vncRecorder {
15 | r := vncRecorder{}
16 | r.filename = fmt.Sprintf("%s.flv", randString(10))
17 | r.p = exec.Command("flvrec.py",
18 | "-o", r.filename, fmt.Sprintf("localhost:%d", vncport-9000))
19 | go r.p.Start()
20 | // if err != nil {
21 | // log.Fatalf("Unable to start VNC recorder %s", err.Error())
22 | // }
23 | return &r
24 | }
25 |
26 | func (v *vncRecorder) stop() string {
27 | v.p.Process.Kill()
28 | v.p.Wait()
29 | return v.filename
30 | }
31 |
32 | func randString(n int) string {
33 | const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
34 | var bytes = make([]byte, n)
35 | rand.Read(bytes)
36 | for i, b := range bytes {
37 | bytes[i] = alphanum[b%byte(len(alphanum))]
38 | }
39 | return string(bytes)
40 | }
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | A Deep Dive into the world of MS-DOS viruses
2 | ===
3 |
4 | 
5 |
6 | These are the tools that I wrote as a part of my talk, These are not fully intended to be used, however you can happily take bits from them if you need them as long as you respect the licence.
7 |
8 | It's worth mentioning that none of this code should be considered that great, it's mostly been written in a rush to get the talk done.
9 |
10 | ### WebUI
11 |
12 | This it the UI to view and inspect the database. You can find a prerendered version of it all here: https://dosv.benjojo.co.uk
13 |
14 | ### BenX86
15 |
16 | I don't know why you would want this, but the program is designed to interact with webui to pull code down, and then submit new subtasks to be completed, it does this by using a very bad x86 emulator to find date/time variations in the code path.
17 |
18 | ### Jobserver
19 |
20 | this is the core of the system, this allows many programs and servers to work on tasks, This also runs the SQLLite3 database.
21 |
22 | ### RecordandTrace
23 |
24 | this is the program that deals with testing a sample, gets jobs from jobserver and submits to it after it's done.
25 |
26 | ### int20h-gdb-trace
27 |
28 | Simple version of the tracer.
29 |
30 |
31 |
32 | ## Requirements
33 |
34 | Capstone:
35 |
36 | `apt install libcapstone-dev`
37 |
38 | flv2rec:
39 |
40 | `apt install python-pip`
41 | `pip install flv2rec`
42 |
43 | QEMU:
44 |
45 | `apt install qemu`
46 |
47 | **WARNING, I can't confirm if this works on any other version of qemu other than Debian 9's, I know this sounds nuts, but trust me on this one**
48 |
49 | Fatboy:
50 |
51 | Compile it from `https://github.com/John-K/fatboy` and install it to /usr/bin/
52 |
53 |
--------------------------------------------------------------------------------
/recordandtrace/qemu.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net"
7 | "os"
8 | "os/exec"
9 | "time"
10 | )
11 |
12 | func startQEMU(hda, fdd string) (VNCPort int, GDBSocket string, monitor string, qemu *exec.Cmd, err error) {
13 | VNCPort = freePort()
14 | GDBSocket = fmt.Sprintf("./gdbsocket-%d.socket", time.Now().UnixNano())
15 | monitor = fmt.Sprintf("./monitor-%d.socket", time.Now().UnixNano())
16 |
17 | qemuS := exec.Command("/usr/bin/qemu-system-x86_64", "-hda", hda,
18 | "-s", "-m", "1", "-fda", fdd,
19 | "-vnc", fmt.Sprintf(":%d", VNCPort-9000),
20 | "-chardev", fmt.Sprintf("socket,id=gdbs,path=%s,server,nowait", GDBSocket),
21 | "-gdb", "chardev:gdbs",
22 | "-chardev", fmt.Sprintf("socket,id=monitor,path=%s,server,nowait", monitor),
23 | "-monitor", "chardev:monitor")
24 |
25 | qemuS.Stderr = os.Stderr
26 | go qemuS.Run()
27 | return VNCPort, GDBSocket, monitor, qemuS, err
28 | }
29 |
30 | func freePort() int {
31 | for {
32 | addr, _ := net.ResolveTCPAddr("tcp", "localhost:0")
33 | l, _ := net.ListenTCP("tcp", addr)
34 | defer l.Close()
35 | if l.Addr().(*net.TCPAddr).Port < 9005 {
36 | continue
37 | }
38 |
39 | return l.Addr().(*net.TCPAddr).Port
40 | }
41 |
42 | }
43 |
44 | func qemuSendKeys(key string, socket string) {
45 | c, err := net.Dial("unix", socket)
46 |
47 | if err != nil {
48 | log.Fatalf("Unable to send key, becuase socket does not work %s", err.Error())
49 | }
50 |
51 | dummy := make([]byte, 1000)
52 | // n, _ := c.Read(dummy)
53 | c.Read(dummy)
54 | // fmt.Print("A" + string(dummy[:n]))
55 | c.Write([]byte("\n"))
56 | dummy = make([]byte, 1000)
57 | // n, _ = c.Read(dummy)
58 | c.Read(dummy)
59 |
60 | // fmt.Print("B" + string(dummy[:n]))
61 |
62 | time.Sleep(time.Millisecond * 100)
63 |
64 | c.Write([]byte(fmt.Sprintf("\nsendkey %s\n", key)))
65 | dummy = make([]byte, 1000)
66 | // fmt.Print("C" + string(dummy[:n]))
67 | // n, _ = c.Read(dummy)
68 | c.Read(dummy)
69 |
70 | time.Sleep(time.Millisecond * 50)
71 |
72 | c.Close()
73 | }
74 |
--------------------------------------------------------------------------------
/recordandtrace/job.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "flag"
7 | "fmt"
8 | "io/ioutil"
9 | "net/http"
10 | "time"
11 | )
12 |
13 | /*
14 | {
15 | "ID": "abc123",
16 | "JobName": "Virus.DOS.Techo",
17 | "Binary": {
18 | "Type": "MZ",
19 | "Data": "abcd"
20 | }
21 | }
22 | */
23 |
24 | type JobData struct {
25 | Binary struct {
26 | Data string `json:"Data"`
27 | Type string `json:"Type"`
28 | } `json:"Binary"`
29 | ID int `json:"ID"`
30 | JobName string `json:"JobName"`
31 | HasSideJob bool
32 | SideJob SideJob `json:"SideJob"`
33 | }
34 |
35 | type SideJob struct {
36 | DateBased bool
37 | Day, Month, Year, Hour, Min, Second int
38 | TimeBased bool
39 | OriginalID int
40 | SideJobID int
41 | }
42 |
43 | type JobFeedback struct {
44 | DoneAt time.Time
45 | FLV []byte
46 | FloppyDisk []byte
47 | Syscalls []dosSyscall
48 | RequestedJob JobData
49 | }
50 |
51 | var (
52 | jobserver = flag.String("jobserver", "", "Job server IP to fetch a job from")
53 | )
54 |
55 | func getJob(sidejob bool) (j *JobData, err error) {
56 | jj := JobData{}
57 | var jobdata []byte
58 | if *jobserver != "" {
59 |
60 | url := fmt.Sprintf("%s/newjob", *jobserver)
61 |
62 | if sidejob {
63 | url = fmt.Sprintf("%s/getsidejob", *jobserver)
64 | }
65 |
66 | resp, err :=
67 | http.Get(url)
68 |
69 | if err != nil {
70 | return j, err
71 | }
72 |
73 | jobdata, err = ioutil.ReadAll(resp.Body)
74 | if err != nil {
75 | return j, err
76 | }
77 | } else {
78 | jobdata, err = ioutil.ReadFile("./job.json")
79 | if err != nil {
80 | return j, err
81 | }
82 | }
83 |
84 | err = json.Unmarshal(jobdata, &jj)
85 | fmt.Printf("[+] Got job: %s\n", jj.JobName)
86 | return &jj, err
87 | }
88 |
89 | func SubmitJob(flvpath string, fddimage []byte, syscalls []dosSyscall, OJob JobData) error {
90 | JF := JobFeedback{
91 | DoneAt: time.Now(),
92 | }
93 |
94 | JF.FloppyDisk = fddimage
95 |
96 | JF.Syscalls = syscalls
97 |
98 | b, _ := ioutil.ReadFile(flvpath)
99 | JF.FLV = b
100 | JF.RequestedJob = OJob
101 |
102 | jsonbytes, _ := json.Marshal(JF)
103 |
104 | if *jobserver != "" {
105 | buf := bytes.NewReader(jsonbytes)
106 | url := fmt.Sprintf("%s/jobdone", *jobserver)
107 |
108 | if OJob.HasSideJob {
109 | url = fmt.Sprintf("%s/sidejobdone", *jobserver)
110 | }
111 | _, err := http.Post(url, "application/json", buf)
112 | return err
113 |
114 | } else {
115 | fmt.Print(string(jsonbytes))
116 | }
117 |
118 | return nil
119 | }
120 |
--------------------------------------------------------------------------------
/recordandtrace/disk.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "crypto/md5"
7 | "encoding/base64"
8 | "fmt"
9 | "io"
10 | "io/ioutil"
11 | "log"
12 | "os"
13 | "os/exec"
14 | "time"
15 | )
16 |
17 | func makeDisk(malwarebase64, filename string) (hda, fdd string) {
18 | // First, Copy the base HDD image over to make a backup
19 | hda = fmt.Sprintf("./temphdd-%d.hda", time.Now().UnixNano())
20 | hdaFD, err := os.Create(hda)
21 | if err != nil {
22 | log.Fatalf("Failed to make init HDD | Create file %s", err)
23 | }
24 |
25 | basehdaFD, err := os.Open("./base.hda")
26 | if err != nil {
27 | log.Fatalf("Failed to make init HDD | Opening base file %s", err)
28 | }
29 |
30 | _, err = io.Copy(hdaFD, basehdaFD)
31 | if err != nil {
32 | log.Fatalf("Failed to make init HDD | copying file %s", err)
33 | }
34 | hdaFD.Close()
35 | basehdaFD.Close()
36 |
37 | // Now the Floppy disk
38 | floppyname := fmt.Sprintf("./tempfdd-%d.fdd", time.Now().UnixNano())
39 | fdd = floppyname
40 | fddFD, err := os.Create(floppyname)
41 | if err != nil {
42 | log.Fatalf("Failed to make init HDD | Create file %s", err)
43 | }
44 |
45 | basefddFD, err := os.Open("./base.fdd")
46 | if err != nil {
47 | log.Fatalf("Failed to make init HDD | Opening base file %s", err)
48 | }
49 |
50 | _, err = io.Copy(fddFD, basefddFD)
51 | if err != nil {
52 | log.Fatalf("Failed to make init HDD | copying file %s", err)
53 | }
54 | fddFD.Close()
55 | basefddFD.Close()
56 |
57 | // Write the malware to a temp file
58 |
59 | data, err := base64.StdEncoding.DecodeString(malwarebase64)
60 | if err != nil {
61 | log.Fatalf("Failed to decode malware base64 %s", err)
62 | }
63 |
64 | err = ioutil.WriteFile(filename, data, 0660)
65 | if err != nil {
66 | log.Fatalf("failed to write temp file %s", err)
67 | }
68 |
69 | // Now we need to put our sample on the floppy disk!
70 |
71 | fatboycmd := exec.Command("fatboy", floppyname, "add", filename)
72 | fatboycmd.Start()
73 | fatboycmd.Wait()
74 |
75 | // We should have the images in place now.
76 | os.Remove(filename)
77 | return hda, fdd
78 | }
79 |
80 | func areDisksDifferent(cleanpath, testpath string) bool {
81 | b1, _ := ioutil.ReadFile(cleanpath)
82 | b2, _ := ioutil.ReadFile(testpath)
83 |
84 | h1 := md5.Sum(b1)
85 | h2 := md5.Sum(b2)
86 |
87 | for i := 0; i < 15; i++ {
88 | if h1[i] != h2[i] {
89 | return true
90 | }
91 | }
92 | return false
93 | }
94 |
95 | func compressDisk(path string) []byte {
96 | b1, _ := ioutil.ReadFile(path)
97 | o := make([]byte, 0)
98 | buf := bytes.NewBuffer(o)
99 | gzr, _ := gzip.NewWriterLevel(buf, gzip.BestCompression)
100 | gzr.Write(b1)
101 | return buf.Bytes()
102 | }
103 |
--------------------------------------------------------------------------------
/jobserver/makedb.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "archive/tar"
5 | "bytes"
6 | "fmt"
7 | "log"
8 | "os"
9 | "os/exec"
10 | "strings"
11 |
12 | "crawshaw.io/sqlite"
13 | )
14 |
15 | func makeDB() {
16 | pool, err := sqlite.Open("file:data.db", 0, 10)
17 | if err != nil {
18 | log.Fatalf("unable make database! %s", err.Error())
19 | }
20 |
21 | // hmm, now we open the tar file.
22 | fd, err := os.Open("data.tar")
23 | if err != nil {
24 | log.Fatalf("unable open tar! %s", err.Error())
25 | }
26 |
27 | rdtar := tar.NewReader(fd)
28 |
29 | conn := pool.Get(nil)
30 | if conn == nil {
31 | return
32 | }
33 |
34 | stmt, _, err := conn.PrepareTransient(`CREATE TABLE samples (
35 | sample_id integer PRIMARY KEY AUTOINCREMENT,
36 | filename text NOT NULL,
37 | filetype text NOT NULL,
38 | samplebinary BLOB NOT NULL,
39 | evaluated integer,
40 | givenout integer,
41 | flv BLOB,
42 | floppydisk BLOB,
43 | syscalls BLOB
44 | );`)
45 | if err != nil {
46 | log.Fatalf("Table setup error 1 %s", err.Error())
47 | }
48 |
49 | _, err = stmt.Step()
50 | if err != nil {
51 | log.Fatalf("Table setup error 2 %s", err.Error())
52 | }
53 |
54 | if err := stmt.Finalize(); err != nil {
55 | log.Fatalf("Table setup error 3 %s", err.Error())
56 | }
57 | conn.Close()
58 | conn = pool.Get(nil)
59 | if conn == nil {
60 | return
61 | }
62 |
63 | rdtar.Next()
64 | for {
65 | stmt, err = conn.Prepare(
66 | "INSERT INTO samples (filename, filetype, samplebinary) VALUES ($filename, $filetype, $sample);")
67 |
68 | if err != nil {
69 | log.Fatalf("Insert setup error %s", err.Error())
70 | }
71 |
72 | header, err := rdtar.Next()
73 | if err != nil {
74 | log.Printf("done? %s", err.Error())
75 | break
76 | }
77 | samplebin := make([]byte, 500*1024)
78 | n, _ := rdtar.Read(samplebin)
79 |
80 | stmt.SetBytes("$sample", samplebin[:n])
81 | stmt.SetText("$filename", header.Name)
82 | stmt.SetText("$filetype", getMagic(samplebin[:n]))
83 | _, err = stmt.Step()
84 | if err != nil {
85 | log.Printf("Insert error %s", err.Error())
86 | }
87 | stmt.Finalize()
88 | fmt.Print(".")
89 | }
90 | conn.Close()
91 | pool.Close()
92 | // defer pool.Put(conn)
93 | }
94 |
95 | func getMagic(data []byte) string {
96 | filecmd := exec.Command("/usr/bin/file", "-")
97 |
98 | stdi, _ := filecmd.StdinPipe()
99 |
100 | if filecmd.Stdout != nil {
101 | return ""
102 | }
103 | if filecmd.Stderr != nil {
104 | return ""
105 | }
106 | var b bytes.Buffer
107 | filecmd.Stdout = &b
108 | filecmd.Stderr = &b
109 | filecmd.Start()
110 |
111 | stdi.Write(data)
112 | stdi.Close()
113 | filecmd.Wait()
114 |
115 | output := b.Bytes()
116 | os := string(output)
117 | if strings.Contains(os, "/dev/stdin:") {
118 | return strings.Split(os, ":")[1]
119 | }
120 | return ""
121 | }
122 |
123 | // filename text NOT NULL,
124 | // filetype text NOT NULL,
125 | // samplebinary BLOB NOT NULL,
126 |
--------------------------------------------------------------------------------
/recordandtrace/gdb.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/binary"
5 | "fmt"
6 | "io"
7 | "log"
8 | "net"
9 | "sync"
10 | "time"
11 |
12 | gdb "github.com/benjojo/dive-into-dos/remotegdb"
13 | )
14 |
15 | type dosSyscall struct {
16 | Time time.Time
17 | Opcode uint8
18 | Registers gdb.X86Registers
19 | DS, PostCode []byte
20 | PostCodeLocation int
21 | Marker int
22 | }
23 |
24 | type gdbTrace struct {
25 | Actions []dosSyscall
26 | stop bool
27 | g *gdb.GDBConnection
28 | l sync.Mutex
29 | Hack net.Conn
30 | }
31 |
32 | // F8 40 19 00
33 | var magicBreakpoint = 0x4289 // old
34 | // var magicBreakpoint = 0x1940f8
35 |
36 | func startTraceMSDOS(gdbsocket string) *gdbTrace {
37 | c, err := net.Dial("unix", gdbsocket)
38 | if err != nil {
39 | log.Fatal("Unabel to dial GDB socket")
40 | }
41 |
42 | gcon := gdb.NewConnection(c)
43 | gcon.SetBreakpoint(magicBreakpoint, true)
44 |
45 | syscallList := make([]dosSyscall, 0)
46 |
47 | gobj := &gdbTrace{
48 | g: gcon,
49 | Actions: syscallList,
50 | Hack: c,
51 | }
52 | go gobj.backgroundTrace()
53 | return gobj
54 | }
55 |
56 | func (g *gdbTrace) Stop() {
57 | g.stop = true
58 | }
59 |
60 | func (g *gdbTrace) didCommandFinish() bool {
61 | g.l.Lock()
62 | defer g.l.Unlock()
63 |
64 | len := len(g.Actions)
65 | if len == 0 {
66 | return false
67 | }
68 |
69 | // check if has suspect syscalls that normally happen when waiting for DOS input
70 | if (g.Actions[len-1].Opcode == 10) && (g.Actions[len-2].Opcode == 93) {
71 | return true
72 | }
73 |
74 | return false
75 | }
76 |
77 | func (g *gdbTrace) SetMarker() {
78 | g.l.Lock()
79 | defer g.l.Unlock()
80 |
81 | g.Actions = append(g.Actions, dosSyscall{Time: time.Now(), Marker: 1})
82 | }
83 |
84 | func (g *gdbTrace) backgroundTrace() {
85 | g.g.Continue()
86 | // magicBreakpoint
87 | g.g.UnsetBreakpoint(magicBreakpoint, true)
88 | g.g.Step()
89 | R, err := g.g.GetRegisters()
90 | if err != nil && !g.stop {
91 | log.Fatalf("failed to register %s", err.Error())
92 | }
93 |
94 | // fmt.Printf("AH: %02x AKA: %s\n", R.Al, opCodes[fmt.Sprintf("%02x", R.Al)])
95 | g.l.Lock()
96 | g.Actions = append(g.Actions, dosSyscall{Time: time.Now(), Opcode: R.Al, Registers: R})
97 | g.l.Unlock()
98 |
99 | for {
100 | g.g.SetBreakpoint(magicBreakpoint, true)
101 | g.g.Continue()
102 | g.g.UnsetBreakpoint(magicBreakpoint, true)
103 | g.g.Step()
104 | R, err := g.g.GetRegisters()
105 | if err != nil {
106 | if !g.stop {
107 | log.Fatalf("failed to register %s", err.Error())
108 | } else {
109 | log.Printf("Tracing disabled")
110 | return
111 | }
112 | }
113 | fmt.Print(".")
114 |
115 | DsPTR := int(R.Ds*16) + int(R.Dx)
116 |
117 | // if R.Al == 9 {
118 | // fmt.Printf("DEBUG: DsPTR location is %x\n", DsPTR)
119 | // fmt.Printf("DEBUG: DSS: %x DSS2: %x DS: %x EDX: %x DX: %x ", R.Dss, R.Dss2, R.Ds, R.Edx, R.Dx)
120 |
121 | // fmt.Printf("\n\n%#v\n", R)
122 | // }
123 |
124 | DSA, err := g.g.GetMemory(DsPTR, 100)
125 | if err != nil {
126 | log.Printf("Failed to get DS:DX for debug capture: %s", err.Error())
127 | }
128 |
129 | // We need to get the code ran after the syscall, the two values we need to get these
130 | // have been placed on the stack, so we need SS:SP
131 | StackPTR := int(uint32(R.Ss)*16) + int(R.Esp)
132 | Stack, err := g.g.GetMemory(StackPTR, 10)
133 | if err != nil {
134 | log.Printf("Failed to get SS:SP for debug capture: %s", err.Error())
135 | }
136 |
137 | // fmt.Printf("SS: %x ESP: %x ", R.Ss, R.Esp)
138 | // fmt.Printf("STACK PTR: %x\n", StackPTR)
139 | // fmt.Printf("STACK: %x\n", Stack)
140 |
141 | // Stack look here like:
142 | // 07 01 94 10
143 | // When we want it to look like:
144 | // 01 07 10 94
145 |
146 | // Meaning:
147 | // AA BB CC DD
148 | // To
149 | // BB AA DD CC
150 |
151 | // This is because we need to construct a PTR again for the code, so like:
152 |
153 | // 1094:0107 = Code Address Return
154 |
155 | // Major := binary.LittleEndian.Uint16([]byte{Stack[3], Stack[2]})
156 | Major := binary.LittleEndian.Uint16([]byte{Stack[2], Stack[3]})
157 | Minor := binary.LittleEndian.Uint16([]byte{Stack[0], Stack[1]})
158 |
159 | CodePTR := int(int32(Major)*16) + int(Minor)
160 | ReturnCode, err := g.g.GetMemory(CodePTR, 50)
161 | if err != nil {
162 | log.Printf("Failed to get return code for debug capture: %s", err.Error())
163 | }
164 | // fmt.Printf("Urgh CS: %x EIP: %x\n", Major, Minor)
165 |
166 | if R.Al == 42 {
167 | // fmt.Printf("RCode: %x\n", ReturnCode)
168 |
169 | // go FuckItProxy(g.Hack)
170 |
171 | // g.l.Lock()
172 | // log.Printf("Launching the fuck it proxy!")
173 | // time.Sleep(time.Hour)
174 |
175 | }
176 |
177 | g.l.Lock()
178 | g.Actions = append(g.Actions, dosSyscall{Time: time.Now(), Opcode: R.Al, Registers: R, DS: DSA, PostCode: ReturnCode, PostCodeLocation: CodePTR})
179 | g.l.Unlock()
180 | }
181 | }
182 |
183 | func FuckItProxy(lol net.Conn) {
184 | l, err := net.Listen("tcp4", "localhost:1234")
185 | if err != nil {
186 | log.Printf("FuckItProxy was not able to run\n\n\n\n\n%s\n\n", err)
187 | return
188 | }
189 |
190 | conn, _ := l.Accept()
191 |
192 | go func() {
193 | io.Copy(conn, lol)
194 | }()
195 | io.Copy(lol, conn)
196 |
197 | }
198 |
--------------------------------------------------------------------------------
/int20h-gdb-trace/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net"
7 |
8 | gdb "github.com/benjojo/dive-into-dos/remotegdb"
9 | )
10 |
11 | func main() {
12 | fmt.Print(".")
13 |
14 | opCodes := make(map[string]string)
15 |
16 | opCodes["00"] = "Program terminate"
17 | opCodes["01"] = "Keyboard input with echo"
18 | opCodes["02"] = "Display output"
19 | opCodes["03"] = "Wait for auxiliary device input"
20 | opCodes["04"] = "Auxiliary output"
21 | opCodes["05"] = "Printer output"
22 | opCodes["06"] = "Direct console I/O"
23 | opCodes["07"] = "Wait for direct console input without echo"
24 | opCodes["08"] = "Wait for console input without echo"
25 | opCodes["09"] = "Print string"
26 | opCodes["0a"] = "Buffered keyboard input"
27 | opCodes["0b"] = "Check standard input status"
28 | opCodes["0c"] = "Clear keyboard buffer, invoke keyboard function"
29 | opCodes["0d"] = "Disk reset"
30 | opCodes["0e"] = "Select disk"
31 | opCodes["0f"] = "Open file using FCB"
32 | opCodes["10"] = "Close file using FCB"
33 | opCodes["11"] = "Search for first entry using FCB"
34 | opCodes["12"] = "Search for next entry using FCB"
35 | opCodes["13"] = "Delete file using FCB"
36 | opCodes["14"] = "Sequential read using FCB"
37 | opCodes["15"] = "Sequential write using FCB"
38 | opCodes["16"] = "Create a file using FCB"
39 | opCodes["17"] = "Rename file using FCB"
40 | opCodes["18"] = "DOS dummy function (CP/M) (not used/listed)"
41 | opCodes["19"] = "Get current default drive"
42 | opCodes["1a"] = "Set disk transfer address"
43 | opCodes["1b"] = "Get allocation table information"
44 | opCodes["1c"] = "Get allocation table info for specific device"
45 | opCodes["1d"] = "DOS dummy function (CP/M) (not used/listed)"
46 | opCodes["1e"] = "DOS dummy function (CP/M) (not used/listed)"
47 | opCodes["1f"] = "Get pointer to default drive parameter table (undocumented)"
48 | opCodes["20"] = "DOS dummy function (CP/M) (not used/listed)"
49 | opCodes["21"] = "Random read using FCB"
50 | opCodes["22"] = "Random write using FCB"
51 | opCodes["23"] = "Get file size using FCB"
52 | opCodes["24"] = "Set relative record field for FCB"
53 | opCodes["25"] = "Set interrupt vector"
54 | opCodes["26"] = "Create new program segment"
55 | opCodes["27"] = "Random block read using FCB"
56 | opCodes["28"] = "Random block write using FCB"
57 | opCodes["29"] = "Parse filename for FCB"
58 | opCodes["2a"] = "Get date"
59 | opCodes["2b"] = "Set date"
60 | opCodes["2c"] = "Get time"
61 | opCodes["2d"] = "Set time"
62 | opCodes["2e"] = "Set/reset verify switch"
63 | opCodes["2f"] = "Get disk transfer address"
64 | opCodes["30"] = "Get DOS version number"
65 | opCodes["31"] = "Terminate process and remain resident"
66 | opCodes["32"] = "Get pointer to drive parameter table (undocumented)"
67 | opCodes["33"] = "Get/set Ctrl-Break check state & get boot drive"
68 | opCodes["34"] = "Get address to DOS critical flag (undocumented)"
69 | opCodes["35"] = "Get vector"
70 | opCodes["36"] = "Get disk free space"
71 | opCodes["37"] = "Get/set switch character (undocumented)"
72 | opCodes["38"] = "Get/set country dependent information"
73 | opCodes["39"] = "Create subdirectory (mkdir)"
74 | opCodes["3a"] = "Remove subdirectory (rmdir)"
75 | opCodes["3b"] = "Change current subdirectory (chdir)"
76 | opCodes["3c"] = "Create file using handle"
77 | opCodes["3d"] = "Open file using handle"
78 | opCodes["3e"] = "Close file using handle"
79 | opCodes["3f"] = "Read file or device using handle"
80 | opCodes["40"] = "Write file or device using handle"
81 | opCodes["41"] = "Delete file"
82 | opCodes["42"] = "Move file pointer using handle"
83 | opCodes["43"] = "Change file mode"
84 | opCodes["44"] = "I/O control for devices (IOCTL)"
85 | opCodes["45"] = "Duplicate file handle"
86 | opCodes["46"] = "Force duplicate file handle"
87 | opCodes["47"] = "Get current directory"
88 | opCodes["48"] = "Allocate memory blocks"
89 | opCodes["49"] = "Free allocated memory blocks"
90 | opCodes["4a"] = "Modify allocated memory blocks"
91 | opCodes["4b"] = "EXEC load and execute program (func 1 undocumented)"
92 | opCodes["4c"] = "Terminate process with return code"
93 | opCodes["4d"] = "Get return code of a sub-process"
94 | opCodes["4e"] = "Find first matching file"
95 | opCodes["4f"] = "Find next matching file"
96 | opCodes["50"] = "Set current process id (undocumented)"
97 | opCodes["51"] = "Get current process id (undocumented)"
98 | opCodes["52"] = "Get pointer to DOS \"INVARS\" (undocumented)"
99 | opCodes["53"] = "Generate drive parameter table (undocumented)"
100 | opCodes["54"] = "Get verify setting"
101 | opCodes["55"] = "Create PSP (undocumented)"
102 | opCodes["56"] = "Rename file"
103 | opCodes["57"] = "Get/set file date and time using handle"
104 | opCodes["58"] = "Get/set memory allocation strategy (3.x+, undocumented)"
105 | opCodes["59"] = "Get extended error information (3.x+)"
106 | opCodes["5a"] = "Create temporary file (3.x+)"
107 | opCodes["5b"] = "Create new file (3.x+)"
108 | opCodes["5c"] = "Lock/unlock file access (3.x+)"
109 | opCodes["5d"] = "Critical error information (undocumented 3.x+)"
110 | opCodes["5e"] = "Network services (3.1+)"
111 | opCodes["5f"] = "Network redirection (3.1+)"
112 | opCodes["60"] = "Get fully qualified file name (undocumented 3.x+)"
113 | opCodes["62"] = "Get address of program segment prefix (3.x+)"
114 | opCodes["63"] = "Get system lead byte table (MSDOS 2.25 only)"
115 | opCodes["64"] = "Set device driver look ahead (undocumented 3.3+)"
116 | opCodes["65"] = "Get extended country information (3.3+)"
117 | opCodes["66"] = "Get/set global code page (3.3+)"
118 | opCodes["67"] = "Set handle count (3.3+)"
119 | opCodes["68"] = "Flush buffer (3.3+)"
120 | opCodes["69"] = "Get/set disk serial number (undocumented DOS 4.0+)"
121 | opCodes["6a"] = "DOS reserved (DOS 4.0+)"
122 | opCodes["6b"] = "DOS reserved"
123 | opCodes["6c"] = "Extended open/create (4.x+)"
124 | opCodes["F8"] = "Set OEM INT 21 handler (functions F9-FF) (undocumented)"
125 |
126 | c, err := net.Dial("tcp", "localhost:1234")
127 | if err != nil {
128 | log.Fatalf("failed to dial %s", err.Error())
129 | }
130 |
131 | g := gdb.NewConnection(c)
132 | for {
133 | g.SetBreakpoint(0x4289, true)
134 | g.Continue()
135 | g.UnsetBreakpoint(0x4289, true)
136 | g.Step()
137 | R, err := g.GetRegisters()
138 | if err != nil {
139 | log.Fatalf("failed to register %s", err.Error())
140 | }
141 |
142 | fmt.Printf("AH: %02x AKA: %s\n", R.Al, opCodes[fmt.Sprintf("%02x", R.Al)])
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/recordandtrace/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "log"
7 | "os"
8 | "time"
9 | )
10 |
11 | func main() {
12 | sidejobEnabled := flag.Bool("sidejobs", false, "Should I look for side jobs instead")
13 |
14 | flag.Parse()
15 | fmt.Print("Loading data...\n")
16 |
17 | // Steps:
18 | // [+] Obtain Manifest
19 | job, err := getJob(*sidejobEnabled)
20 | if err != nil {
21 | log.Fatalf("Unable to fetch job, %s", err.Error())
22 | }
23 | log.Print("[+] Obtained Manifest")
24 |
25 | // [+] Make floppy disk image + Hard Disk
26 | filename := "test.com"
27 | if job.Binary.Type == "MZ" {
28 | filename = "test.exe"
29 | }
30 |
31 | hddimage, fddimage := makeDisk(job.Binary.Data, filename)
32 | log.Print("[+] Make floppy disk image + Hard Disk")
33 |
34 | // [+] Start QEMU in Stopped GDB mode and monitor mode set to stdin
35 | vncport, gdbsocketpath, monitorsocketpath, qemuProcess, err := startQEMU(hddimage, fddimage)
36 | if err != nil {
37 | log.Fatalf("Unable to start qemu ! %q", err.Error())
38 | }
39 | log.Print("[+] Start QEMU in Stopped GDB mode + Monitor + VNC")
40 |
41 | // [+] Check boot every second for complete
42 | time.Sleep(time.Second * 6)
43 |
44 | // [+] Send keys to change drive, type in exec name
45 | log.Print("[+] Send keys to change drive, type in exec name")
46 |
47 | if *sidejobEnabled {
48 | log.Print("[+] Typing in date")
49 |
50 | if job.SideJob.DateBased {
51 | qemuSendKeys("d", monitorsocketpath)
52 | qemuSendKeys("a", monitorsocketpath)
53 | qemuSendKeys("t", monitorsocketpath)
54 | qemuSendKeys("e", monitorsocketpath)
55 | qemuSendKeys("kp_enter", monitorsocketpath)
56 | time.Sleep(time.Millisecond * 200)
57 |
58 | datestr := fmt.Sprintf("%02d-%02d-%d", job.SideJob.Month, job.SideJob.Day, job.SideJob.Year)
59 |
60 | for _, v := range []byte(datestr) {
61 | switch v {
62 | case '0':
63 | qemuSendKeys("0", monitorsocketpath)
64 | case '1':
65 | qemuSendKeys("1", monitorsocketpath)
66 | case '2':
67 | qemuSendKeys("2", monitorsocketpath)
68 | case '3':
69 | qemuSendKeys("3", monitorsocketpath)
70 | case '4':
71 | qemuSendKeys("4", monitorsocketpath)
72 | case '5':
73 | qemuSendKeys("5", monitorsocketpath)
74 | case '6':
75 | qemuSendKeys("6", monitorsocketpath)
76 | case '7':
77 | qemuSendKeys("7", monitorsocketpath)
78 | case '8':
79 | qemuSendKeys("8", monitorsocketpath)
80 | case '9':
81 | qemuSendKeys("9", monitorsocketpath)
82 | case '-':
83 | qemuSendKeys("minus", monitorsocketpath)
84 | }
85 | }
86 |
87 | qemuSendKeys("kp_enter", monitorsocketpath)
88 | time.Sleep(time.Millisecond * 200)
89 |
90 | }
91 |
92 | // Can't and won't always be correct, but we can try
93 | if job.SideJob.TimeBased {
94 | qemuSendKeys("t", monitorsocketpath)
95 | qemuSendKeys("i", monitorsocketpath)
96 | qemuSendKeys("m", monitorsocketpath)
97 | qemuSendKeys("e", monitorsocketpath)
98 | qemuSendKeys("kp_enter", monitorsocketpath)
99 | time.Sleep(time.Millisecond * 200)
100 |
101 | timestr := fmt.Sprintf("%02d:%02d:%02d.0", job.SideJob.Hour, job.SideJob.Min, job.SideJob.Second)
102 |
103 | for _, v := range []byte(timestr) {
104 | switch v {
105 | case '0':
106 | qemuSendKeys("0", monitorsocketpath)
107 | case '1':
108 | qemuSendKeys("1", monitorsocketpath)
109 | case '2':
110 | qemuSendKeys("2", monitorsocketpath)
111 | case '3':
112 | qemuSendKeys("3", monitorsocketpath)
113 | case '4':
114 | qemuSendKeys("4", monitorsocketpath)
115 | case '5':
116 | qemuSendKeys("5", monitorsocketpath)
117 | case '6':
118 | qemuSendKeys("6", monitorsocketpath)
119 | case '7':
120 | qemuSendKeys("7", monitorsocketpath)
121 | case '8':
122 | qemuSendKeys("8", monitorsocketpath)
123 | case '9':
124 | qemuSendKeys("9", monitorsocketpath)
125 | case ':':
126 | qemuSendKeys("shift-semicolon", monitorsocketpath)
127 | case '.':
128 | qemuSendKeys("dot", monitorsocketpath)
129 | }
130 | }
131 |
132 | qemuSendKeys("kp_enter", monitorsocketpath)
133 | time.Sleep(time.Millisecond * 200)
134 | log.Print("[+] Typing in binary path")
135 |
136 | }
137 | }
138 |
139 | qemuSendKeys("a", monitorsocketpath)
140 | qemuSendKeys("shift-semicolon", monitorsocketpath)
141 | qemuSendKeys("kp_enter", monitorsocketpath)
142 | time.Sleep(time.Millisecond * 200)
143 | qemuSendKeys("t", monitorsocketpath)
144 | qemuSendKeys("e", monitorsocketpath)
145 | qemuSendKeys("s", monitorsocketpath)
146 | qemuSendKeys("t", monitorsocketpath)
147 | qemuSendKeys("dot", monitorsocketpath)
148 | if job.Binary.Type == "MZ" {
149 | qemuSendKeys("e", monitorsocketpath)
150 | qemuSendKeys("x", monitorsocketpath)
151 | qemuSendKeys("e", monitorsocketpath)
152 | } else {
153 | qemuSendKeys("c", monitorsocketpath)
154 | qemuSendKeys("o", monitorsocketpath)
155 | qemuSendKeys("m", monitorsocketpath)
156 | }
157 | // qemuSendKeys("p", monitorsocketpath)
158 | // qemuSendKeys("a", monitorsocketpath)
159 | // qemuSendKeys("h", monitorsocketpath)
160 | // qemuSendKeys("dot", monitorsocketpath)
161 | // qemuSendKeys("c", monitorsocketpath)
162 | // qemuSendKeys("o", monitorsocketpath)
163 | // qemuSendKeys("m", monitorsocketpath)
164 |
165 | // [+] Install trace handle breakpoint
166 | log.Print("[+] Install trace handle breakpoint")
167 |
168 | gdbtracer := startTraceMSDOS(gdbsocketpath)
169 |
170 | // [+] Start VNC recorder
171 | log.Print("[+] Start VNC recorder")
172 |
173 | vncr := startVNCRecording(vncport)
174 | // [+] Send enter
175 | log.Print("[+] Send enter")
176 |
177 | qemuSendKeys("kp_enter", monitorsocketpath)
178 |
179 | // [+] Wait 15 seconds
180 | log.Print("[+] Wait 15 seconds")
181 |
182 | time.Sleep(time.Second * 15)
183 |
184 | if gdbtracer.didCommandFinish() {
185 | log.Print("[+] Marking and then trying to run a few short commands to test infections")
186 |
187 | gdbtracer.SetMarker()
188 | qemuSendKeys("p", monitorsocketpath)
189 | qemuSendKeys("r", monitorsocketpath)
190 | qemuSendKeys("i", monitorsocketpath)
191 | qemuSendKeys("n", monitorsocketpath)
192 | qemuSendKeys("t", monitorsocketpath)
193 | qemuSendKeys("dot", monitorsocketpath)
194 | qemuSendKeys("c", monitorsocketpath)
195 | qemuSendKeys("o", monitorsocketpath)
196 | qemuSendKeys("m", monitorsocketpath)
197 | gdbtracer.SetMarker()
198 | qemuSendKeys("kp_enter", monitorsocketpath)
199 |
200 | log.Print("[+] Send enter")
201 | time.Sleep(time.Second * 2)
202 | }
203 |
204 | // [+] Stop recorder
205 | log.Print("[+] Stop recorder")
206 |
207 | vncrecFN := vncr.stop()
208 | syscalls := gdbtracer.Actions
209 |
210 | // [+] Stop QEMU
211 | log.Print("[+] Stop QEMU")
212 |
213 | gdbtracer.SetMarker() // TEMP: REMOVE ASAP
214 | gdbtracer.Stop()
215 | qemuProcess.Process.Kill()
216 | qemuProcess.Wait()
217 |
218 | // [+] Check floppy for changes
219 | log.Print("[+] Check floppy for changes")
220 | var diskimg []byte
221 | if areDisksDifferent(fddimage, "./base.fdd") {
222 | diskimg = compressDisk(fddimage)
223 | }
224 |
225 | // [+] Send (Recording + Diff binarys + GDB Trace log)
226 | log.Print("[+] Send (Recording + Diff binarys + GDB Trace log)")
227 | err = SubmitJob(vncrecFN, diskimg, syscalls, *job)
228 | if err != nil {
229 | log.Printf("[-] Failed to send, %s", err.Error())
230 | }
231 |
232 | os.Remove(monitorsocketpath)
233 | os.Remove(gdbsocketpath)
234 | os.Remove(hddimage)
235 | os.Remove(fddimage)
236 | os.Remove(vncrecFN)
237 |
238 | // log.Printf("VNC: %s \n syscalls: %+v", vncrecFN, syscalls)
239 | }
240 |
--------------------------------------------------------------------------------
/remotegdb/net.go:
--------------------------------------------------------------------------------
1 | package gdb
2 |
3 | import (
4 | "encoding/binary"
5 | "encoding/hex"
6 | "fmt"
7 | "io"
8 | "time"
9 | )
10 |
11 | type GDBConnection struct {
12 | c io.ReadWriter
13 | askedVcont bool
14 | }
15 |
16 | func NewConnection(C io.ReadWriter) *GDBConnection {
17 | C.Write([]byte("+"))
18 |
19 | g := &GDBConnection{
20 | c: C,
21 | }
22 |
23 | g.checksumAndSend("qSupported:xmlRegisters=i386,arm,mips")
24 |
25 | time.Sleep(time.Millisecond * 500)
26 | throwaway := make([]byte, 100000)
27 | g.c.Read(throwaway)
28 |
29 | g.checksumAndSend("Hg0")
30 | time.Sleep(time.Millisecond * 500)
31 | throwaway = make([]byte, 100000)
32 | g.c.Read(throwaway)
33 |
34 | return g
35 | }
36 |
37 | type X86Registers struct {
38 | Eax uint32
39 | Ecx uint32
40 | Edx uint32
41 | Ebx uint32
42 | Esp uint32
43 | Ebp uint32
44 | Esi uint32
45 | Edi uint32
46 | Eip uint32
47 | Eflags uint32
48 | Cs uint32
49 | Ss uint16
50 | Ds uint32
51 | Dss uint16
52 | Dss2 uint16
53 | Dss3 uint16
54 | Es uint32
55 | Ess uint16
56 | Fs uint32
57 | Gs uint32
58 | Ax uint16
59 | Dx uint16
60 | Bx uint16
61 | Ah uint8
62 | Al uint8
63 | }
64 |
65 | func (g *GDBConnection) GetRegisters() (o X86Registers, err error) {
66 | g.checksumAndSend("g") // https://www.reddit.com/r/ggggg/
67 | rout, err := g.readAndChecksum()
68 | if err != nil {
69 | return o, err // ERR
70 | }
71 |
72 | // 00840000
73 | // f90000007f00000000000000900a0000000000000d010000fb010000d24000004600000019000000160100001601000016010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000
74 |
75 | hexs, err := hex.DecodeString(rout[:len(rout)-1])
76 | if err != nil {
77 | return o, err
78 | }
79 |
80 | o.Eax = binary.LittleEndian.Uint32(hexs[0 : 0+4])
81 | o.Ax = binary.LittleEndian.Uint16(hexs[0 : 0+2])
82 | o.Ah = uint8(hexs[0])
83 | o.Al = uint8(hexs[1])
84 |
85 | o.Ecx = binary.LittleEndian.Uint32(hexs[4 : 4+4])
86 | o.Edx = binary.LittleEndian.Uint32(hexs[8 : 8+4])
87 | o.Dx = binary.LittleEndian.Uint16(hexs[8 : 8+2])
88 |
89 | o.Ebx = binary.LittleEndian.Uint32(hexs[12 : 12+4])
90 | o.Bx = binary.LittleEndian.Uint16(hexs[12 : 12+2])
91 |
92 | // o.Esp = binary.LittleEndian.Uint32(hexs[16 : 16+4])
93 | // comes in like
94 | // 00009412
95 | // should look like
96 | // 0000:1294
97 | // 11223344
98 | // 11224433
99 | ESPstart := 16
100 | SPR := make([]byte, 4)
101 | SPR[1], SPR[0], SPR[3], SPR[2] = hexs[ESPstart+1], hexs[ESPstart], hexs[ESPstart+3], hexs[ESPstart+2]
102 | o.Esp = binary.LittleEndian.Uint32(SPR)
103 | // fmt.Printf("IS THIS WHAT YOU WANT? SPR = %x AND ESP = %x\n", SPR, o.Esp)
104 |
105 | o.Ebp = binary.LittleEndian.Uint32(hexs[20 : 20+4])
106 | o.Esi = binary.LittleEndian.Uint32(hexs[24 : 24+4])
107 | o.Edi = binary.LittleEndian.Uint32(hexs[28 : 28+4])
108 | o.Eip = binary.LittleEndian.Uint32(hexs[32 : 32+4])
109 | o.Eflags = binary.LittleEndian.Uint32(hexs[36 : 36+4])
110 | // o.Cs = binary.LittleEndian.Uint32(hexs[40 : 40+4])
111 | // o.Ss = binary.LittleEndian.Uint32(hexs[44 : 44+4])
112 | // o.Ds = binary.LittleEndian.Uint32(hexs[48 : 48+4])
113 |
114 | // comes in like
115 | // 00009412
116 | // should look like
117 | // 0000:1294
118 | // 11223344
119 | // 11224433
120 |
121 | CSR := make([]byte, 4)
122 | CSR[1], CSR[0], CSR[3], CSR[2] = hexs[45], hexs[44], hexs[42], hexs[43]
123 |
124 | SSR := make([]byte, 2)
125 | SSR[1], SSR[0] = hexs[45], hexs[44]
126 |
127 | DSR := make([]byte, 4)
128 | DSR[1], DSR[0], DSR[3], DSR[2] = hexs[53], hexs[52], hexs[50], hexs[51]
129 |
130 | o.Cs = binary.LittleEndian.Uint32(CSR[:])
131 | o.Ss = binary.LittleEndian.Uint16(SSR[:])
132 | o.Ds = binary.LittleEndian.Uint32(DSR[:])
133 |
134 | fmt.Printf("\nRAW REGS: %x\n\n", hexs)
135 | // fmt.Printf("HAHAH %x OR! %x\n", CSR)
136 | o.Dss = binary.LittleEndian.Uint16(hexs[48 : 48+2])
137 | o.Dss2 = binary.LittleEndian.Uint16(hexs[50 : 50+2])
138 | o.Es = binary.LittleEndian.Uint32(hexs[52 : 52+4])
139 | o.Ess = binary.LittleEndian.Uint16(hexs[52 : 52+2])
140 | o.Fs = binary.LittleEndian.Uint32(hexs[56 : 56+4])
141 | o.Gs = binary.LittleEndian.Uint32(hexs[60 : 60+4])
142 |
143 | return o, err
144 | }
145 |
146 | func (g *GDBConnection) Continue() {
147 | if !g.askedVcont {
148 | // fuck it, I'm going to assume it works
149 | g.checksumAndSend("vCont?")
150 | time.Sleep(time.Millisecond * 50)
151 | g.readAndChecksum()
152 | g.askedVcont = true
153 | }
154 | g.checksumAndSend("vCont;c")
155 | g.readAndChecksum()
156 | }
157 |
158 | func (g *GDBConnection) Step() {
159 | g.checksumAndSend("vCont;s")
160 | g.readAndChecksum()
161 | }
162 |
163 | func (g *GDBConnection) SetBreakpoint(address int, hardwareBreakpoint bool) error {
164 | hardwarebit := 1
165 | if hardwareBreakpoint != true {
166 | hardwarebit = 0
167 | }
168 |
169 | err := g.checksumAndSend(fmt.Sprintf("Z%d,%04x,1", hardwarebit, address))
170 | if err != nil {
171 | return err
172 | }
173 |
174 | _, err = g.readAndChecksum()
175 | if err != nil {
176 | return err
177 | }
178 |
179 | return nil
180 | }
181 |
182 | func (g *GDBConnection) GetMemory(address int, length int) (ram []byte, err error) {
183 |
184 | err = g.checksumAndSend(fmt.Sprintf("m%x,%d", address, length))
185 | if err != nil {
186 | return ram, err
187 | }
188 |
189 | rams, err := g.readAndChecksum()
190 | if err != nil {
191 | return ram, err
192 | }
193 |
194 | if len(rams)%2 == 1 {
195 | rams = rams + "0"
196 | }
197 |
198 | ram, err = hex.DecodeString(rams)
199 |
200 | return ram, err
201 | }
202 |
203 | func (g *GDBConnection) UnsetBreakpoint(address int, hardwareBreakpoint bool) error {
204 | hardwarebit := 1
205 | if hardwareBreakpoint != true {
206 | hardwarebit = 0
207 | }
208 |
209 | err := g.checksumAndSend(fmt.Sprintf("z%d,%04x,1", hardwarebit, address))
210 | if err != nil {
211 | return err
212 | }
213 |
214 | _, err = g.readAndChecksum()
215 | if err != nil {
216 | return err
217 | }
218 |
219 | return nil
220 | }
221 |
222 | var EremoteGDBerror = fmt.Errorf("The remove GDB stub was unable to decode message")
223 |
224 | func (g *GDBConnection) checksumAndSend(msg string) error {
225 | cs := byte(0)
226 | for _, v := range []byte(msg) {
227 | cs += v
228 | }
229 |
230 | message := fmt.Sprintf("$%s#%02x", msg, cs)
231 |
232 | n, err := g.c.Write([]byte(message))
233 | if err != nil {
234 | return err
235 | }
236 |
237 | if n != len(message) {
238 | // Uh. fuck?
239 | panic("Window too full, not bothered to write this code yet.")
240 | }
241 |
242 | o := make([]byte, 1)
243 | _, err = g.c.Read(o)
244 | if err != nil {
245 | return err
246 | }
247 |
248 | if o[0] == byte('+') {
249 | return nil
250 | }
251 | return EremoteGDBerror
252 | }
253 |
254 | var EbadGDBresponce = fmt.Errorf("GDB Stub gave back an abnormal response")
255 |
256 | func (g *GDBConnection) readAndChecksum() (string, error) {
257 | jumbobuffer := make([]byte, 0)
258 |
259 | mini := make([]byte, 1)
260 | g.c.Read(mini)
261 |
262 | if mini[0] == '+' {
263 | g.c.Read(mini)
264 | }
265 |
266 | if mini[0] != '$' {
267 | fmt.Printf("???????? %s ?????????/\n", string(mini))
268 | return string(mini), EbadGDBresponce
269 | }
270 |
271 | for {
272 | mini := make([]byte, 128000)
273 | n, err := g.c.Read(mini)
274 | if err != nil {
275 | return "", err
276 | }
277 |
278 | hashash := false
279 | hashpos := 0
280 | for k, v := range mini[:n] {
281 | if v == '#' {
282 | hashash = true
283 | hashpos = k
284 | }
285 | }
286 |
287 | if hashash {
288 | if hashpos == 0 {
289 | break
290 | }
291 | jumbobuffer = append(jumbobuffer, mini[:hashpos-1]...)
292 | break
293 | }
294 | jumbobuffer = append(jumbobuffer, mini...)
295 | }
296 |
297 | g.c.Write([]byte("+"))
298 | return string(jumbobuffer), nil
299 | }
300 |
--------------------------------------------------------------------------------
/jobserver/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/base64"
5 | "encoding/json"
6 | "flag"
7 | "io/ioutil"
8 | "log"
9 | "net/http"
10 | "strings"
11 | "sync"
12 | "time"
13 |
14 | "crawshaw.io/sqlite"
15 | gdb "github.com/benjojo/dive-into-dos/remotegdb"
16 | )
17 |
18 | var (
19 | gendb = flag.Bool("generatedb", false, "take a tar and produce a init database")
20 | dbpool *sqlite.Pool
21 | )
22 |
23 | func main() {
24 | flag.Parse()
25 |
26 | if *gendb {
27 | makeDB()
28 | return
29 | }
30 |
31 | var err error
32 | dbpool, err = sqlite.Open("file:data.db", 0, 10)
33 | if err != nil {
34 | log.Fatalf("Unable to open SQLliteDB %s", err)
35 | }
36 |
37 | http.HandleFunc("/newjob", handleNewJob)
38 | http.HandleFunc("/jobdone", handleJobDone)
39 | http.HandleFunc("/addsidejob", handleSideJob)
40 | http.HandleFunc("/getsidejob", fetchSideJob)
41 | http.HandleFunc("/sidejobdone", logSideJob)
42 | http.ListenAndServe(":9998", http.DefaultServeMux)
43 | }
44 |
45 | type SideJob struct {
46 | DateBased bool
47 | Day, Month, Year, Hour, Min, Second int
48 | TimeBased bool
49 | OriginalID int
50 | SideJobID int
51 | }
52 |
53 | type JobData struct {
54 | Binary struct {
55 | Data string `json:"Data"`
56 | Type string `json:"Type"`
57 | } `json:"Binary"`
58 | ID int `json:"ID"`
59 | JobName string `json:"JobName"`
60 | HasSideJob bool
61 | SideJob SideJob `json:"SideJob"`
62 | }
63 |
64 | func fetchSideJob(rw http.ResponseWriter, req *http.Request) {
65 | conn := dbpool.Get(req.Context().Done())
66 | if conn == nil {
67 | log.Printf("nil connection")
68 | return
69 | }
70 | defer dbpool.Put(conn)
71 |
72 | JD := JobData{}
73 |
74 | stmt, err := conn.Prepare("SELECT subtask_id,sample_id,state FROM subtasks WHERE evaluated = 0 LIMIT 1")
75 | if err != nil {
76 | Error(rw, err)
77 | return
78 | }
79 | uniqLock.Lock()
80 | defer uniqLock.Unlock()
81 | SID := int64(0)
82 | for {
83 | hasRow, err := stmt.Step()
84 | if err != nil {
85 | Error(rw, err)
86 | return
87 | } else if !hasRow {
88 | log.Print("uh")
89 | break
90 | }
91 |
92 | JD.HasSideJob = true
93 |
94 | subState := stmt.GetText("state")
95 | SJ := SideJob{}
96 | json.Unmarshal([]byte(subState), &SJ)
97 | JD.SideJob = SJ
98 | JD.SideJob.SideJobID = int(stmt.GetInt64("subtask_id"))
99 | JD.SideJob.OriginalID = int(stmt.GetInt64("sample_id"))
100 |
101 | }
102 |
103 | stmt2, err := conn.Prepare("SELECT sample_id,filename,filetype,samplebinary FROM samples WHERE sample_id = $SID LIMIT 1")
104 | if err != nil {
105 | Error(rw, err)
106 | return
107 | }
108 |
109 | stmt2.SetInt64("$SID", int64(JD.SideJob.OriginalID))
110 |
111 | for {
112 | hasRow, err := stmt2.Step()
113 | if err != nil {
114 | Error(rw, err)
115 | return
116 | } else if !hasRow {
117 | log.Print("uh")
118 | break
119 | }
120 | SID = stmt2.GetInt64("sample_id")
121 | JD.ID = int(SID)
122 | JD.JobName = stmt2.GetText("filename")
123 | Ftype := stmt2.GetText("filetype")
124 | if strings.Contains(Ftype, "MS-DOS executable") {
125 | JD.Binary.Type = "MZ"
126 | } else {
127 | JD.Binary.Type = "COM"
128 | }
129 |
130 | sample := make([]byte, 512*1024)
131 |
132 | n := stmt2.GetBytes("samplebinary", sample)
133 |
134 | JD.Binary.Data = base64.StdEncoding.EncodeToString(sample[:n])
135 | break
136 | }
137 | stmt2.Finalize()
138 |
139 | stmt2, err = conn.Prepare("UPDATE subtasks SET evaluated = 1 WHERE subtask_id = $sid")
140 | if err != nil {
141 | Error(rw, err)
142 | return
143 | }
144 | stmt2.SetInt64("$sid", int64(JD.SideJob.SideJobID))
145 | _, err = stmt2.Step()
146 | if err != nil {
147 | Error(rw, err)
148 | return
149 | }
150 | err = stmt2.Finalize()
151 | if err != nil {
152 | Error(rw, err)
153 | return
154 | }
155 |
156 | b, _ := json.Marshal(JD)
157 | rw.Write(b)
158 |
159 | return
160 | }
161 |
162 | func handleSideJob(rw http.ResponseWriter, req *http.Request) {
163 | data, err := ioutil.ReadAll(req.Body)
164 | if err != nil {
165 | Error(rw, err)
166 | return
167 | }
168 |
169 | SJ := SideJob{}
170 | err = json.Unmarshal(data, &SJ)
171 | if err != nil {
172 | Error(rw, err)
173 | return
174 | }
175 |
176 | if SJ.OriginalID == 0 {
177 | log.Printf("Invalid data, 0121")
178 | return
179 | }
180 |
181 | // ok we have the data, so let's store the sub-task
182 |
183 | conn := dbpool.Get(req.Context().Done())
184 | if conn == nil {
185 | log.Printf("nil connection")
186 | return
187 | }
188 | defer dbpool.Put(conn)
189 |
190 | /*
191 | CREATE TABLE subtasks (
192 | subtask_id integer PRIMARY KEY AUTOINCREMENT,
193 | sample_id integer,
194 | evaluated integer,
195 | flv BLOB,
196 | floppydisk BLOB,
197 | state BLOB,
198 | syscalls BLOB
199 | );
200 |
201 | */
202 | stmt, err := conn.Prepare("INSERT INTO subtasks (sample_id,state,evaluated) VALUES ($sid, $state, 0);")
203 | if err != nil {
204 | Error(rw, err)
205 | return
206 | }
207 | stmt.SetInt64("$sid", int64(SJ.OriginalID))
208 | stmt.SetBytes("$state", data)
209 |
210 | _, err = stmt.Step()
211 | if err != nil {
212 | Error(rw, err)
213 | return
214 | }
215 | err = stmt.Finalize()
216 | if err != nil {
217 | Error(rw, err)
218 | return
219 | }
220 |
221 | return
222 | }
223 |
224 | var uniqLock sync.Mutex
225 |
226 | func handleNewJob(rw http.ResponseWriter, req *http.Request) {
227 | conn := dbpool.Get(req.Context().Done())
228 | if conn == nil {
229 | log.Printf("nil connection")
230 | return
231 | }
232 | defer dbpool.Put(conn)
233 |
234 | JD := JobData{}
235 |
236 | stmt, err := conn.Prepare("SELECT sample_id,filename,filetype,samplebinary FROM samples WHERE evaluated = 0 LIMIT 1")
237 | if err != nil {
238 | Error(rw, err)
239 | return
240 | }
241 | uniqLock.Lock()
242 | defer uniqLock.Unlock()
243 |
244 | SID := int64(0)
245 | for {
246 | hasRow, err := stmt.Step()
247 | if err != nil {
248 | Error(rw, err)
249 | return
250 | } else if !hasRow {
251 | log.Print("uh")
252 | break
253 | }
254 | SID = stmt.GetInt64("sample_id")
255 | JD.ID = int(SID)
256 | JD.JobName = stmt.GetText("filename")
257 | Ftype := stmt.GetText("filetype")
258 | if strings.Contains(Ftype, "MS-DOS executable") {
259 | JD.Binary.Type = "MZ"
260 | } else {
261 | JD.Binary.Type = "COM"
262 | }
263 |
264 | sample := make([]byte, 512*1024)
265 |
266 | n := stmt.GetBytes("samplebinary", sample)
267 |
268 | JD.Binary.Data = base64.StdEncoding.EncodeToString(sample[:n])
269 | break
270 | }
271 | stmt.Finalize()
272 |
273 | stmt, err = conn.Prepare("UPDATE samples SET evaluated = 1 WHERE sample_id = $sid")
274 | if err != nil {
275 | Error(rw, err)
276 | return
277 | }
278 | stmt.SetInt64("$sid", SID)
279 | _, err = stmt.Step()
280 | if err != nil {
281 | Error(rw, err)
282 | return
283 | }
284 | err = stmt.Finalize()
285 | if err != nil {
286 | Error(rw, err)
287 | return
288 | }
289 |
290 | b, _ := json.Marshal(JD)
291 | rw.Write(b)
292 |
293 | }
294 |
295 | func Error(rw http.ResponseWriter, err error) {
296 | log.Printf("ERR: %s ", err.Error())
297 | http.Error(rw, err.Error(), http.StatusInternalServerError)
298 | }
299 |
300 | type dosSyscall struct {
301 | Time time.Time
302 | Opcode uint8
303 | Registers gdb.X86Registers
304 | DS, PostCode []byte
305 | Marker int
306 | PostCodeLocation int
307 | }
308 |
309 | type JobFeedback struct {
310 | DoneAt time.Time
311 | FLV []byte
312 | FloppyDisk []byte
313 | Syscalls []dosSyscall
314 | RequestedJob JobData
315 | }
316 |
317 | func logSideJob(rw http.ResponseWriter, req *http.Request) {
318 | data, err := ioutil.ReadAll(req.Body)
319 | if err != nil {
320 | Error(rw, err)
321 | return
322 | }
323 |
324 | JF := JobFeedback{}
325 |
326 | log.Printf("wtf %s", string(data))
327 |
328 | err = json.Unmarshal(data, &JF)
329 | if err != nil {
330 | Error(rw, err)
331 | return
332 | }
333 |
334 | conn := dbpool.Get(req.Context().Done())
335 | if conn == nil {
336 | log.Printf("nil connection")
337 | return
338 | }
339 | defer dbpool.Put(conn)
340 |
341 | /*
342 | CREATE TABLE subtasks (
343 | subtask_id integer PRIMARY KEY AUTOINCREMENT,
344 | sample_id integer,
345 | evaluated integer,
346 | flv BLOB,
347 | floppydisk BLOB,
348 | state BLOB,
349 | syscalls BLOB
350 | );
351 |
352 | */
353 |
354 | stmt, err := conn.Prepare("UPDATE subtasks SET flv = $flv, floppydisk = $floppy, syscalls = $syscalls, evaluated = 2 WHERE subtask_id = $sid")
355 | if err != nil {
356 | Error(rw, err)
357 | return
358 | }
359 | stmt.SetInt64("$sid", int64(JF.RequestedJob.SideJob.SideJobID))
360 |
361 | stmt.SetBytes("$flv", JF.FLV)
362 | stmt.SetBytes("$floppy", JF.FloppyDisk)
363 | JFSys, _ := json.Marshal(JF.Syscalls)
364 | stmt.SetBytes("$syscalls", JFSys)
365 | _, err = stmt.Step()
366 | if err != nil {
367 | Error(rw, err)
368 | return
369 | }
370 | err = stmt.Finalize()
371 | if err != nil {
372 | Error(rw, err)
373 | return
374 | }
375 |
376 | }
377 |
378 | func handleJobDone(rw http.ResponseWriter, req *http.Request) {
379 | data, err := ioutil.ReadAll(req.Body)
380 | if err != nil {
381 | Error(rw, err)
382 | return
383 | }
384 |
385 | JF := JobFeedback{}
386 |
387 | log.Printf("wtf %s", string(data))
388 |
389 | err = json.Unmarshal(data, &JF)
390 | if err != nil {
391 | Error(rw, err)
392 | return
393 | }
394 |
395 | conn := dbpool.Get(req.Context().Done())
396 | if conn == nil {
397 | log.Printf("nil connection")
398 | return
399 | }
400 | defer dbpool.Put(conn)
401 |
402 | stmt, err := conn.Prepare("UPDATE samples SET flv = $flv, floppydisk = $floppy, syscalls = $syscalls, evaluated = 2 WHERE sample_id = $sid")
403 | if err != nil {
404 | Error(rw, err)
405 | return
406 | }
407 | stmt.SetInt64("$sid", int64(JF.RequestedJob.ID))
408 |
409 | stmt.SetBytes("$flv", JF.FLV)
410 | stmt.SetBytes("$floppy", JF.FloppyDisk)
411 | JFSys, _ := json.Marshal(JF.Syscalls)
412 | stmt.SetBytes("$syscalls", JFSys)
413 | _, err = stmt.Step()
414 | if err != nil {
415 | Error(rw, err)
416 | return
417 | }
418 | err = stmt.Finalize()
419 | if err != nil {
420 | Error(rw, err)
421 | return
422 | }
423 |
424 | }
425 |
--------------------------------------------------------------------------------
/webui/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/binary"
5 | "encoding/hex"
6 | "encoding/json"
7 | "fmt"
8 | "io"
9 | "io/ioutil"
10 | "log"
11 | "net/http"
12 | "os"
13 | "os/exec"
14 | "strconv"
15 | "strings"
16 | "sync"
17 |
18 | "crawshaw.io/sqlite"
19 | gdb "github.com/benjojo/dive-into-dos/remotegdb"
20 | "github.com/bnagy/gapstone"
21 | )
22 |
23 | func main() {
24 |
25 | var err error
26 | dbpool, err = sqlite.Open("file:data.db", 0, 10)
27 | if err != nil {
28 | log.Fatalf("Unable to open SQLliteDB %s", err)
29 | }
30 |
31 | http.HandleFunc("/", listAll)
32 | http.HandleFunc("/time", listAllTime)
33 | http.HandleFunc("/favicon.ico", nothing)
34 | http.HandleFunc("/robots.txt", nothing)
35 | http.HandleFunc("/sample", sampleview)
36 | http.HandleFunc("/gif", getGIF)
37 |
38 | log.Fatalf("Failed to listen %s", http.ListenAndServe(":8888", nil).Error())
39 | }
40 |
41 | func nothing(rw http.ResponseWriter, req *http.Request) {
42 |
43 | }
44 |
45 | type syscallCapture struct {
46 | Opcode int `json:"Opcode"`
47 | Time string `json:"Time"`
48 | Registers gdb.X86Registers
49 | DS, PostCode []byte
50 | Marker int
51 | PostCodeLocation int
52 | }
53 |
54 | var giflock sync.Mutex
55 |
56 | func getGIF(rw http.ResponseWriter, req *http.Request) {
57 | /*
58 | ffmpeg -i $1 -vf "fps=15,palettegen" -y /tmp/palette.png
59 | ffmpeg -i $1 -i /tmp/palette.png -lavfi "fps=15 [x]; [x][1:v] paletteuse" -
60 | */
61 |
62 | id := req.URL.Query().Get("id")
63 |
64 | iid, _ := strconv.ParseInt(id, 10, 64)
65 | subiid := 0
66 | if iid == 0 {
67 | subid := req.URL.Query().Get("subid")
68 |
69 | siid, _ := strconv.ParseInt(subid, 10, 64)
70 | if siid != 0 {
71 | subiid = int(siid)
72 | } else {
73 | return
74 | }
75 | }
76 |
77 | conn := dbpool.Get(req.Context().Done())
78 | defer dbpool.Put(conn)
79 |
80 | query := "SELECT flv FROM samples WHERE sample_id = $sid LIMIT 1"
81 |
82 | if subiid != 0 {
83 | query = "SELECT flv FROM subtasks WHERE subtask_id = $sid LIMIT 1"
84 | }
85 |
86 | giflock.Lock()
87 | defer giflock.Unlock()
88 |
89 | stmt, err := conn.Prepare(query)
90 | if err != nil {
91 | Error(rw, err)
92 | return
93 | }
94 |
95 | if subiid != 0 {
96 | stmt.SetInt64("$sid", int64(subiid))
97 | } else {
98 | stmt.SetInt64("$sid", iid)
99 | }
100 | flvbytes := make([]byte, 10*10e6)
101 |
102 | for {
103 | hasRow, err := stmt.Step()
104 | if err != nil {
105 | Error(rw, err)
106 | return
107 | } else if !hasRow {
108 | log.Print("uh")
109 | break
110 | }
111 | break
112 | }
113 | n := stmt.GetBytes("flv", flvbytes)
114 | ioutil.WriteFile("/tmp/lol.flv", flvbytes[:n], 0777)
115 | stmt.Finalize()
116 |
117 | pg := exec.Command("ffmpeg", "-i", "/tmp/lol.flv", "-vf", "fps=15,palettegen", "-y", "/tmp/palette.png")
118 | pg.Stderr = os.Stderr
119 | pg.Stdout = os.Stdout
120 | pg.Run()
121 | pg.Wait()
122 |
123 | // Yeah I don't actually care about the output here
124 | pg2 := exec.Command("ffmpeg", "-i", "/tmp/lol.flv", "-i", "/tmp/palette.png", "-lavfi", "fps=15 [x]; [x][1:v] paletteuse", "-f", "gif", "-")
125 | pg2.Stderr = os.Stderr
126 | stdout, _ := pg2.StdoutPipe()
127 | pg2.Start()
128 | rw.Header().Set("Content-Type", "image/gif")
129 | rw.WriteHeader(200)
130 | io.Copy(rw, stdout)
131 | pg2.Wait()
132 |
133 | }
134 |
135 | func cleanUpSyscalls(in []syscallCapture) []syscallCapture {
136 | if len(in) < 10 {
137 | return in
138 | }
139 |
140 | if in[0].Opcode == 64 &&
141 | in[1].Opcode == 41 &&
142 | in[2].Opcode == 41 &&
143 | in[3].Opcode == 41 &&
144 | in[4].Opcode == 26 &&
145 | in[5].Opcode == 71 &&
146 | in[6].Opcode == 78 &&
147 | in[7].Opcode == 71 &&
148 | in[8].Opcode == 73 &&
149 | in[9].Opcode == 75 {
150 | in = in[10:]
151 | }
152 |
153 | for k, v := range in {
154 | if v.Opcode == 72 {
155 |
156 | if len(in)-k < 8 {
157 | // Checking if we have enough syscalls ahead of us
158 | // for this check to work.
159 | return in
160 | }
161 |
162 | if in[k+1].Opcode == 72 &&
163 | in[k+2].Opcode == 37 &&
164 | in[k+3].Opcode == 37 &&
165 | in[k+4].Opcode == 37 &&
166 | in[k+5].Opcode == 62 &&
167 | in[k+6].Opcode == 62 {
168 | return in[:k-1]
169 | }
170 | }
171 | }
172 |
173 | return in
174 | }
175 |
176 | type sampleFeatures struct {
177 | Finishes, LooksAtTime, WritesToFiles bool
178 | }
179 |
180 | func getFlagsFromSample(syscalls string) (f sampleFeatures, clean []syscallCapture) {
181 | var SysCalls []syscallCapture
182 |
183 | json.Unmarshal([]byte(syscalls), &SysCalls)
184 | CleanSySCalls := cleanUpSyscalls(SysCalls)
185 |
186 | len := len(SysCalls)
187 | if len == 0 {
188 | return f, CleanSySCalls
189 | }
190 |
191 | // check if has suspect syscalls that normally happen when waiting for DOS input
192 | if (SysCalls[len-1].Opcode == 10) && (SysCalls[len-2].Opcode == 93) {
193 | f.Finishes = true
194 | }
195 |
196 | for _, v := range CleanSySCalls {
197 | if v.Opcode == 44 {
198 | f.LooksAtTime = true
199 | }
200 |
201 | if v.Opcode == 42 {
202 | f.LooksAtTime = true
203 | }
204 |
205 | if v.Opcode == 65 || // Unlink
206 | v.Opcode == 0x17 || // Rename
207 | v.Opcode == 0x40 || // Write file or device
208 | v.Opcode == 0x41 || // Unlink 2
209 | v.Opcode == 0x13 || // Unlink
210 | v.Opcode == 0x5A || // Create unique file
211 | v.Opcode == 0x56 { // Rename 2
212 | f.WritesToFiles = true
213 | }
214 |
215 | }
216 |
217 | return f, CleanSySCalls
218 | }
219 |
220 | func sampleview(rw http.ResponseWriter, req *http.Request) {
221 | id := req.URL.Query().Get("id")
222 |
223 | iid, _ := strconv.ParseInt(id, 10, 64)
224 | if iid == 0 {
225 | return
226 | }
227 |
228 | conn := dbpool.Get(req.Context().Done())
229 | defer dbpool.Put(conn)
230 |
231 | stmt, err := conn.Prepare("SELECT filename,syscalls FROM samples WHERE sample_id = $sid LIMIT 1")
232 | if err != nil {
233 | Error(rw, err)
234 | return
235 | }
236 |
237 | stmt.SetInt64("$sid", iid)
238 | var filename, syscalls string
239 |
240 | for {
241 | hasRow, err := stmt.Step()
242 | if err != nil {
243 | Error(rw, err)
244 | return
245 | } else if !hasRow {
246 | log.Print("uh")
247 | break
248 | }
249 |
250 | filename = stmt.GetText("filename")
251 | syscalls = stmt.GetText("syscalls")
252 | break
253 | }
254 | stmt.Finalize()
255 |
256 | var SysCalls []syscallCapture
257 |
258 | json.Unmarshal([]byte(syscalls), &SysCalls)
259 | TSC := SysCalls
260 | SysCalls = cleanUpSyscalls(SysCalls)
261 |
262 | if req.URL.Query().Get("raw") != "" {
263 | log.Printf("Weeeeeeeeeeeeee")
264 | b, err := json.Marshal(struct {
265 | Filename string
266 | SC []syscallCapture
267 | }{
268 | Filename: filename,
269 | SC: TSC,
270 | })
271 |
272 | if err != nil {
273 | http.Error(rw, err.Error(), 500)
274 | return
275 | }
276 | rw.WriteHeader(200)
277 | rw.Write(b)
278 | return
279 | }
280 |
281 | // Now we render!
282 |
283 | rw.Write([]byte(`
284 |
285 |
286 |
297 |
298 |
299 |
300 | Sample viewer
`))
301 |
302 | rw.Write([]byte(fmt.Sprintf("%s
\n.
GIF
", filename)))
303 |
304 | rw.Write([]byte(fmt.Sprintf("
\"\nSyscalls:
", iid)))
305 |
306 | rw.Write([]byte(`
307 |
308 | | Time |
309 | Syscall Op |
310 | Syscall Name |
311 |
`))
312 |
313 | for _, v := range SysCalls {
314 | rw.Write([]byte(fmt.Sprintf(`
315 | | %s | %d | PC: %x | %s %s |
`,
316 | v.Time, v.Opcode, v.PostCodeLocation, getSyscallName(v.Opcode), AnnotateSyscall(v.Opcode, v.Registers, v))))
317 | }
318 |
319 | rw.Write([]byte(`
`))
320 |
321 | rw.Write([]byte(``))
322 |
323 | // Now lookup the sub-tasks
324 |
325 | stmt2, err := conn.Prepare("SELECT subtask_id,state,syscalls FROM subtasks WHERE sample_id = $sid AND evaluated = 2")
326 | if err != nil {
327 | Error(rw, err)
328 | return
329 | }
330 |
331 | stmt2.SetInt64("$sid", iid)
332 |
333 | for {
334 | var subtaskID int64
335 | var state, syscallblob string
336 |
337 | hasRow, err := stmt2.Step()
338 | if err != nil {
339 | Error(rw, err)
340 | break
341 | } else if !hasRow {
342 | log.Print("uh")
343 | break
344 | }
345 |
346 | state = stmt2.GetText("state")
347 | syscallblob = stmt2.GetText("syscalls")
348 | subtaskID = stmt2.GetInt64("subtask_id")
349 |
350 | rw.Write([]byte(fmt.Sprintf("%s
\n.
GIF
", state)))
351 |
352 | rw.Write([]byte(fmt.Sprintf("
\nSyscalls:
", subtaskID)))
353 |
354 | var SysCalls []syscallCapture
355 |
356 | json.Unmarshal([]byte(syscallblob), &SysCalls)
357 | // TSC := SysCalls
358 | SysCalls = cleanUpSyscalls(SysCalls)
359 |
360 | rw.Write([]byte(`
361 |
362 | | Time |
363 | Syscall Op |
364 | Syscall Name |
365 |
`))
366 |
367 | for _, v := range SysCalls {
368 | rw.Write([]byte(fmt.Sprintf(`
369 | | %s | %d | PC: %x | %s %s |
`,
370 | v.Time, v.Opcode, v.PostCodeLocation, getSyscallName(v.Opcode), AnnotateSyscall(v.Opcode, v.Registers, v))))
371 | }
372 |
373 | rw.Write([]byte(`
`))
374 |
375 | rw.Write([]byte(``))
376 |
377 | }
378 | stmt2.Finalize()
379 |
380 | rw.Write([]byte(`
381 |
382 | `))
383 | }
384 |
385 | var dbpool *sqlite.Pool
386 |
387 | func getSyscallName(in int) string {
388 | switch in {
389 | case 0x00:
390 | return "Program terminate"
391 | case 0x01:
392 | return "Character input"
393 | case 0x02:
394 | return "Character output"
395 | case 0x03:
396 | return "Auxiliary input"
397 | case 0x04:
398 | return "Auxiliary output"
399 | case 0x05:
400 | return "Printer output"
401 | case 0x06:
402 | return "Direct console I/O"
403 | case 0x07:
404 | return "Direct console input without echo"
405 | case 0x08:
406 | return "Console input without echo"
407 | case 0x09:
408 | return "Display string"
409 | case 0x0A:
410 | return "Buffered keyboard input"
411 | case 0x0B:
412 | return "Get input status"
413 | case 0x0C:
414 | return "Flush input buffer and input"
415 | case 0x0D:
416 | return "Disk reset"
417 | case 0x0E:
418 | return "Set default drive"
419 | case 0x0F:
420 | return "Open file"
421 | case 0x10:
422 | return "Close file"
423 | case 0x11:
424 | return "Find first file"
425 | case 0x12:
426 | return "Find next file"
427 | case 0x13:
428 | return "Delete file"
429 | case 0x14:
430 | return "Sequential read"
431 | case 0x15:
432 | return "Sequential write"
433 | case 0x16:
434 | return "Create or truncate file"
435 | case 0x17:
436 | return "Rename file"
437 | case 0x18:
438 | return "Reserved"
439 | case 0x19:
440 | return "Get default drive"
441 | case 0x1A:
442 | return "Set disk transfer address"
443 | case 0x1B:
444 | return "Get allocation info for default drive"
445 | case 0x1C:
446 | return "Get allocation info for specified drive"
447 | case 0x1D:
448 | return "Reserved"
449 | case 0x1E:
450 | return "Reserved"
451 | case 0x1F:
452 | return "Get disk parameter block for default drive"
453 | case 0x20:
454 | return "Reserved"
455 | case 0x21:
456 | return "Random read"
457 | case 0x22:
458 | return "Random write"
459 | case 0x23:
460 | return "Get file size in records"
461 | case 0x24:
462 | return "Set random record number"
463 | case 0x25:
464 | return "Set interrupt vector"
465 | case 0x26:
466 | return "Create PSP"
467 | case 0x27:
468 | return "Random block read"
469 | case 0x28:
470 | return "Random block write"
471 | case 0x29:
472 | return "Parse filename"
473 | case 0x2A:
474 | return "Get date"
475 | case 0x2B:
476 | return "Set date"
477 | case 0x2C:
478 | return "Get time"
479 | case 0x2D:
480 | return "Set time"
481 | case 0x2E:
482 | return "Set verify flag"
483 | case 0x2F:
484 | return "Get disk transfer address"
485 | case 0x30:
486 | return "Get DOS version"
487 | case 0x31:
488 | return "Terminate and stay resident"
489 | case 0x32:
490 | return "Get disk parameter block for specified drive"
491 | case 0x33:
492 | return "Get or set Ctrl-Break"
493 | case 0x34:
494 | return "Get InDOS flag pointer"
495 | case 0x35:
496 | return "Get interrupt vector"
497 | case 0x36:
498 | return "Get free disk space"
499 | case 0x37:
500 | return "Get or set switch character"
501 | case 0x38:
502 | return "Get or set country info"
503 | case 0x39:
504 | return "Create subdirectory"
505 | case 0x3A:
506 | return "Remove subdirectory"
507 | case 0x3B:
508 | return "Change current directory"
509 | case 0x3C:
510 | return "Create or truncate file"
511 | case 0x3D:
512 | return "Open file"
513 | case 0x3E:
514 | return "Close file"
515 | case 0x3F:
516 | return "Read file or device"
517 | case 0x40:
518 | return "Write file or device"
519 | case 0x41:
520 | return "Delete file"
521 | case 0x42:
522 | return "Move file pointer"
523 | case 0x43:
524 | return "Get or set file attributes"
525 | case 0x44:
526 | return "I/O control for devices"
527 | case 0x45:
528 | return "Duplicate handle"
529 | case 0x46:
530 | return "Redirect handle"
531 | case 0x47:
532 | return "Get current directory"
533 | case 0x48:
534 | return "Allocate memory"
535 | case 0x49:
536 | return "Release memory"
537 | case 0x4A:
538 | return "Reallocate memory"
539 | case 0x4B:
540 | return "Execute program"
541 | case 0x4C:
542 | return "Terminate with return code"
543 | case 0x4D:
544 | return "Get program return code"
545 | case 0x4E:
546 | return "Find first file"
547 | case 0x4F:
548 | return "Find next file"
549 | case 0x50:
550 | return "Set current PSP"
551 | case 0x51:
552 | return "Get current PSP"
553 | case 0x52:
554 | return "Get DOS internal pointers (SYSVARS)"
555 | case 0x53:
556 | return "Create disk parameter block"
557 | case 0x54:
558 | return "Get verify flag"
559 | case 0x55:
560 | return "Create program PSP"
561 | case 0x56:
562 | return "Rename file"
563 | case 0x57:
564 | return "Get or set file date and time"
565 | case 0x58:
566 | return "case 0xGet or set allocation strateg:"
567 | case 0x59:
568 | return "Get extended error info"
569 | case 0x5A:
570 | return "Create unique file"
571 | case 0x5B:
572 | return "Create new file"
573 | case 0x5C:
574 | return "Lock or unlock file"
575 | case 0x5D:
576 | return "File sharing functions"
577 | case 0x5E:
578 | return "Network functions"
579 | case 0x5F:
580 | return "Network redirection functions"
581 | case 0x60:
582 | return "Qualify filename"
583 | case 0x61:
584 | return "Reserved"
585 | case 0x62:
586 | return "Get current PSP"
587 | case 0x63:
588 | return "Get DBCS lead byte table pointer"
589 | case 0x64:
590 | return "Set wait for external event flag"
591 | case 0x65:
592 | return "Get extended country info"
593 | case 0x66:
594 | return "Get or set code page"
595 | case 0x67:
596 | return "Set handle count"
597 | case 0x68:
598 | return "Commit file"
599 | case 0x69:
600 | return "Get or set media id"
601 | case 0x6A:
602 | return "Commit file"
603 | case 0x6B:
604 | return "Reserved"
605 | case 0x6C:
606 | return "Extended open/create file"
607 | }
608 |
609 | return "UNKNOWN!"
610 | }
611 |
612 | func int16ToHex(in uint16) string {
613 | bs := make([]byte, 2)
614 | binary.LittleEndian.PutUint16(bs, in)
615 | return hex.EncodeToString(bs)
616 | }
617 |
618 | func AnnotateSyscall(Ah int, R gdb.X86Registers, full syscallCapture) string {
619 | switch Ah {
620 | case 0x02:
621 | return fmt.Sprintf("(Char = '%s')", int16ToHex(R.Dx)[:2])
622 | case 0x09:
623 | i := strings.IndexByte(string(full.DS), '$')
624 | if i == -1 {
625 | return "(Could not find end pointer)"
626 | }
627 | return fmt.Sprintf("(String= '%s')", string(full.DS[:i]))
628 | case 0x0E:
629 | // return "Set default drive"
630 | b, _ := hex.DecodeString(int16ToHex(R.Dx)[:2])
631 | return fmt.Sprintf("(Drive = '%s')", string('A'+b[0]))
632 | case 0x0F:
633 | return fmt.Sprintf("(Filename = '%s')", string(full.DS))
634 | case 0x3D: // Open File using Handle
635 | i := strings.IndexByte(string(full.DS), 0x00)
636 | if i == -1 {
637 | return ""
638 | }
639 | // return fmt.Sprintf("(Filename = '%s') / RC%s", string(full.DS[:i]), dumpReturnCode(full))
640 | return fmt.Sprintf("(Filename = '%s')", string(full.DS[:i]))
641 | case 0x3F: // Read using Handle
642 | return fmt.Sprintf("(Read %d bytes on handle %d)", R.Ecx, R.Bx)
643 | case 0x40: // Write using handle
644 | return fmt.Sprintf("(Write %d bytes on handle %d)", R.Ecx, R.Bx)
645 | case 0x41: // Delete using Handle
646 | i := strings.IndexByte(string(full.DS), 0x00)
647 | if i == -1 {
648 | return ""
649 | }
650 | return fmt.Sprintf("(Filename = '%s')", string(full.DS[:i]))
651 | case 0x44: // Get/Set File Attributes
652 | i := strings.IndexByte(string(full.DS), 0x00)
653 | if i == -1 {
654 | return ""
655 | }
656 | if R.Al == 00 {
657 | return fmt.Sprintf("(Get for = '%s')", string(full.DS[:i]))
658 | }
659 | return fmt.Sprintf("(Set for = '%s')", string(full.DS[:i]))
660 |
661 | case 0x25:
662 | return fmt.Sprintf("(Interrupt = '%d' AKA '%s')", R.Ah, getSyscallName(int(R.Ah)))
663 | // return "Set Interrupt Vector"
664 | case 0x31:
665 | return fmt.Sprintf("(Return code = '%d' | Memory size = '%d')", R.Ah, R.Dx)
666 | // return "Terminate and stay resident"
667 | case 0x35:
668 | return fmt.Sprintf("(Interrupt = '%d' AKA '%s')", R.Ah, getSyscallName(int(R.Ah)))
669 | // return "Get Interrupt Vector"
670 | case 0x4C:
671 | return fmt.Sprintf("(Return code = '%d')", R.Ah)
672 | // return "Terminate with return code"
673 | case 0x2A: // Get Date
674 | return dumpReturnCode(full)
675 | case 0x2C: // Get Time
676 | return dumpReturnCode(full)
677 | }
678 | return ""
679 | }
680 |
681 | func dumpReturnCode(all syscallCapture) string {
682 | log.Printf("wow ! \n\n%#v\n", all)
683 |
684 | buffer := "(DISAM FAILED)"
685 | if len(all.PostCode) == 0 {
686 | return buffer
687 | }
688 |
689 | engine, err := gapstone.New(
690 | gapstone.CS_ARCH_X86,
691 | // gapstone.CS_MODE_32,
692 | gapstone.CS_MODE_16,
693 | )
694 |
695 | if err == nil {
696 |
697 | defer engine.Close()
698 | buffer = ""
699 | maj, min := engine.Version()
700 | log.Printf("Hello one! Version: %v.%v\n", maj, min)
701 | log.Printf("Code: %x", all.PostCode)
702 |
703 | insns, err := engine.Disasm(
704 | []byte(all.PostCode), // code buffer
705 | uint64(all.PostCodeLocation), // starting address
706 | 20, // insns to disassemble, 0 for all
707 | )
708 |
709 | if err == nil {
710 | log.Printf("Disasm:\n")
711 | for _, insn := range insns {
712 | log.Printf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
713 | buffer += fmt.Sprintf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
714 | }
715 | return buffer
716 | } else {
717 | log.Printf("FUCK \n\n\n\n FUCK FUUUUUUUUUUUUUUCK~ \n\n\n %s \n\n\n", err.Error())
718 | }
719 | }
720 | return buffer
721 | }
722 |
723 | func listAllTime(rw http.ResponseWriter, req *http.Request) {
724 | conn := dbpool.Get(req.Context().Done())
725 | defer dbpool.Put(conn)
726 |
727 | stmt, err := conn.Prepare("SELECT DISTINCT(sample_id) as A FROM subtasks WHERE evaluated = 2;") // ORDER BY filename DESC
728 | if err != nil {
729 | Error(rw, err)
730 | return
731 | }
732 | defer stmt.Finalize()
733 |
734 | rw.Write([]byte(`
735 |
736 |
737 |
748 |
749 |
750 |
751 | Malware listing
752 |
753 |
754 |
755 | | ID |
756 | Filename |
757 | Diff syscalls |
758 | Diff Removes Files |
759 | Diff Hangs system |
760 | Link |
761 |
`))
762 |
763 | for {
764 | hasRow, err := stmt.Step()
765 | if err != nil {
766 | Error(rw, err)
767 | return
768 | } else if !hasRow {
769 | log.Print("uh")
770 | break
771 | }
772 |
773 | var jobid int64
774 | // var sampleName string
775 |
776 | jobid = stmt.GetInt64("A")
777 |
778 | stmt2, err := conn.Prepare("SELECT syscalls FROM subtasks WHERE sample_id = $SID;") // ORDER BY filename DESC
779 | if err != nil {
780 | Error(rw, err)
781 | return
782 | }
783 |
784 | stmt2.SetInt64("$SID", jobid)
785 | syscallsamples := make([][]byte, 0)
786 |
787 | for {
788 | rr, err := stmt2.Step()
789 | if !rr || err != nil {
790 | break
791 | }
792 |
793 | syscallblob := make([]byte, 1e6*15)
794 | n := stmt2.GetBytes("syscalls", syscallblob)
795 | // syscallblob
796 |
797 | syscallsamples = append(syscallsamples, []byte(syscallblob[:n]))
798 | }
799 | stmt2.Finalize()
800 | stmt2, err = conn.Prepare("SELECT filename,syscalls FROM samples WHERE sample_id = $SID;") // ORDER BY filename DESC
801 | if err != nil {
802 | Error(rw, err)
803 | return
804 | }
805 |
806 | stmt2.SetInt64("$SID", jobid)
807 | filename := ""
808 | for {
809 | rr, err := stmt2.Step()
810 | if !rr || err != nil {
811 | break
812 | }
813 |
814 | syscallblob := stmt2.GetText("syscalls")
815 | filename = stmt2.GetText("filename")
816 | // syscallblob
817 | // fmt.Printf("\n%v\n", syscallblob)
818 |
819 | syscallsamples = append(syscallsamples, []byte(syscallblob))
820 | }
821 | stmt2.Finalize()
822 |
823 | //
824 | // We have all the syscall blobs now in syscallsamples
825 | //
826 |
827 | // ID |
828 | // Filename |
829 | // Diff syscalls |
830 | // Diff Removes Files |
831 | // Diff Hangs system |
832 | // Link |
833 |
834 | syscalllen := make(map[int]int)
835 | removesfiles := 0
836 | hangssystem := 0
837 |
838 | for _, v := range syscallsamples {
839 | var SysCalls []syscallCapture
840 |
841 | json.Unmarshal([]byte(v), &SysCalls)
842 | CleanSySCalls := cleanUpSyscalls(SysCalls)
843 |
844 | rfiles := false
845 |
846 | syscalllen[len(SysCalls)]++
847 |
848 | for _, scall := range CleanSySCalls {
849 | if scall.Opcode == 0x41 {
850 | rfiles = true
851 | }
852 |
853 | if scall.Opcode == 0x13 {
854 | rfiles = true
855 | }
856 | }
857 |
858 | if rfiles {
859 | removesfiles++
860 | }
861 |
862 | if len(SysCalls) > 2 &&
863 | SysCalls[len(SysCalls)-2].Opcode == 93 && SysCalls[len(SysCalls)-1].Opcode == 10 {
864 |
865 | } else {
866 | hangssystem++
867 | }
868 | }
869 |
870 | rw.Write([]byte(fmt.Sprintf(`
871 | | %d |
872 | %s |
873 | %v |
874 | %v |
875 | %v |
876 | Link |
`, jobid, filename,
877 | len(syscalllen) != 1, len(syscalllen) != 1,
878 | removesfiles != len(syscallsamples) && removesfiles != 0, removesfiles != len(syscallsamples) && removesfiles != 0,
879 | hangssystem != len(syscallsamples), hangssystem != len(syscallsamples),
880 | jobid)))
881 |
882 | fmt.Printf("Sample: %d,\n\n Sampleset %d, \n Samples that remove files %d\nSamples that hang %d\nLendist: %v\n\n----\n", jobid, len(syscallsamples), removesfiles, hangssystem, syscalllen)
883 |
884 | }
885 |
886 | rw.Write([]byte(`
887 |
888 |
889 |
890 | `))
891 | }
892 |
893 | func listAll(rw http.ResponseWriter, req *http.Request) {
894 | conn := dbpool.Get(req.Context().Done())
895 | defer dbpool.Put(conn)
896 |
897 | stmt, err := conn.Prepare("SELECT sample_id,filename,syscalls FROM samples WHERE evaluated = 2") // ORDER BY filename DESC
898 | if err != nil {
899 | Error(rw, err)
900 | return
901 | }
902 | defer stmt.Finalize()
903 |
904 | rw.Write([]byte(`
905 |
906 |
907 |
918 |
919 |
920 |
921 | Malware listing
922 |
923 |
924 |
925 | | ID |
926 | Filename |
927 | Program Finishes |
928 | Checks Time |
929 | Modifies Files |
930 | Does something |
931 | Link |
932 |
`))
933 |
934 | for {
935 | hasRow, err := stmt.Step()
936 | if err != nil {
937 | Error(rw, err)
938 | return
939 | } else if !hasRow {
940 | log.Print("uh")
941 | break
942 | }
943 |
944 | var jobid int64
945 | var sampleName string
946 |
947 | jobid = stmt.GetInt64("sample_id")
948 | sampleName = stmt.GetText("filename")
949 | samples := stmt.GetText("syscalls")
950 | sFeatures, fCalls := getFlagsFromSample(samples)
951 |
952 | rw.Write([]byte(fmt.Sprintf(`
953 | | %d |
954 | %s |
955 | %v |
956 | %v |
957 | %v |
958 | %v |
959 | Link |
`, jobid, sampleName,
960 | sFeatures.Finishes, sFeatures.Finishes,
961 | sFeatures.LooksAtTime, sFeatures.LooksAtTime,
962 | sFeatures.WritesToFiles, sFeatures.WritesToFiles,
963 | len(fCalls) != 0, len(fCalls) != 0,
964 | jobid)))
965 |
966 | }
967 |
968 | rw.Write([]byte(`
969 |
970 |
971 |
972 | `))
973 | }
974 |
975 | func Error(rw http.ResponseWriter, err error) {
976 | log.Printf("ERR: %s ", err.Error())
977 | http.Error(rw, err.Error(), http.StatusInternalServerError)
978 | }
979 |
--------------------------------------------------------------------------------
/benx86/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "encoding/json"
7 | "flag"
8 | "fmt"
9 | "io/ioutil"
10 | "log"
11 | "net/http"
12 | "net/url"
13 | "strconv"
14 | "strings"
15 | "time"
16 |
17 | gdb "github.com/benjojo/dive-into-dos/remotegdb"
18 | "github.com/bnagy/gapstone"
19 | )
20 |
21 | type Webdata struct {
22 | Filename string
23 | SC []syscallCapture
24 | }
25 |
26 | type syscallCapture struct {
27 | Opcode int `json:"Opcode"`
28 | Time string `json:"Time"`
29 | Registers gdb.X86Registers
30 | DS, PostCode []byte
31 | Marker int
32 | PostCodeLocation int
33 | }
34 |
35 | func main() {
36 | urladdr := flag.String("url", "", "the target sample URL")
37 | flag.Parse()
38 | log.SetFlags(log.Lshortfile | log.Ltime)
39 |
40 | if *urladdr == "" {
41 | flag.Usage()
42 | return
43 | }
44 |
45 | resp, err := http.Get(*urladdr)
46 | if err != nil {
47 | log.Fatalf("Unable to fetch job, %s", err.Error())
48 | }
49 |
50 | b, _ := ioutil.ReadAll(resp.Body)
51 | WD := Webdata{}
52 | err = json.Unmarshal(b, &WD)
53 | if err != nil {
54 | log.Fatalf("Unable to decode job %s", err.Error())
55 | }
56 |
57 | utimes := make([]time.Time, 0)
58 | udates := make([]time.Time, 0)
59 | donepc := make(map[int]bool)
60 |
61 | for _, call := range WD.SC {
62 | if call.Opcode == 0x2A {
63 | if donepc[call.PostCodeLocation] {
64 | continue
65 | }
66 | donepc[call.PostCodeLocation] = true
67 |
68 | paths := SolveForDate(call)
69 | if len(paths) == 0 || len(paths) == 1 {
70 | continue
71 | }
72 | // idk handle it
73 |
74 | for _, v := range paths {
75 | udates = append(udates, v[0])
76 | }
77 | }
78 |
79 | if call.Opcode == 0x2C {
80 | if donepc[call.PostCodeLocation] {
81 | continue
82 | }
83 | donepc[call.PostCodeLocation] = true
84 |
85 | paths := SolveForTime(call)
86 | if len(paths) == 0 || len(paths) == 1 {
87 | continue
88 | }
89 |
90 | for _, v := range paths {
91 | utimes = append(utimes, v[0])
92 | }
93 |
94 | }
95 | }
96 |
97 | // now we need to calculate tests
98 |
99 | jobs := make([]SideJob, 0)
100 |
101 | bits, _ := url.Parse(*urladdr)
102 | idn, _ := strconv.ParseInt(bits.Query().Get("id"), 10, 64)
103 |
104 | if len(utimes) == 0 && len(udates) != 0 {
105 | // Compile for dates
106 | for _, v := range udates {
107 | SJ := SideJob{}
108 | SJ.DateBased = true
109 | SJ.TimeBased = false
110 |
111 | SJ.Day = v.Day()
112 | SJ.Year = v.Year()
113 | SJ.Month = int(v.Month())
114 | SJ.OriginalID = int(idn)
115 |
116 | jobs = append(jobs, SJ)
117 | }
118 |
119 | } else if len(udates) == 0 && len(utimes) != 0 {
120 | // Compile for time
121 |
122 | for _, v := range utimes {
123 | SJ := SideJob{}
124 | SJ.DateBased = false
125 | SJ.TimeBased = true
126 |
127 | SJ.Hour = v.Hour()
128 | SJ.Min = v.Minute()
129 | SJ.Second = v.Second()
130 | SJ.OriginalID = int(idn)
131 |
132 | jobs = append(jobs, SJ)
133 | }
134 | } else if len(udates) == 0 && len(utimes) == 0 {
135 | // None
136 | } else {
137 | // Both
138 |
139 | for _, timeS := range utimes {
140 | for _, dateS := range utimes {
141 |
142 | SJ := SideJob{}
143 | SJ.DateBased = true
144 | SJ.TimeBased = true
145 |
146 | SJ.Day = dateS.Day()
147 | SJ.Year = dateS.Year()
148 | SJ.Month = int(dateS.Month())
149 |
150 | SJ.Hour = timeS.Hour()
151 | SJ.Min = timeS.Minute()
152 | SJ.Second = timeS.Second()
153 | SJ.OriginalID = int(idn)
154 |
155 | jobs = append(jobs, SJ)
156 | }
157 | }
158 | }
159 |
160 | log.Printf("Puzzle solver results:")
161 | for _, v := range jobs {
162 | log.Printf("%#v\n", v)
163 |
164 | b, _ := json.Marshal(v)
165 |
166 | req, _ := http.Post("http://localhost:9998/addsidejob", "application/json", bytes.NewReader(b))
167 | req.Body.Close()
168 | }
169 |
170 | }
171 |
172 | type SideJob struct {
173 | DateBased bool
174 | Day, Month, Year, Hour, Min, Second int
175 | TimeBased bool
176 | OriginalID int
177 | SideJobID int
178 | }
179 |
180 | func SolveForTime(in syscallCapture) map[string][]time.Time {
181 | // This will emulate x86 until a different solution is found.
182 |
183 | engine, err := gapstone.New(
184 | gapstone.CS_ARCH_X86,
185 | // gapstone.CS_MODE_32,
186 | gapstone.CS_MODE_16,
187 | )
188 |
189 | defer engine.Close()
190 | maj, min := engine.Version()
191 | log.Printf("Hello Capstone! Version: %v.%v\n", maj, min)
192 | log.Printf("Code: %x", in.PostCode)
193 |
194 | insns, err := engine.Disasm(
195 | []byte(in.PostCode), // code buffer
196 | uint64(in.PostCodeLocation), // starting address
197 | 0, // insns to disassemble, 0 for all
198 | )
199 |
200 | if err != nil {
201 | log.Fatalf("unable to disasm: %s", err.Error())
202 | }
203 |
204 | log.Printf("Disasm:\n")
205 | for _, insn := range insns {
206 | log.Printf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
207 | // buffer += fmt.Sprintf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
208 | }
209 |
210 | DHmonth := 1 // 1-12
211 | DLday := 1 // 1-31
212 |
213 | testingdate := time.Date(1980, time.Month(DHmonth), DLday, 0, 0, 0, 0, time.Local)
214 |
215 | dateToPath := make(map[string][]time.Time)
216 |
217 | for {
218 | Dhour, Dmins, Dseconds := testingdate.Hour(), testingdate.Minute(), testingdate.Second()
219 | // and run the test here
220 |
221 | injectRegisters := in.Registers
222 | injectRegisters = setRegister("ch", uint16(Dhour), injectRegisters)
223 | injectRegisters = setRegister("cl", uint16(Dmins), injectRegisters)
224 | injectRegisters = setRegister("dh", uint16(Dseconds), injectRegisters)
225 | injectRegisters = setRegister("dl", uint16(0), injectRegisters)
226 |
227 | path := runFunctionWithRegisters(insns, in.PostCodeLocation, injectRegisters)
228 |
229 | pstr := fmt.Sprintf("%v", path)
230 | timearray := dateToPath[pstr]
231 | if timearray == nil {
232 | timearray = make([]time.Time, 0)
233 | }
234 | timearray = append(timearray, testingdate)
235 | dateToPath[pstr] = timearray
236 |
237 | // Done?
238 | // set it again for the loop
239 |
240 | testingdate = testingdate.Add(time.Second)
241 | if testingdate.Day() != 1 {
242 | break
243 | }
244 | }
245 |
246 | log.Printf("Paths:")
247 | for k, v := range dateToPath {
248 | log.Printf("For the path: %s", k)
249 | if len(v) > 20 {
250 | log.Printf("\n\nDate: %v\n", v[:20])
251 | } else {
252 | log.Printf("\n\nDate: %v\n", v)
253 |
254 | }
255 | }
256 |
257 | return dateToPath
258 | }
259 |
260 | func SolveForDate(in syscallCapture) map[string][]time.Time {
261 | // This will emulate x86 until a different solution is found.
262 |
263 | engine, err := gapstone.New(
264 | gapstone.CS_ARCH_X86,
265 | // gapstone.CS_MODE_32,
266 | gapstone.CS_MODE_16,
267 | )
268 |
269 | defer engine.Close()
270 | maj, min := engine.Version()
271 | log.Printf("Hello Capstone! Version: %v.%v\n", maj, min)
272 | log.Printf("Code: %x", in.PostCode)
273 |
274 | insns, err := engine.Disasm(
275 | []byte(in.PostCode), // code buffer
276 | uint64(in.PostCodeLocation), // starting address
277 | 0, // insns to disassemble, 0 for all
278 | )
279 |
280 | if err != nil {
281 | log.Fatalf("unable to disasm: %s", err.Error())
282 | }
283 |
284 | log.Printf("Disasm:\n")
285 | for _, insn := range insns {
286 | log.Printf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
287 | // buffer += fmt.Sprintf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
288 | }
289 |
290 | t, err := lazyScan(insns)
291 | if err == nil {
292 | log.Printf("Possible solution: %q", t)
293 | } else {
294 | log.Printf("failed")
295 | }
296 |
297 | DHmonth := 1 // 1-12
298 | DLday := 1 // 1-31
299 |
300 | testingdate := time.Date(1980, time.Month(DHmonth), DLday, 5, 5, 5, 5, time.Local)
301 |
302 | dateToPath := make(map[string][]time.Time)
303 |
304 | for {
305 | ALdayofweek := int(testingdate.Weekday())
306 | Dyear, DHmonth, DLday := testingdate.Date()
307 | // and run the test here
308 |
309 | injectRegisters := in.Registers
310 | injectRegisters.Al = uint8(ALdayofweek)
311 | injectRegisters = setRegister("al", uint16(injectRegisters.Al), injectRegisters)
312 | // injectRegisters.Dx = binary.LittleEndian.Uint16([]byte{byte(DLday), byte(DHmonth)})
313 | injectRegisters = setRegister("dl", uint16(byte(DLday)), injectRegisters)
314 | injectRegisters = setRegister("dh", uint16(byte(DHmonth)), injectRegisters)
315 | injectRegisters.Ecx = uint32(Dyear)
316 | injectRegisters = setRegister("cx", uint16(Dyear), injectRegisters)
317 |
318 | path := runFunctionWithRegisters(insns, in.PostCodeLocation, injectRegisters)
319 |
320 | pstr := fmt.Sprintf("%v", path)
321 | timearray := dateToPath[pstr]
322 | if timearray == nil {
323 | timearray = make([]time.Time, 0)
324 | }
325 | timearray = append(timearray, testingdate)
326 | dateToPath[pstr] = timearray
327 |
328 | // Done?
329 | // set it again for the loop
330 |
331 | Dyear, DHmonth, DLday = testingdate.AddDate(0, 0, 1).Date()
332 | testingdate = time.Date(Dyear, time.Month(DHmonth), DLday, 5, 5, 5, 5, time.Local)
333 | if Dyear == 2005 {
334 | break
335 | }
336 | }
337 |
338 | log.Printf("Paths:")
339 | for k, v := range dateToPath {
340 | log.Printf("For the path: %s", k)
341 | if len(v) > 20 {
342 | log.Printf("\n\nDate: %v\n", v[:20])
343 | } else {
344 | log.Printf("\n\nDate: %v\n", v)
345 |
346 | }
347 | }
348 |
349 | return dateToPath
350 | }
351 |
352 | func lazyScan(opcodes []gapstone.Instruction) (t time.Time, err error) {
353 | log.Printf("YOOOOOOOOOOOOOOOOOOOOOOOOO")
354 |
355 | dateRegisters := map[string]bool{"al": true, "ax": true, "cx": true, "dh": true, "dl": true, "dx": true}
356 | for _, v := range opcodes {
357 | if v.Mnemonic == "cmp" {
358 | parts := strings.Split(v.OpStr, ",")
359 | for k, v := range parts {
360 | parts[k] = strings.Trim(v, " \t\n\r")
361 | }
362 |
363 | if !dateRegisters[parts[0]] {
364 | // It does not compare a date register
365 | continue
366 | }
367 |
368 | // If we are at this point then we are CMPing date registers.
369 |
370 | if parts[0] == "ax" {
371 | log.Printf("a CMP with a AX comparison for date, this is not traceable due to lack of context")
372 | return time.Time{}, nil
373 | }
374 |
375 | if parts[0] == "al" {
376 | // Day of week!
377 | day := 14
378 | for {
379 | tdate := time.Date(1995, time.Month(2), day, 5, 5, 5, 0, time.Local)
380 | targetDOW, err := parsePram(parts[1])
381 |
382 | // simpleint, err := strconv.ParseInt(parts[1], 10, 64)
383 | // if err != nil {
384 | // // must be an addr
385 | // simpleint = int64(resolveAddress(parts[1]))
386 | if err != nil {
387 | log.Printf("failed to parse CMP, %s", parts[1])
388 | break
389 | }
390 | // }
391 |
392 | if int(targetDOW) == int(tdate.Weekday()) {
393 | // Cool, that' works for me
394 | log.Printf("DOW trigger, %q", tdate.Weekday())
395 | return tdate, nil
396 | }
397 | day++
398 | if day == 30 {
399 | break
400 | }
401 | }
402 | log.Printf("failed to find date needed for AL")
403 | return time.Time{}, fmt.Errorf("Unable to find solution")
404 | }
405 |
406 | if parts[0] == "cx" {
407 | targetyear, err := parsePram(parts[1])
408 | if err != nil {
409 | log.Printf("failed to parse CMP, %s", parts[1])
410 | return time.Time{}, fmt.Errorf("Unable to find solution")
411 | }
412 | tdate := time.Date(int(targetyear), time.Month(2), 14, 5, 5, 5, 0, time.Local)
413 | log.Printf("Year trigger, %d", targetyear)
414 | return tdate, nil
415 | }
416 |
417 | if parts[0] == "dh" {
418 | month, err := parsePram(parts[1])
419 | if err != nil {
420 | log.Printf("failed to parse CMP, %s", parts[1])
421 | return time.Time{}, fmt.Errorf("Unable to find solution")
422 | }
423 | tdate := time.Date(1995, time.Month(month), 14, 5, 5, 5, 0, time.Local)
424 | log.Printf("Month trigger, %d", month)
425 | return tdate, nil
426 | }
427 |
428 | if parts[0] == "dl" {
429 | day, err := parsePram(parts[1])
430 | if err != nil {
431 | log.Printf("failed to parse CMP, %s", parts[1])
432 | return time.Time{}, fmt.Errorf("Unable to find solution")
433 | }
434 | tdate := time.Date(1995, 1, int(day), 5, 5, 5, 0, time.Local)
435 | log.Printf("Day trigger, %d", day)
436 | return tdate, nil
437 | }
438 |
439 | if parts[0] == "dx" {
440 | // Oh. Shit.
441 |
442 | // They are checking a combo of day and month in a single cmp.
443 | dx, err := parsePram(parts[1])
444 | if err != nil {
445 | log.Printf("failed to parse CMP, %s", parts[1])
446 | return time.Time{}, fmt.Errorf("Unable to find solution")
447 | }
448 | // log.Printf("YOOOOOOOOOOOOOOOOOOOOOOOOO")
449 | urgh := make([]byte, 2)
450 | binary.LittleEndian.PutUint16(urgh, uint16(dx))
451 | log.Printf("DX check %#v %x", parts, urgh)
452 |
453 | tdate := time.Date(1995, time.Month(urgh[1]), int(urgh[0]), 5, 5, 5, 0, time.Local)
454 | log.Printf("Day and Month trigger, %d the %d", time.Month(urgh[1]), int(urgh[0]))
455 |
456 | return tdate, nil
457 | }
458 |
459 | }
460 | return time.Time{}, fmt.Errorf("Unable to find solution")
461 | // Fuck it lol
462 | }
463 |
464 | return time.Time{}, fmt.Errorf("Unable to find solution")
465 | }
466 |
467 | func parsePram(in string) (int, error) {
468 | simpleint, err := strconv.ParseInt(in, 10, 64)
469 | if err != nil {
470 | // must be an addr
471 | simpleint = int64(resolveAddress(in))
472 | if simpleint == 999999999 {
473 | log.Printf("failed to parse pram, %s", in)
474 | return 0, fmt.Errorf("Failed to get anything reasonable")
475 | }
476 | return int(simpleint), nil
477 | }
478 | return int(simpleint), nil
479 | }
480 |
481 | var maxopcodes = 10000
482 |
483 | type eflags struct {
484 | CF, PF, AF, ZF, SF, TF, IF, DF, OF bool
485 | }
486 |
487 | // Runs a x86 snippet and logs where it is going until it reaches the end or the max op code limit
488 | func runFunctionWithRegisters(opcodes []gapstone.Instruction, startloc int, oreg gdb.X86Registers) []uint {
489 | execpath := make([]uint, 0)
490 | ptr := 0
491 | OpsRan := 0
492 |
493 | CPUFlags := eflags{}
494 | CMPOp1 := uint16(0)
495 | CMPOp2 := uint16(0)
496 |
497 | state := oreg
498 | for {
499 | OpsRan++
500 | if OpsRan > maxopcodes {
501 | return execpath
502 | }
503 |
504 | execpath = append(execpath, opcodes[ptr].Address) // log path
505 |
506 | // clean up the parts of the opstr for later use
507 | parts := strings.Split(opcodes[ptr].OpStr, ",")
508 | for k, v := range parts {
509 | parts[k] = strings.Trim(v, " \t\n\r")
510 | }
511 |
512 | // log.Printf("0x%x:\t%s\t\t%s\n", opcodes[ptr].Address, opcodes[ptr].Mnemonic, opcodes[ptr].OpStr)
513 |
514 | for _, v := range parts {
515 | if strings.Contains(v, "ptr") {
516 | log.Printf("data access to a memory pointer happened, this can't be simulated, exiting.")
517 | return execpath // Unhappy path
518 | }
519 | }
520 |
521 | // Check if it's a conditional jump
522 | switch opcodes[ptr].Mnemonic {
523 | case "jz", "je":
524 | if (CMPOp1 - CMPOp2) == 0 {
525 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
526 | if nextptr != -1 {
527 | ptr = nextptr
528 | continue
529 | } else {
530 | return execpath
531 | }
532 | }
533 | ptr++
534 | continue
535 | case "ja":
536 | if CMPOp1 > CMPOp2 {
537 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
538 | if nextptr != -1 {
539 | ptr = nextptr
540 | continue
541 | } else {
542 | return execpath
543 | }
544 | }
545 | ptr++
546 | continue
547 | case "jne":
548 | // log.Printf("JNE does %x != %x", CMPOp1, CMPOp2)
549 | if CMPOp1 != CMPOp2 {
550 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
551 | if nextptr != -1 {
552 | ptr = nextptr
553 | continue
554 | } else {
555 | return execpath
556 | }
557 | }
558 | ptr++
559 | continue
560 | case "jmp":
561 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
562 | if nextptr != -1 {
563 | ptr = nextptr
564 | continue
565 | } else {
566 | return execpath
567 | }
568 | case "jb":
569 | if CMPOp1 < CMPOp2 {
570 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
571 | if nextptr != -1 {
572 | ptr = nextptr
573 | continue
574 | } else {
575 | return execpath
576 | }
577 | }
578 | ptr++
579 | continue
580 | case "jae":
581 | if CMPOp1 < CMPOp2 || CMPOp1 == CMPOp2 {
582 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
583 | if nextptr != -1 {
584 | ptr = nextptr
585 | continue
586 | } else {
587 | return execpath
588 | }
589 | }
590 | ptr++
591 | continue
592 | case "jbe":
593 | if CMPOp1 > CMPOp2 || CMPOp1 == CMPOp2 {
594 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
595 | if nextptr != -1 {
596 | ptr = nextptr
597 | continue
598 | } else {
599 | return execpath
600 | }
601 | }
602 | ptr++
603 | continue
604 | case "jl":
605 | if CMPOp1 < CMPOp2 {
606 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
607 | if nextptr != -1 {
608 | ptr = nextptr
609 | continue
610 | } else {
611 | return execpath
612 | }
613 | }
614 | ptr++
615 | continue
616 | case "jge":
617 | if CMPOp1 > CMPOp2 || CMPOp1 == CMPOp2 {
618 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
619 | if nextptr != -1 {
620 | ptr = nextptr
621 | continue
622 | } else {
623 | return execpath
624 | }
625 | }
626 | ptr++
627 | continue
628 | case "jg":
629 | if CMPOp1 > CMPOp2 {
630 | nextptr := FindAddressPtr(opcodes, resolveAddress(opcodes[ptr].OpStr))
631 | if nextptr != -1 {
632 | ptr = nextptr
633 | continue
634 | } else {
635 | return execpath
636 | }
637 | }
638 | ptr++
639 | continue
640 | case "loop":
641 | // Will almost never end well, end simulation
642 | return execpath
643 | case "lea":
644 | // Will almost never end well, end simulation
645 | return execpath
646 | case "ret", "call", "les", "out", "iret":
647 | // End the simulation, since not possible
648 | return execpath
649 | }
650 |
651 | // Check if it's maths
652 | switch opcodes[ptr].Mnemonic {
653 | case "cmp":
654 | // ok, so it is normally formualted like:
655 | // cmp cx, 0x7bc
656 | // or
657 | // cmp dx, cx
658 |
659 | // so we will split the ops in two and then I guess run some
660 | // test maths on them, CPU flags are going to be a pain but that's
661 | // just like in the world of x86!
662 |
663 | if len(parts) != 2 {
664 | log.Fatalf("there is a more than two pram CMP! WTF?! %#v", parts)
665 | }
666 |
667 | if isRegister(parts[0]) {
668 | CMPOp1 = resolveRegister(parts[0], state)
669 | } else if resolveAddress(parts[0]) != 999999999 {
670 | // It's an address! neat.
671 | CMPOp1 = uint16(resolveAddress(parts[0]))
672 | } else {
673 | log.Printf("Unsupported CMP %#v", parts)
674 | return execpath
675 | }
676 |
677 | if isRegister(parts[1]) {
678 | CMPOp2 = resolveRegister(parts[1], state)
679 | } else if resolveAddress(parts[1]) != 999999999 {
680 | // It's an address! neat.
681 | CMPOp2 = uint16(resolveAddress(parts[1]))
682 | } else {
683 | log.Printf("Unsupported CMP %#v", parts)
684 | return execpath
685 | }
686 |
687 | ptr++
688 | continue
689 | case "sub":
690 | if len(parts) != 2 {
691 | log.Fatalf("there is a more than two pram SUB! WTF?! %#v", parts)
692 | }
693 |
694 | CMPOp1 = resolveRegister(parts[0], state)
695 | if isRegister(parts[1]) {
696 | CMPOp2 = resolveRegister(parts[1], state)
697 | } else {
698 | CMPOp2 = uint16(resolveAddress(parts[1]))
699 | }
700 |
701 | state = setRegister(parts[0], CMPOp1-CMPOp2, state)
702 | ptr++
703 | continue
704 | case "inc":
705 | CMPOp1 = resolveRegister(parts[0], state)
706 | state = setRegister(parts[0], CMPOp1+1, state)
707 | ptr++
708 | continue
709 | case "or":
710 | if len(parts) != 2 {
711 | log.Fatalf("there is a more than two pram ADD! WTF?! %#v", parts)
712 | }
713 |
714 | CMPOp1 = resolveRegister(parts[0], state)
715 | if isRegister(parts[1]) {
716 | CMPOp2 = resolveRegister(parts[1], state)
717 | } else {
718 | CMPOp2 = uint16(resolveAddress(parts[1]))
719 | }
720 |
721 | state = setRegister(parts[0], CMPOp1|CMPOp2, state)
722 | ptr++
723 | continue
724 | case "xor":
725 | if len(parts) != 2 {
726 | log.Fatalf("there is a more than two pram ADD! WTF?! %#v", parts)
727 | }
728 |
729 | CMPOp1 = resolveRegister(parts[0], state)
730 | if isRegister(parts[1]) {
731 | CMPOp2 = resolveRegister(parts[1], state)
732 | } else {
733 | CMPOp2 = uint16(resolveAddress(parts[1]))
734 | }
735 |
736 | state = setRegister(parts[0], CMPOp1^CMPOp2, state)
737 | ptr++
738 | continue
739 | case "add":
740 | if len(parts) != 2 {
741 | log.Fatalf("there is a more than two pram ADD! WTF?! %#v", parts)
742 | }
743 |
744 | CMPOp1 = resolveRegister(parts[0], state)
745 | if isRegister(parts[1]) {
746 | CMPOp2 = resolveRegister(parts[1], state)
747 | } else {
748 | CMPOp2 = uint16(resolveAddress(parts[1]))
749 | }
750 |
751 | state = setRegister(parts[0], CMPOp1+CMPOp2, state)
752 | ptr++
753 | continue
754 | case "and":
755 | if len(parts) != 2 {
756 | log.Fatalf("there is a more than two pram ADD! WTF?! %#v", parts)
757 | }
758 |
759 | CMPOp1 = resolveRegister(parts[0], state)
760 | if isRegister(parts[1]) {
761 | CMPOp2 = resolveRegister(parts[1], state)
762 | } else {
763 | CMPOp2 = uint16(resolveAddress(parts[1]))
764 | }
765 |
766 | state = setRegister(parts[0], CMPOp1&CMPOp2, state)
767 | ptr++
768 | continue
769 |
770 | case "dec":
771 | CMPOp1 = resolveRegister(parts[0], state)
772 | state = setRegister(parts[0], CMPOp1-1, state)
773 | ptr++
774 | continue
775 | case "shl":
776 | // Fuck this lol
777 | if len(parts) != 2 {
778 | log.Fatalf("there is a more than two pram SHL! WTF?! %#v", parts)
779 | }
780 |
781 | CMPOp1 = resolveRegister(parts[0], state)
782 | if isRegister(parts[1]) {
783 | CMPOp2 = resolveRegister(parts[1], state)
784 | } else {
785 | CMPOp2 = uint16(resolveAddress(parts[1]))
786 | }
787 |
788 | if CMPOp2 == 1 {
789 | state = setRegister(parts[0], CMPOp1*2, state)
790 | } else {
791 | for i := uint16(0); i < CMPOp2; i++ {
792 | CMPOp1 = CMPOp1 * 2
793 | }
794 | state = setRegister(parts[0], CMPOp1, state)
795 | }
796 | ptr++
797 | continue
798 | case "mul":
799 | if len(parts) != 1 {
800 | log.Fatalf("Invalid MUL! %#v", parts)
801 | }
802 |
803 | CMPOp1 = resolveRegister("al", state)
804 | if isRegister(parts[0]) {
805 | CMPOp2 = resolveRegister(parts[0], state)
806 | } else {
807 | CMPOp2 = uint16(resolveAddress(parts[0]))
808 | }
809 |
810 | state = setRegister("ax", CMPOp1*CMPOp2, state)
811 | ptr++
812 | continue
813 | // case "not":
814 | // case "test":
815 | case "cld":
816 | CPUFlags.DF = false
817 | ptr++
818 | continue
819 |
820 | // case "xchg":
821 | //https://www.felixcloutier.com/x86/XCHG.html
822 |
823 | case "nop", "cli", "sti":
824 | // Nice
825 | ptr++
826 | continue
827 | }
828 |
829 | // // Stack
830 | // // Check if it's maths
831 | // switch opcodes[ptr].Mnemonic {
832 | // case "pop":
833 | // case "push":
834 | // case "pushf":
835 | // // Push Flags
836 | // }
837 |
838 | // Mov:
839 | if opcodes[ptr].Mnemonic == "mov" {
840 | if isRegister(parts[1]) {
841 | CMPOp2 = resolveRegister(parts[1], state)
842 | } else {
843 | CMPOp2 = uint16(resolveAddress(parts[1]))
844 | }
845 |
846 | state = setRegister(parts[0], CMPOp2, state)
847 | ptr++
848 | continue
849 | }
850 |
851 | if opcodes[ptr].Mnemonic == "stosw" {
852 | // https://www.felixcloutier.com/x86/STOS:STOSB:STOSW:STOSD:STOSQ.html
853 | }
854 |
855 | //log.Printf("Bailing out since I've hit an unsupported opcode %s\t%s", opcodes[ptr].Mnemonic, opcodes[ptr].OpStr)
856 | break
857 | }
858 |
859 | return execpath
860 | }
861 |
862 | func setRegister(register string, value uint16, g gdb.X86Registers) gdb.X86Registers {
863 | if !isRegister(register) {
864 | log.Printf("!!! Register set with a invalid register name! %s", register)
865 | return g
866 | }
867 |
868 | switch register {
869 | case "ax":
870 | g.Eax = uint32(value)
871 | return g
872 | case "bx":
873 | g.Ebx = uint32(value)
874 | return g
875 | case "cx":
876 | g.Ecx = uint32(value)
877 | return g
878 | case "dx":
879 | g.Edx = uint32(value)
880 | return g
881 |
882 | case "ah":
883 | reg := make([]byte, 2)
884 | binary.LittleEndian.PutUint16(reg, uint16(g.Eax))
885 | // AL:AH
886 | u16 := binary.LittleEndian.Uint16([]byte{reg[0], byte(value)})
887 | g.Eax = uint32(u16)
888 | return g
889 |
890 | case "al":
891 | reg := make([]byte, 2)
892 | binary.LittleEndian.PutUint16(reg, uint16(g.Eax))
893 | // AL:AH
894 | u16 := binary.LittleEndian.Uint16([]byte{byte(value), reg[1]})
895 | g.Eax = uint32(u16)
896 | return g
897 |
898 | case "bh":
899 | reg := make([]byte, 2)
900 | binary.LittleEndian.PutUint16(reg, uint16(g.Ebx))
901 | // AL:AH
902 | u16 := binary.LittleEndian.Uint16([]byte{reg[0], byte(value)})
903 | g.Ebx = uint32(u16)
904 | return g
905 | case "bl":
906 | reg := make([]byte, 2)
907 | binary.LittleEndian.PutUint16(reg, uint16(g.Ebx))
908 | // AL:AH
909 | u16 := binary.LittleEndian.Uint16([]byte{byte(value), reg[1]})
910 | g.Ebx = uint32(u16)
911 | return g
912 |
913 | case "ch":
914 | reg := make([]byte, 2)
915 | binary.LittleEndian.PutUint16(reg, uint16(g.Ecx))
916 | // AL:AH
917 | u16 := binary.LittleEndian.Uint16([]byte{reg[0], byte(value)})
918 | g.Ecx = uint32(u16)
919 | return g
920 | case "cl":
921 | reg := make([]byte, 2)
922 | binary.LittleEndian.PutUint16(reg, uint16(g.Ecx))
923 | // AL:AH
924 | u16 := binary.LittleEndian.Uint16([]byte{byte(value), reg[1]})
925 | g.Ecx = uint32(u16)
926 | return g
927 |
928 | case "dh":
929 | reg := make([]byte, 2)
930 | binary.LittleEndian.PutUint16(reg, uint16(g.Edx))
931 | // DL:DH
932 | u16 := binary.LittleEndian.Uint16([]byte{reg[0], byte(value)})
933 | g.Edx = uint32(u16)
934 | return g
935 |
936 | case "dl":
937 | reg := make([]byte, 2)
938 | binary.LittleEndian.PutUint16(reg, uint16(g.Edx))
939 | // DL:DH
940 | u16 := binary.LittleEndian.Uint16([]byte{byte(value), reg[1]})
941 | g.Edx = uint32(u16)
942 | return g
943 |
944 | case "bp":
945 | g.Ebp = uint32(value)
946 | return g
947 |
948 | case "si":
949 | g.Esi = uint32(value)
950 | return g
951 |
952 | case "di":
953 | g.Edi = uint32(value)
954 | return g
955 |
956 | case "sp":
957 | g.Esp = uint32(value)
958 | return g
959 |
960 | }
961 |
962 | log.Fatalf("Boom, Unknown register, still got past filter")
963 | return g
964 |
965 | }
966 |
967 | func isRegister(in string) bool {
968 | if in == "ax" || in == "ah" || in == "al" ||
969 | in == "bh" || in == "bl" || in == "bx" ||
970 | in == "ch" || in == "cl" || in == "cx" ||
971 | in == "dh" || in == "dl" || in == "dx" ||
972 | in == "bp" || in == "si" || in == "di" || in == "sp" {
973 | return true
974 | }
975 | return false
976 | }
977 |
978 | func resolveRegister(register string, g gdb.X86Registers) uint16 {
979 | switch register {
980 | case "ax":
981 | return uint16(g.Eax)
982 | case "bx":
983 | return uint16(g.Ebx)
984 | case "cx":
985 | return uint16(g.Ecx)
986 | case "dx":
987 | return uint16(g.Edx)
988 |
989 | // lol you are now fucked
990 |
991 | case "ah":
992 | reg := make([]byte, 2)
993 | binary.LittleEndian.PutUint16(reg, uint16(g.Eax))
994 | return uint16(reg[1])
995 |
996 | case "al":
997 | reg := make([]byte, 2)
998 | binary.LittleEndian.PutUint16(reg, uint16(g.Eax))
999 | return uint16(reg[0])
1000 |
1001 | case "bh":
1002 | reg := make([]byte, 2)
1003 | binary.LittleEndian.PutUint16(reg, uint16(g.Ebx))
1004 | return uint16(reg[1])
1005 |
1006 | case "bl":
1007 | reg := make([]byte, 2)
1008 | binary.LittleEndian.PutUint16(reg, uint16(g.Ebx))
1009 | return uint16(reg[0])
1010 |
1011 | case "ch":
1012 | reg := make([]byte, 2)
1013 | binary.LittleEndian.PutUint16(reg, uint16(g.Ecx))
1014 | return uint16(reg[1])
1015 |
1016 | case "cl":
1017 | reg := make([]byte, 2)
1018 | binary.LittleEndian.PutUint16(reg, uint16(g.Ecx))
1019 | return uint16(reg[0])
1020 |
1021 | case "dh":
1022 | reg := make([]byte, 2)
1023 | binary.LittleEndian.PutUint16(reg, uint16(g.Edx))
1024 | return uint16(reg[1])
1025 |
1026 | case "dl":
1027 | reg := make([]byte, 2)
1028 | binary.LittleEndian.PutUint16(reg, uint16(g.Edx))
1029 | return uint16(reg[0])
1030 |
1031 | case "bp":
1032 | return uint16(g.Ebp)
1033 |
1034 | case "si":
1035 | return uint16(g.Esi)
1036 |
1037 | case "di":
1038 | return uint16(g.Edi)
1039 |
1040 | case "sp":
1041 | return uint16(g.Esp)
1042 |
1043 | }
1044 | log.Fatalf("UNKNOWN REGISTER %s", register)
1045 | return 0
1046 | }
1047 |
1048 | func resolveAddress(in string) uint {
1049 | if !strings.HasPrefix(in, "0x") {
1050 | i, err := strconv.ParseInt(in, 10, 64)
1051 | if err == nil {
1052 | return uint(i)
1053 | }
1054 | return 999999999
1055 | }
1056 | // log.Printf("Hello from 0x parsing land")
1057 | s := strings.Split(in, "x")
1058 | if len(s) != 2 {
1059 | return 999999999
1060 | }
1061 |
1062 | if len(s[1])%2 == 1 {
1063 | // we need to pad one
1064 | s[1] = "0" + s[1]
1065 | }
1066 |
1067 | i, err := strconv.ParseUint(s[1], 16, 20)
1068 | if err != nil {
1069 | return 999999999
1070 | }
1071 |
1072 | return uint(i)
1073 | }
1074 |
1075 | func FindAddressPtr(opcodes []gapstone.Instruction, target uint) int {
1076 | for k, v := range opcodes {
1077 | if v.Address == target {
1078 | return k
1079 | }
1080 | }
1081 |
1082 | return -1
1083 | }
1084 |
--------------------------------------------------------------------------------