├── COPYING
├── CREDITS
├── README
├── README.md
├── ext_gdb
└── sync.py
├── ext_ida
├── SyncPlugin.py
├── broker.py
└── dispatcher.py
├── ext_lldb
└── sync.py
├── ext_olly2
├── Ollydbg.lib
├── Sync.vcxproj
├── plugin.h
├── sync.c
├── tunnel.c
└── tunnel.h
├── ext_windbg
└── sync
│ ├── make.rb
│ ├── makefile
│ ├── outputcallback.cpp
│ ├── outputcallbacks.h
│ ├── sources
│ ├── sources.props
│ ├── sync.cpp
│ ├── sync.def
│ ├── sync.h
│ ├── sync.sln
│ ├── sync.vcxproj
│ ├── tunnel.cpp
│ └── tunnel.h
└── ext_x64dbg
├── x64dbg_sync.sln
└── x64dbg_sync
├── core.cpp
├── core.h
├── sync.cpp
├── sync.h
├── tunnel.cpp
├── tunnel.h
├── x64dbg_sync.vcxproj
├── x64dbg_sync.vcxproj.filters
└── x64dbg_sync.vcxproj.user
/CREDITS:
--------------------------------------------------------------------------------
1 | N: Alexandre Gazet
2 | E: contact (_at_) company (_dot_) com
3 | D: author
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quarkslab/qb-sync/30740ebb5946f8265c82b091ec93302b49219892/README
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | qb-sync
2 | =======
3 |
4 | qb-sync is an open source tool to add some helpful glue between IDA Pro and Windbg.
5 | Its core feature is to dynamically synchronize IDA's graph windows with Windbg's position.
6 |
--------------------------------------------------------------------------------
/ext_gdb/sync.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2012-2014, Quarkslab.
3 | #
4 | # This file is part of qb-sync.
5 | #
6 | # qb-sync is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # This program is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with this program. If not, see .
18 | #
19 |
20 | #!/usr/bin/env python
21 | # -*- coding: utf-8 -*-
22 |
23 | import os
24 | import re
25 | import sys
26 | import time
27 | import socket
28 | import errno
29 | import base64
30 | import tempfile
31 | import threading
32 | import gdb
33 | try:
34 | import configparser
35 | except ImportError:
36 | import ConfigParser as configparser
37 |
38 |
39 | VERBOSE = 0
40 |
41 | HOST = "localhost"
42 | PORT = 9100
43 |
44 | TIMER_PERIOD = 0.2
45 |
46 |
47 | # function gdb_execute courtesy of StalkR
48 | # Wrapper when gdb.execute(cmd, to_string=True) does not work
49 | def gdb_execute(cmd):
50 | f = tempfile.NamedTemporaryFile()
51 | gdb.execute("set logging file %s" % f.name)
52 | gdb.execute("set logging redirect on")
53 | gdb.execute("set logging overwrite")
54 | gdb.execute("set logging on")
55 |
56 | try:
57 | gdb.execute(cmd)
58 | except Exception as e:
59 | gdb.execute("set logging off")
60 | f.close()
61 | raise e
62 |
63 | gdb.execute("set logging off")
64 | s = open(f.name, "r").read()
65 | f.close()
66 | return s
67 |
68 |
69 | def get_pid():
70 | inferiors = gdb.inferiors()
71 | for inf in gdb.inferiors():
72 | if inf.is_valid():
73 | return inf.pid
74 |
75 | raise Exception("get_pid(): failed to find program's pid")
76 |
77 |
78 | def get_maps(verbose=True):
79 | "Return list of maps (start, end, permissions, file name) via /proc"
80 | pid = get_pid()
81 | if pid is False:
82 | if verbose:
83 | print("Program not started")
84 | return []
85 | maps = []
86 |
87 | mapping = gdb_execute('info proc mappings')
88 | try:
89 | for line in mapping.splitlines():
90 | e = [x for x in line.strip().split() if x != '']
91 | if (not e) or (len(e) < 5):
92 | continue
93 | else:
94 | if not e[0].startswith('0x'):
95 | continue
96 |
97 | name = (' ').join(e[4:])
98 | e = e[:4] + [name]
99 | start, end, size, offset, name = e
100 | maps.append([int(start, 16), int(end, 16), int(size, 16), name])
101 |
102 | except Exception as e:
103 | print(e)
104 | print("[sync] failed to parse info proc mappings")
105 |
106 | return maps
107 |
108 |
109 | def get_mod_by_addr(maps, addr):
110 | for mod in maps:
111 | if (addr > mod[0]) and (addr < mod[1]):
112 | return [mod[0], mod[3]]
113 | return None
114 |
115 |
116 | def get_mod_by_name(maps, name):
117 | for mod in maps:
118 | if os.path.basename(mod[3]) == name:
119 | return [mod[0], mod[3]]
120 | return None
121 |
122 |
123 | def get_pc():
124 | try:
125 | pc_str = str(gdb.parse_and_eval("$pc"))
126 | except Exception as e:
127 | # debugger may not be running: 'No registers':
128 | return None
129 |
130 | return int((pc_str.split(" ")[0]), 16)
131 |
132 |
133 | class Tunnel():
134 |
135 | def __init__(self, host):
136 | try:
137 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
138 | self.sock.connect((host, PORT))
139 | except socket.error as msg:
140 | self.sock.close()
141 | self.sock = None
142 | self.sync = False
143 | print("[sync] Tunnel initialization error: %s" % msg)
144 | return None
145 |
146 | self.sync = True
147 |
148 | def is_up(self):
149 | return (self.sock != None and self.sync == True)
150 |
151 | def poll(self):
152 | if not self.is_up():
153 | return None
154 |
155 | self.sock.setblocking(False)
156 |
157 | try:
158 | msg = self.sock.recv(4096).decode()
159 | except socket.error as e:
160 | err = e.args[0]
161 | if (err == errno.EAGAIN or err == errno.EWOULDBLOCK):
162 | return '\n'
163 | else:
164 | self.close()
165 | return None
166 |
167 | self.sock.setblocking(True)
168 | return msg
169 |
170 | def send(self, msg):
171 | if not self.sock:
172 | print("[sync] tunnel_send: tunnel is unavailable (did you forget to sync ?)")
173 | return
174 |
175 | try:
176 | self.sock.send(msg.encode())
177 | except socket.error as msg:
178 | print(msg)
179 | self.sync = False
180 | self.close()
181 |
182 | print("[sync] tunnel_send error: %s" % msg)
183 |
184 | def close(self):
185 | if self.is_up():
186 | self.send("[notice]{\"type\":\"dbg_quit\",\"msg\":\"dbg disconnected\"}\n")
187 |
188 | if self.sock:
189 | try:
190 | self.sock.close()
191 | except socket.error as msg:
192 | print("[sync] tunnel_close error: %s" % msg)
193 |
194 | self.sync = False
195 | self.sock = None
196 |
197 |
198 | # run commands
199 | # from https://sourceware.org/gdb/onlinedocs/gdb/Basic-Python.html#Basic-Python
200 | # GDB is not thread-safe. If your Python program uses multiple threads,
201 | # you must be careful to only call GDB-specific functions in the GDB thread.
202 | # post_event ensures this.
203 | class Runner():
204 |
205 | def __init__(self, batch):
206 | self.batch = batch
207 |
208 | def __call__(self):
209 | for cmd in self.batch:
210 | if (cmd == ''):
211 | continue
212 | gdb.execute(cmd, True, False)
213 |
214 |
215 | # periodically poll socket in a dedicated thread
216 | class Poller(threading.Thread):
217 |
218 | def __init__(self, sync):
219 | threading.Thread.__init__(self)
220 | self.evt_enabled = threading.Event()
221 | self.evt_enabled.clear()
222 | self.evt_stop = threading.Event()
223 | self.evt_stop.clear()
224 | self.sync = sync
225 |
226 | def run(self):
227 | while True:
228 | if self.evt_stop.is_set():
229 | break
230 |
231 | self.evt_enabled.wait()
232 |
233 | if not self.sync.tunnel:
234 | break
235 |
236 | if self.sync.tunnel.is_up():
237 | self.poll()
238 |
239 | time.sleep(TIMER_PERIOD)
240 |
241 | def poll(self):
242 | msg = self.sync.tunnel.poll()
243 | if msg:
244 | batch = [cmd.strip() for cmd in msg.split('\n') if cmd]
245 | if batch:
246 | gdb.post_event(Runner(batch))
247 | else:
248 | gdb.post_event(Runner(['syncoff']))
249 | self.stop()
250 |
251 | def enable(self):
252 | self.evt_enabled.set()
253 |
254 | def disable(self):
255 | self.evt_enabled.clear()
256 |
257 | def stop(self):
258 | self.evt_stop.set()
259 |
260 |
261 | class Sync(gdb.Command):
262 |
263 | def __init__(self):
264 | gdb.Command.__init__(self, "sync", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
265 | self.pid = None
266 | self.maps = None
267 | self.base = None
268 | self.offset = None
269 | self.tunnel = None
270 | self.poller = None
271 | gdb.events.exited.connect(self.exit_handler)
272 | gdb.events.cont.connect(self.cont_handler)
273 | gdb.events.stop.connect(self.stop_handler)
274 | gdb.events.new_objfile.connect(self.newobj_handler)
275 |
276 | print("[sync] commands added")
277 |
278 | def identity(self):
279 | f = tempfile.NamedTemporaryFile()
280 | gdb.execute("shell uname -svm > %s" % f.name)
281 | id = open(f.name, 'r').read()
282 | f.close()
283 | return id.strip()
284 |
285 | def mod_info(self, addr):
286 | if not self.maps:
287 | self.maps = get_maps()
288 | if not self.maps:
289 | print("[sync] failed to get maps")
290 | return None
291 |
292 | return get_mod_by_addr(self.maps, addr)
293 |
294 | def locate(self):
295 | offset = get_pc()
296 | if not offset:
297 | print("")
298 | return
299 |
300 | if not self.pid:
301 | self.pid = get_pid()
302 | if not self.pid:
303 | print("[sync] failed to get pid")
304 | return
305 | else:
306 | print("[sync] pid: %s" % self.pid)
307 |
308 | self.offset = offset
309 | mod = self.mod_info(self.offset)
310 | if mod:
311 | if VERBOSE >= 2:
312 | print("[sync] mod found")
313 | print(mod)
314 |
315 | base, sym = mod
316 |
317 | if self.base != base:
318 | self.tunnel.send("[notice]{\"type\":\"module\",\"path\":\"%s\"}\n" % sym)
319 | self.base = base
320 |
321 | self.tunnel.send("[sync]{\"type\":\"loc\",\"base\":%d,\"offset\":%d}\n" % (self.base, self.offset))
322 | else:
323 | print("[sync] unknown module at 0x%x" % self.offset)
324 | self.base = None
325 | self.offset = None
326 |
327 | def create_poll_timer(self):
328 | if not self.poller:
329 | self.poller = Poller(self)
330 | self.poller.start()
331 |
332 | def release_poll_timer(self):
333 | if self.poller:
334 | self.poller.stop()
335 | self.poller = None
336 |
337 | def newobj_handler(self, event):
338 | # force a new capture
339 | self.maps = None
340 |
341 | def cont_handler(self, event):
342 | if self.tunnel:
343 | self.poller.disable()
344 | return ''
345 |
346 | def stop_handler(self, event):
347 | if self.tunnel:
348 | self.locate()
349 | self.poller.enable()
350 | return ''
351 |
352 | def exit_handler(self, event):
353 | self.reset_state()
354 | print("[sync] exit, sync finished")
355 |
356 | def reset_state(self):
357 | try:
358 | self.release_poll_timer()
359 |
360 | if self.tunnel:
361 | self.tunnel.close()
362 | self.tunnel = None
363 |
364 | self.pid = None
365 | self.maps = None
366 | self.base = None
367 | self.offset = None
368 | except Exception as e:
369 | print(e)
370 |
371 | def invoke(self, arg, from_tty):
372 | if self.tunnel and not self.tunnel.is_up():
373 | self.tunnel = None
374 |
375 | if not self.tunnel:
376 | if arg == "":
377 | arg = HOST
378 |
379 | self.tunnel = Tunnel(arg)
380 | if not self.tunnel.is_up():
381 | print("[sync] sync failed")
382 | return
383 |
384 | id = self.identity()
385 | self.tunnel.send("[notice]{\"type\":\"new_dbg\",\"msg\":\"dbg connect - %s\",\"dialect\":\"gdb\"}\n" % id)
386 | print("[sync] sync is now enabled with host %s" % str(arg))
387 | self.create_poll_timer()
388 | else:
389 | print('(update)')
390 |
391 | self.locate()
392 | self.poller.enable()
393 |
394 |
395 | class Syncoff(gdb.Command):
396 |
397 | def __init__(self, sync):
398 | gdb.Command.__init__(self, "syncoff", gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
399 | self.sync = sync
400 |
401 | def invoke(self, arg, from_tty):
402 | self.sync.reset_state()
403 | print("[sync] sync is now disabled")
404 |
405 |
406 | class Cmt(gdb.Command):
407 |
408 | def __init__(self, sync):
409 | gdb.Command.__init__(self, "cmt", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
410 | self.sync = sync
411 |
412 | def invoke(self, arg, from_tty):
413 | if not self.sync.base:
414 | print("[sync] process not synced, command is dropped")
415 | return
416 |
417 | if arg == "":
418 | print("[sync] usage: cmt [-a 0xBADF00D] ")
419 | return
420 |
421 | self.sync.tunnel.send("[sync]{\"type\":\"cmt\",\"msg\":\"%s\",\"base\":%d,\"offset\":%d}\n" %
422 | (arg, self.sync.base, self.sync.offset))
423 |
424 |
425 | class Fcmt(gdb.Command):
426 |
427 | def __init__(self, sync):
428 | gdb.Command.__init__(self, "fcmt", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
429 | self.sync = sync
430 |
431 | def invoke(self, arg, from_tty):
432 | if not self.sync.base:
433 | print("[sync] process not synced, command is dropped")
434 | return
435 |
436 | self.sync.tunnel.send("[sync]{\"type\":\"fcmt\",\"msg\":\"%s\",\"base\":%d,\"offset\":%d}\n" %
437 | (arg, self.sync.base, self.sync.offset))
438 |
439 |
440 | class Rcmt(gdb.Command):
441 |
442 | def __init__(self, sync):
443 | gdb.Command.__init__(self, "rcmt", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
444 | self.sync = sync
445 |
446 | def invoke(self, arg, from_tty):
447 | if not self.sync.base:
448 | print("[sync] process not synced, command is dropped")
449 | return
450 |
451 | self.sync.tunnel.send("[sync]{\"type\":\"rcmt\",\"msg\":\"%s\",\"base\":%d,\"offset\":%d}\n" %
452 | (arg, self.sync.base, self.sync.offset))
453 |
454 |
455 | class Translate(gdb.Command):
456 |
457 | def __init__(self, sync):
458 | gdb.Command.__init__(self, "translate", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
459 | self.sync = sync
460 |
461 | def invoke(self, arg, from_tty):
462 | if not self.sync.base:
463 | print("[sync] process not synced, command is dropped")
464 | return
465 |
466 | base, address, module = [a.strip() for a in arg.split(" ")]
467 | maps = get_maps()
468 | if not maps:
469 | print("[sync] failed to get maps")
470 | return None
471 |
472 | mod = get_mod_by_name(maps, module)
473 | if not mod:
474 | print("[sync] failed to locate module %s" % module)
475 | return None
476 |
477 | mod_base, mod_sym = mod
478 | rebased = int(address, 16) - int(base, 16) + mod_base
479 | print("[sync] module %s based at 0x%x, rebased address: 0x%x\n" % (mod_sym, mod_base, rebased))
480 |
481 |
482 | class Bc(gdb.Command):
483 |
484 | def __init__(self, sync):
485 | gdb.Command.__init__(self, "bc", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
486 | self.sync = sync
487 |
488 | def invoke(self, arg, from_tty):
489 | if not self.sync.base:
490 | print("[sync] process not synced, command is dropped")
491 | return
492 |
493 | if arg == "":
494 | arg = "oneshot"
495 |
496 | if not (arg in ["on", "off", "oneshot"]):
497 | print("[sync] usage: bc <|on|off>")
498 | return
499 |
500 | self.sync.tunnel.send("[notice]{\"type\":\"bc\",\"msg\":\"%s\",\"base\":%d,\"offset\":%d}\n" %
501 | (arg, self.sync.base, self.sync.offset))
502 |
503 |
504 | class Cmd(gdb.Command):
505 |
506 | def __init__(self, sync):
507 | gdb.Command.__init__(self, "cmd", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
508 | self.sync = sync
509 |
510 | def invoke(self, arg, from_tty):
511 | if not self.sync.base:
512 | print("[sync] process not synced, command is dropped")
513 | return
514 |
515 | if arg == "":
516 | print("[sync] usage: cmd ")
517 | cmd_output = gdb_execute(arg).encode('ascii')
518 | b64_output = base64.b64encode(cmd_output).decode()
519 | self.sync.tunnel.send("[sync] {\"type\":\"cmd\",\"msg\":\"%s\", \"base\":%d,\"offset\":%d}\n" % (b64_output, self.sync.base, self.sync.offset))
520 | print("[sync] command output:\n%s" % cmd_output.strip())
521 |
522 |
523 | class Help(gdb.Command):
524 |
525 | def __init__(self):
526 | gdb.Command.__init__(self, "synchelp", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE)
527 |
528 | def invoke(self, arg, from_tty):
529 | print(
530 | """[sync] extension commands help:
531 | > sync = synchronize with or the default value
532 | > syncoff = stop synchronization
533 | > cmt [-a address] = add comment at current eip (or [addr]) in IDA
534 | > rcmt [-a address] = reset comments at current eip (or [addr]) in IDA
535 | > fcmt [-a address] = add a function comment for 'f = get_func(eip)' (or [addr]) in IDA
536 | > cmd = execute command and add its output as comment at current eip in IDA
537 | > bc = enable/disable path coloring in IDA
538 | color a single instruction at current eip if called without argument
539 | > translate = rebase an address with respect to local module's base\n\n""")
540 |
541 |
542 | if __name__ == "__main__":
543 |
544 | locations = [os.path.join(os.path.realpath(os.path.dirname(__file__)), ".sync"),
545 | os.path.join(os.environ['HOME'], ".sync")]
546 |
547 | for confpath in locations:
548 | if os.path.exists(confpath):
549 | config = configparser.SafeConfigParser({'host': HOST, 'port': PORT})
550 | config.read(confpath)
551 | HOST = config.get("INTERFACE", 'host')
552 | PORT = config.getint("INTERFACE", 'port')
553 | print("[sync] configuration file loaded %s:%s" % (HOST, PORT))
554 | break
555 |
556 | sync = Sync()
557 | Syncoff(sync)
558 | Cmt(sync)
559 | Rcmt(sync)
560 | Fcmt(sync)
561 | Bc(sync)
562 | Translate(sync)
563 | Cmd(sync)
564 | Help()
565 |
--------------------------------------------------------------------------------
/ext_ida/broker.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2012-2015, Quarkslab.
3 | #
4 | # This file is part of qb-sync.
5 | #
6 | # qb-sync is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # This program is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with this program. If not, see .
18 | #
19 |
20 | #!/usr/bin/env python
21 | # -*- coding: utf-8 -*-
22 |
23 | import os
24 | import sys
25 | import time
26 | import re
27 | import shlex
28 | import argparse
29 | import subprocess
30 | import socket
31 | import select
32 | import binascii
33 | import ConfigParser
34 |
35 | try:
36 | import json
37 | except:
38 | print "[-] failed to import json\n%s" % repr(sys.exc_info())
39 | sys.exit(0)
40 |
41 |
42 | RUN_DISPATCHER_MAX_ATTEMPT = 4
43 | HOST = "localhost"
44 | PORT = 9100
45 |
46 | # default value is current script's path
47 | DISPATCHER_PATH = os.path.join(os.path.realpath(os.path.dirname(__file__)), "dispatcher.py")
48 | if not os.path.exists(DISPATCHER_PATH):
49 | print "[-] dispatcher path is not properly set, current value: <%s>" % DISPATCHER_PATH
50 | sys.exit(0)
51 |
52 |
53 | class Client():
54 |
55 | def __init__(self, s):
56 | self.sock = s
57 | self.buffer = ''
58 |
59 | def feed(self, data):
60 | batch = []
61 | self.buffer = ''.join([self.buffer, data])
62 | if self.buffer.endswith("\n"):
63 | batch = [req for req in self.buffer.strip().split('\n') if req != '']
64 | self.buffer = ''
65 |
66 | return batch
67 |
68 |
69 | class BrokerSrv():
70 |
71 | def puts(self, msg):
72 | print msg
73 | sys.stdout.flush()
74 |
75 | def announcement(self, msg):
76 | self.puts("[sync]{\"type\":\"broker\",\"subtype\":\"msg\",\"msg\":\"%s\"}\n" % msg)
77 |
78 | def notice_idb(self, msg):
79 | self.puts("[sync]{\"type\":\"broker\",\"subtype\":\"notice\",\"port\":\"%d\"}\n" % msg)
80 |
81 | def notice_dispatcher(self, type, args=None):
82 | if args:
83 | notice = "[notice]{\"type\":\"%s\",%s}\n" % (type, args)
84 | else:
85 | notice = "[notice]{\"type\":\"%s\"}\n" % (type)
86 |
87 | self.notify_socket.sendall(notice)
88 |
89 | def bind(self):
90 | self.srv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
91 | self.srv_sock.bind(('localhost', 0))
92 | self.srv_port = self.srv_sock.getsockname()[1]
93 |
94 | def run_dispatcher(self):
95 | cmdline = "\"%s\" -u \"%s\"" % (os.path.join(PYTHON_PATH, PYTHON_BIN), DISPATCHER_PATH)
96 | tokenizer = shlex.shlex(cmdline)
97 | tokenizer.whitespace_split = True
98 | args = [arg.replace('\"', '') for arg in list(tokenizer)]
99 |
100 | try:
101 | proc = subprocess.Popen(args, shell=False, close_fds=True)
102 | pid = proc.pid
103 | except:
104 | pid = None
105 | self.announcement("failed to run dispatcher")
106 |
107 | time.sleep(0.2)
108 | return pid
109 |
110 | def notify(self):
111 | for attempt in range(RUN_DISPATCHER_MAX_ATTEMPT):
112 | try:
113 | self.notify_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
114 | self.notify_socket.connect((HOST, PORT))
115 | break
116 | except:
117 | self.notify_socket.close()
118 | if (attempt != 0):
119 | self.announcement("failed to connect to dispatcher (attempt %d)" % (attempt))
120 | if (attempt == (RUN_DISPATCHER_MAX_ATTEMPT - 1)):
121 | self.announcement("failed to connect to dispatcher, too much attempts, exiting...")
122 | sys.exit()
123 |
124 | self.announcement("dispatcher not found, trying to run it")
125 | pid = self.run_dispatcher()
126 | if pid:
127 | self.announcement("dispatcher now runs with pid: %d" % (pid))
128 |
129 | time.sleep(0.1)
130 | self.notice_dispatcher("new_client", "\"port\":%d,\"idb\":\"%s\"" % (self.srv_port, self.name))
131 | self.announcement('connected to dispatcher')
132 | self.notice_idb(self.srv_port)
133 |
134 | def accept(self):
135 | new_socket, addr = self.srv_sock.accept()
136 | self.clients_list.append(Client(new_socket))
137 | self.opened_sockets.append(new_socket)
138 |
139 | def close(self, s):
140 | client = [client for client in self.clients_list if (client.sock == s)]
141 | if len(client) == 1:
142 | self.clients_list.remove(client[0])
143 | s.close()
144 | self.opened_sockets.remove(s)
145 |
146 | def recvall(self, client):
147 | try:
148 | data = client.sock.recv(4096)
149 | if data == '':
150 | raise
151 | except:
152 | self.announcement("dispatcher connection error, quitting")
153 | sys.exit()
154 |
155 | return client.feed(data)
156 |
157 | def req_dispatcher(self, s, hash):
158 | subtype = hash['subtype']
159 | if (subtype == 'msg'):
160 | msg = hash['msg']
161 | self.announcement("dispatcher msg: %s" % msg)
162 |
163 | def req_cmd(self, s, hash):
164 | cmd = hash['cmd']
165 | self.notice_dispatcher("cmd", "\"cmd\":\"%s\"" % cmd)
166 |
167 | def req_kill(self, s, hash):
168 | self.notice_dispatcher("kill")
169 | self.announcement("received kill notice")
170 | for s in ([self.srv_sock] + self.opened_sockets):
171 | s.close()
172 | sys.exit()
173 |
174 | def parse_exec(self, s, req):
175 | if not (req[0:8] == '[notice]'):
176 | self.puts(req)
177 | return
178 |
179 | req = self.normalize(req, 8)
180 |
181 | try:
182 | hash = json.loads(req)
183 | except:
184 | print "[-] broker failed to parse json\n %s" % repr(req)
185 | return
186 |
187 | type = hash['type']
188 | if not type in self.req_handlers:
189 | print ("[*] broker unknown request: %s" % type)
190 | return
191 |
192 | req_handler = self.req_handlers[type]
193 | req_handler(s, hash)
194 |
195 | def normalize(self, req, taglen):
196 | req = req[taglen:]
197 | req = req.replace("\\", "\\\\")
198 | req = req.replace("\n", "")
199 | return req
200 |
201 | def handle(self, s):
202 | client = [client for client in self.clients_list if (client.sock == s)]
203 | if len(client) == 1:
204 | batch = self.recvall(client[0])
205 | else:
206 | self.announcement("socket error")
207 | raise Exception("rabbit eating the cable")
208 |
209 | for req in batch:
210 | if req != '':
211 | self.parse_exec(s, req)
212 |
213 | def loop(self):
214 | self.srv_sock.listen(5)
215 | while True:
216 | rlist, wlist, xlist = select.select([self.srv_sock] + self.opened_sockets, [], [])
217 |
218 | if not rlist:
219 | self.announcement("socket error: select")
220 | raise Exception("rabbit eating the cable")
221 |
222 | for s in rlist:
223 | if s is self.srv_sock:
224 | self.accept()
225 | else:
226 | self.handle(s)
227 |
228 | def __init__(self, name):
229 | self.name = name
230 | self.opened_sockets = []
231 | self.clients_list = []
232 | self.pat = re.compile('dbg disconnected')
233 | self.req_handlers = {
234 | 'dispatcher': self.req_dispatcher,
235 | 'cmd': self.req_cmd,
236 | 'kill': self.req_kill
237 | }
238 |
239 |
240 | def err_log(msg):
241 | fd = open("%s.err" % __file__, 'w')
242 | fd.write(msg)
243 | fd.close()
244 |
245 | if __name__ == "__main__":
246 |
247 | try:
248 | PYTHON_PATH = os.environ['PYTHON_PATH']
249 | PYTHON_BIN = os.environ['PYTHON_BIN']
250 | except Exception as e:
251 | err_log("broker failed to retreive PYTHON_PATH or PYTHON_BIN value.")
252 | sys.exit()
253 |
254 | parser = argparse.ArgumentParser()
255 | parser.add_argument('--idb', nargs=1, action='store')
256 | args = parser.parse_args()
257 |
258 | if not args.idb:
259 | print "[sync] no idb argument"
260 | sys.exit()
261 |
262 | for loc in ['IDB_PATH', 'USERPROFILE', 'HOME']:
263 | if loc in os.environ:
264 | confpath = os.path.join(os.path.realpath(os.environ[loc]), '.sync')
265 | if os.path.exists(confpath):
266 | config = ConfigParser.SafeConfigParser({'port': PORT})
267 | config.read(confpath)
268 | PORT = config.getint("INTERFACE", 'port')
269 | break
270 |
271 | server = BrokerSrv(args.idb[0])
272 |
273 | try:
274 | server.bind()
275 | except Exception as e:
276 | server.announcement("failed to bind")
277 | err_log(repr(e))
278 | sys.exit()
279 |
280 | try:
281 | server.notify()
282 | except Exception as e:
283 | server.announcement("failed to notify dispatcher")
284 | err_log(repr(e))
285 | sys.exit()
286 |
287 | try:
288 | server.loop()
289 | except Exception as e:
290 | server.announcement("broker stop")
291 | err_log(repr(e))
292 |
--------------------------------------------------------------------------------
/ext_ida/dispatcher.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2012-2014, Quarkslab.
3 | #
4 | # This file is part of qb-sync.
5 | #
6 | # qb-sync is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # This program is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with this program. If not, see .
18 | #
19 |
20 | #!/usr/bin/env python
21 | # -*- coding: utf-8 -*-
22 |
23 | import os
24 | import sys
25 | import socket
26 | import select
27 | import base64
28 | import binascii
29 | import re
30 | import ConfigParser
31 | import traceback
32 |
33 | HOST = 'localhost'
34 | PORT = 9100
35 |
36 | try:
37 | import json
38 | except:
39 | print "[-] failed to import json\n%s" % repr(sys.exc_info())
40 | sys.exit(0)
41 |
42 |
43 | class Client():
44 |
45 | def __init__(self, s_client, s_srv, name):
46 | self.client_sock = s_client
47 | self.srv_sock = s_srv
48 | self.name = name
49 | self.enabled = False
50 | self.buffer = ''
51 |
52 | def close(self):
53 | self.enabled = False
54 | if self.client_sock:
55 | self.client_sock.close()
56 | if self.srv_sock:
57 | self.srv_sock.close()
58 |
59 | def feed(self, data):
60 | batch = []
61 | self.buffer = ''.join([self.buffer, data])
62 | if self.buffer.endswith("\n"):
63 | batch = [req for req in self.buffer.strip().split('\n') if req != '']
64 | self.buffer = ''
65 |
66 | return batch
67 |
68 |
69 | class DispatcherSrv():
70 |
71 | def __init__(self):
72 | self.idb_clients = []
73 | self.dbg_client = None
74 |
75 | self.srv_socks = []
76 | self.opened_socks = []
77 |
78 | self.current_dbg = None
79 | self.current_dialect = 'unknown'
80 | self.current_idb = None
81 | self.current_module = None
82 |
83 | self.sync_mode_auto = True
84 | self.pat = re.compile('dbg disconnected')
85 | self.req_handlers = {
86 | 'new_client': self.req_new_client,
87 | 'new_dbg': self.req_new_dbg,
88 | 'dbg_quit': self.req_dbg_quit,
89 | 'idb_n': self.req_idb_n,
90 | 'idb_list': self.req_idb_list,
91 | 'module': self.req_module,
92 | 'sync_mode': self.req_sync_mode,
93 | 'cmd': self.req_cmd,
94 | 'bc': self.req_bc,
95 | 'kill': self.req_kill
96 | }
97 |
98 | def bind(self, host, port):
99 | self.dbg_srv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
100 | self.dbg_srv_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
101 | self.dbg_srv_sock.bind((host, port))
102 | self.srv_socks.append(self.dbg_srv_sock)
103 |
104 | if not (socket.gethostbyname(host) == '127.0.0.1'):
105 | self.localhost_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
106 | self.localhost_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
107 | self.localhost_sock.bind(('localhost', port))
108 | self.srv_socks.append(self.localhost_sock)
109 |
110 | def accept(self, s):
111 | new_socket, addr = s.accept()
112 | self.opened_socks.append(new_socket)
113 |
114 | def listen(self):
115 | for s in self.srv_socks:
116 | s.listen(5)
117 |
118 | def close(self, s):
119 | s.close()
120 | self.opened_socks.remove(s)
121 |
122 | def loop(self):
123 | self.listen()
124 | self.announcement("dispatcher listening")
125 |
126 | while True:
127 | rlist, wlist, xlist = select.select(self.srv_socks + self.opened_socks, [], [])
128 |
129 | if not rlist:
130 | self.announcement("socket error: select")
131 | raise Exception("rabbit eating the cable")
132 |
133 | for s in rlist:
134 | if s in self.srv_socks:
135 | self.accept(s)
136 | else:
137 | self.handle(s)
138 |
139 | def handle(self, s):
140 | client = self.sock_to_client(s)
141 | for req in self.recvall(client):
142 | self.parse_exec(s, req)
143 |
144 | # find client object for its srv socket
145 | def sock_to_client(self, s):
146 | if self.current_dbg and (s == self.current_dbg.srv_sock):
147 | client = self.current_dbg
148 | else:
149 | clist = [client for client in self.idb_clients if (client.srv_sock == s)]
150 | if not clist:
151 | client = Client(None, s, None)
152 | self.idb_clients.append(client)
153 | else:
154 | client = clist[0]
155 |
156 | return client
157 |
158 | # buffered readline like function
159 | def recvall(self, client):
160 | try:
161 | data = client.srv_sock.recv(4096)
162 | if data == '':
163 | raise
164 | except:
165 | if client == self.current_dbg:
166 | self.broadcast("debugger closed the connection")
167 | self.dbg_quit()
168 | else:
169 | self.client_quit(client.srv_sock)
170 | self.broadcast("a client quit, nb client(s) left: %d" % len(self.idb_clients))
171 |
172 | return []
173 |
174 | return client.feed(data)
175 |
176 | # parse and execute requests from clients (idbs or dbg)
177 | def parse_exec(self, s, req):
178 | if not (req[0:8] == '[notice]'):
179 | # this is a normal [sync] request from debugger, forward it
180 | self.forward(req)
181 | # receive 'dbg disconnected', socket can be closed
182 | if re.search(self.pat, req):
183 | self.close(s)
184 | return
185 |
186 | req = self.normalize(req, 8)
187 | try:
188 | hash = json.loads(req)
189 | except:
190 | print "[-] dispatcher failed to parse json\n %s\n" % req
191 | return
192 |
193 | type = hash['type']
194 | if not type in self.req_handlers:
195 | print ("[*] dispatcher unknown request: %s" % type)
196 | return
197 |
198 | req_handler = self.req_handlers[type]
199 | req_handler(s, hash)
200 |
201 | def normalize(self, req, taglen):
202 | req = req[taglen:]
203 | req = req.replace("\\", "\\\\")
204 | req = req.replace("\n", "")
205 | return req
206 |
207 | def puts(self, msg, s):
208 | s.sendall(msg)
209 |
210 | # dispatcher announcements are forwarded to the idb
211 | def announcement(self, msg, s=None):
212 | if not s:
213 | if not self.current_idb:
214 | return
215 | s = self.current_idb.client_sock
216 |
217 | try:
218 | s.sendall("[notice]{\"type\":\"dispatcher\",\"subtype\":\"msg\",\"msg\":\"%s\"}\n" % msg)
219 | except:
220 | return
221 |
222 | # send message to all connected idb clients
223 | def broadcast(self, msg):
224 | for idbc in self.idb_clients:
225 | self.announcement(msg, idbc.client_sock)
226 |
227 | # send dbg message to currently active idb client
228 | def forward(self, msg, s=None):
229 | if not s:
230 | if not self.current_idb:
231 | return
232 | s = self.current_idb.client_sock
233 |
234 | if s:
235 | s.sendall(msg + "\n")
236 |
237 | # send dbg message to all idb clients
238 | def forward_all(self, msg, s=None):
239 | for idbc in self.idb_clients:
240 | self.forward(msg, idbc.client_sock)
241 |
242 | # disable current idb and enable new idb matched from current module name
243 | def switch_idb(self, new_idb):
244 | msg = "[sync]{\"type\":\"broker\",\"subtype\":\"%s\"}\n"
245 | if (not self.current_idb == new_idb) & (self.current_idb.enabled):
246 | self.current_idb.client_sock.sendall(msg % "disable_idb")
247 | self.current_idb.enabled = False
248 |
249 | if new_idb:
250 | new_idb.client_sock.sendall(msg % "enable_idb")
251 | self.current_idb = new_idb
252 | new_idb.enabled = True
253 |
254 | # a new idb client connects to the dispatcher via its broker
255 | def req_new_client(self, srv_sock, hash):
256 | port, name = hash['port'], hash['idb']
257 | try:
258 | client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
259 | client_sock.connect(('localhost', port))
260 | self.opened_socks.append(client_sock)
261 | except:
262 | self.opened_socks.remove(srv_sock)
263 | srv_sock.close()
264 | return
265 |
266 | # check if an idb client is already registered with the same name
267 | conflicting = [client for client in self.idb_clients if (client.name == name)]
268 |
269 | # promote to idb client
270 | new_client = self.sock_to_client(srv_sock)
271 | new_client.client_sock = client_sock
272 | new_client.name = name
273 | self.broadcast("add new client (listening on port %d), nb client(s): %d" % (port, len(self.idb_clients)))
274 |
275 | if conflicting:
276 | self.broadcast("conflicting name: %s !" % new_client.name)
277 |
278 | if not self.current_idb:
279 | self.current_idb = new_client
280 |
281 | # if new client match current module name, then enable it
282 | if self.current_module == name:
283 | self.switch_idb(new_client)
284 |
285 | # inform new client about debugger's dialect
286 | self.dbg_dialect(new_client)
287 |
288 | # clean state when a client is quiting
289 | def client_quit(self, s):
290 | self.opened_socks.remove(s)
291 | # remove exiting client from the list of active clients
292 | for idbc in [idbc for idbc in self.idb_clients if (idbc.srv_sock == s)]:
293 | self.idb_clients.remove(idbc)
294 | self.opened_socks.remove(idbc.client_sock)
295 | idbc.close()
296 |
297 | # no more clients, let's kill ourself
298 | if not self.idb_clients:
299 | for s in self.srv_socks:
300 | s.close()
301 | sys.exit()
302 |
303 | # a new debugger client connects to the dispatcher
304 | def req_new_dbg(self, s, hash):
305 | msg = hash['msg']
306 | if self.current_dbg:
307 | self.dbg_quit()
308 |
309 | # promote to dbg client
310 | self.current_dbg = self.sock_to_client(s)
311 | self.current_dbg.client_sock = s
312 | self.idb_clients.remove(self.current_dbg)
313 | self.broadcast("new debugger client: %s" % msg)
314 |
315 | # store dbb's dialect
316 | if 'dialect' in hash:
317 | self.current_dialect = hash['dialect']
318 |
319 | self.dbg_dialect()
320 |
321 | # inform client about debugger's dialect
322 | def dbg_dialect(self, client=None):
323 | msg = "[sync]{\"type\":\"dialect\",\"dialect\":\"%s\"}\n" % self.current_dialect
324 | if client:
325 | client.client_sock.sendall(msg)
326 | else:
327 | for idbc in self.idb_clients:
328 | idbc.client_sock.sendall(msg)
329 |
330 | # debugger client disconnect from the dispatcher
331 | def req_dbg_quit(self, s, hash):
332 | msg = hash['msg']
333 | self.broadcast("debugger quit: %s" % msg)
334 | self.dbg_quit()
335 |
336 | # clean state when debugger is quiting
337 | def dbg_quit(self):
338 | self.opened_socks.remove(self.current_dbg.srv_sock)
339 | self.current_dbg.close()
340 | self.current_dbg = None
341 | self.current_module = None
342 | self.switch_idb(None)
343 | self.current_dialect = 'unknown'
344 |
345 | # handle kill notice from a client, exit properly if no more client
346 | def req_kill(self, s, hash):
347 | self.client_quit(s)
348 | self.broadcast("received a kill notice from client, %d client(s) left" % len(self.idb_clients))
349 |
350 | # send list of currently connected idb clients
351 | def req_idb_list(self, s, hash):
352 | clist = "> currently connected idb(s):\n"
353 | if not self.idb_clients:
354 | clist += " no idb client yet\n"
355 | else:
356 | for i in range(len(self.idb_clients)):
357 | clist += (" [%d] %s\n" % (i, self.idb_clients[i].name))
358 |
359 | s.sendall(clist)
360 |
361 | # manually set current active idb to idb n from idb list
362 | def req_idb_n(self, s, hash):
363 | idb = hash['idb']
364 | try:
365 | idbn = int(idb)
366 | except:
367 | s.sendall("> n should be a decimal value")
368 | return
369 |
370 | try:
371 | idbc = self.idb_clients[idbn]
372 | except:
373 | s.sendall("> %d is invalid (see idblist)" % idbn)
374 | return
375 |
376 | self.switch_idb(idbc)
377 | s.sendall("> current idb set to %d" % idbn)
378 |
379 | # dbg notice that its current module has changed
380 | def req_module(self, s, hash):
381 | modpath = hash['path']
382 | self.current_module = modname = os.path.basename(modpath)
383 | matching = [idbc for idbc in self.idb_clients if (idbc.name.lower() == modname.lower())]
384 |
385 | if not self.sync_mode_auto:
386 | self.broadcast("sync_mode_auto off")
387 | return
388 |
389 | if len(matching) == 1:
390 | # matched is set as active
391 | self.switch_idb(matching[0])
392 | else:
393 | if not len(matching):
394 | msg = "mod request has no match for %s"
395 | else:
396 | msg = "ambiguous mod request, too many matches for %s"
397 |
398 | self.broadcast(msg % modname)
399 | # no match current idb (if existing) is disabled
400 | if self.current_idb.enabled:
401 | self.switch_idb(None)
402 |
403 | # sync mode tells if idb switch is automatic or manual
404 | def req_sync_mode(self, s, hash):
405 | mode = hash['auto']
406 | self.broadcast("sync mode auto set to %s" % mode)
407 | self.sync_mode_auto = (mode == "on")
408 |
409 | # bc request should be forwarded to all idbs
410 | def req_bc(self, s, hash):
411 | msg = "[sync]%s" % json.dumps(hash)
412 | self.forward_all(msg)
413 |
414 | def req_cmd(self, s, hash):
415 | cmd = hash['cmd']
416 | self.current_dbg.client_sock.sendall("%s\n" % cmd)
417 |
418 |
419 | def err_log(msg):
420 | fd = open("%s.err" % __file__, 'w')
421 | fd.write(msg)
422 | fd.close()
423 |
424 | if __name__ == "__main__":
425 |
426 | server = DispatcherSrv()
427 |
428 | for loc in ['IDB_PATH', 'USERPROFILE', 'HOME']:
429 | if loc in os.environ:
430 | confpath = os.path.join(os.path.realpath(os.environ[loc]), '.sync')
431 | if os.path.exists(confpath):
432 | config = ConfigParser.SafeConfigParser({'host': HOST, 'port': PORT})
433 | config.read(confpath)
434 | HOST = config.get("INTERFACE", 'host')
435 | PORT = config.getint("INTERFACE", 'port')
436 | server.announcement("configuration file loaded")
437 | break
438 |
439 | try:
440 | server.bind(HOST, PORT)
441 | except Exception as e:
442 | err_log("dispatcher failed to bind on %s:%s\n-> %s" % (HOST, PORT, repr(e)))
443 | sys.exit()
444 |
445 | try:
446 | server.loop()
447 | except Exception as e:
448 | err_log("dispatcher failed\n-> %s" % repr(e))
449 | server.announcement("dispatcher stop")
450 |
--------------------------------------------------------------------------------
/ext_lldb/sync.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2014, Cedric TESSIER
3 |
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above
13 | copyright notice, this list of conditions and the following
14 | disclaimer in the documentation and/or other materials provided
15 | with the distribution.
16 |
17 | * Neither the name of Cedric TESSIER nor the names of other
18 | contributors may be used to endorse or promote products derived
19 | from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 | """
33 | import socket
34 | import time
35 | import sys
36 | import threading
37 | import json
38 | import base64
39 | import os
40 | import ConfigParser
41 |
42 | HOST = "localhost"
43 | PORT = 9100
44 |
45 |
46 | if __name__ == "__main__":
47 | print("Run only as script from lldb... Not as standalone program")
48 | sys.exit(1)
49 |
50 | try:
51 | import lldb
52 | except:
53 | pass
54 |
55 |
56 | CMD_NOTICE = 1
57 | CMD_SYNC = 2
58 |
59 | CMD_CLS = {CMD_NOTICE: "notice", CMD_SYNC: "sync"}
60 |
61 |
62 | # TODO: factorize with GNU GDB plugin
63 | class Tunnel():
64 |
65 | def __init__(self, host):
66 | try:
67 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
68 | self.sock.connect((host, PORT))
69 | except socket.error, msg:
70 | self.sock.close()
71 | self.sock = None
72 | self.sync = False
73 | print "[sync] Tunnel initialization error: %s" % msg
74 | return None
75 |
76 | self.sync = True
77 |
78 | def is_up(self):
79 | return (self.sock != None and self.sync == True)
80 |
81 | def send(self, msg):
82 | if not self.sock:
83 | print "[sync] tunnel_send: tunnel is unavailable (did you forget to sync ?)"
84 | return
85 |
86 | try:
87 | self.sock.send(msg)
88 | except socket.error, msg:
89 | self.sync = False
90 | self.close()
91 |
92 | print "[sync] tunnel_send error: %s" % msg
93 |
94 | def close(self):
95 | if self.is_up():
96 | self.send("[notice]{\"type\":\"dbg_quit\",\"msg\":\"dbg disconnected\"}\n")
97 |
98 | if self.sock:
99 | try:
100 | self.sock.close()
101 | except socket.error, msg:
102 | print "[sync] tunnel_close error: %s" % msg
103 |
104 | self.sync = False
105 | self.sock = None
106 |
107 |
108 | class EventHandlerThread(threading.Thread):
109 | def __init__(self, sync):
110 | self.sync = sync
111 | self.process = sync.process
112 | self.listener = lldb.SBListener('qb_sync listener')
113 | self.broadcaster = self.process.GetBroadcaster()
114 | self.broadcaster.AddListener(self.listener, lldb.SBProcess.eBroadcastBitStateChanged)
115 | self.event = lldb.SBEvent()
116 | super(EventHandlerThread, self).__init__()
117 |
118 | def run(self):
119 | while self.sync._tunnel and self.process.is_alive:
120 | if self.listener.PeekAtNextEventForBroadcasterWithType(self.broadcaster,
121 | lldb.SBProcess.eBroadcastBitStateChanged, self.event):
122 | self.sync._handleNewState(self.process)
123 | self.listener.Clear()
124 | time.sleep(0.1)
125 | # Broadcast last process state
126 | self.sync._handleNewState(self.process)
127 | print "[sync] event handler stopped"
128 |
129 |
130 | class Sync(object):
131 |
132 | def __init__(self):
133 | self._tunnel = None
134 | self._pcache = {}
135 | self._dbg = lldb.debugger
136 | self._platform = self._dbg.GetSelectedPlatform()
137 |
138 | def reset(self):
139 | if self._tunnel:
140 | self._tunnel.close()
141 | self._tunnel = None
142 | self._pcache = {}
143 |
144 | def _getIdentity(self):
145 | return self._platform.GetOSDescription()
146 |
147 | identity = property(_getIdentity)
148 |
149 | def _getProcess(self):
150 | target = self._dbg.GetSelectedTarget()
151 | return target.GetProcess()
152 |
153 | def procinfo(self, process=None):
154 | if not process:
155 | process = self.process
156 | uid = process.GetUniqueID()
157 | return self._pcache.get(uid, None)
158 |
159 | process = property(_getProcess)
160 |
161 | def _locate(self, process):
162 | pinfo = self.procinfo(process)
163 | if not pinfo:
164 | return
165 |
166 | thread = process.GetSelectedThread()
167 | frame = thread.GetFrameAtIndex(0)
168 | offset = frame.pc
169 |
170 | mod = frame.GetModule()
171 |
172 | # Find first mapped section of the module
173 | base = 0
174 | for i in range(4):
175 | sect = mod.GetSectionAtIndex(i)
176 | addr = int(sect.addr)
177 | if addr:
178 | base = addr
179 | break
180 |
181 | pinfo["offset"] = offset
182 | # Notice if we changed current module
183 | if base != pinfo["base"]:
184 | pinfo["base"] = base
185 | modname = mod.GetFileSpec().fullpath
186 | self.cmd(CMD_NOTICE, "module", path=modname)
187 |
188 | self.cmd(CMD_SYNC, "loc", base=base, offset=offset)
189 |
190 | def _handleStop(self, process):
191 | if not self._tunnel:
192 | return
193 | self._locate(process)
194 |
195 | def _handleExit(self, process):
196 | self.reset()
197 | print "[sync] exit, sync finished"
198 |
199 | def _handleNewState(self, process):
200 | state = process.GetState()
201 | if state == lldb.eStateStopped:
202 | self._handleStop(process)
203 | elif state == lldb.eStateRunning:
204 | pass
205 | elif state == lldb.eStateExited:
206 | self._handleExit(process)
207 |
208 | def _connect(self, host):
209 | if self._tunnel:
210 | return True
211 | if not host:
212 | host = HOST
213 | print "[sync] connecting to %s" % host
214 | self._tunnel = Tunnel(host)
215 | if not self._tunnel.is_up():
216 | print "[sync] sync failed"
217 | self.reset()
218 | return False
219 | self.cmd(CMD_NOTICE, "new_dbg", msg="dbg connect - %s" % self.identity)
220 | print "[sync] sync is now enabled with host %s" % host
221 | return True
222 |
223 | def initialize(self, host):
224 | if not self._connect(host):
225 | return
226 | # Sync cannot do more if a process is not alive
227 | if not self.process.is_alive:
228 | return
229 | uid = self.process.GetUniqueID()
230 | if not uid in self._pcache:
231 | # Init per process cache
232 | self._pcache[uid] = {}
233 | pinfo = self._pcache[uid]
234 | pinfo["base"] = 0
235 | pinfo["offset"] = 0
236 | # Init per process event handler
237 | thread = EventHandlerThread(self)
238 | pinfo["thread"] = thread
239 | thread.start()
240 | print "[sync] event handler started"
241 |
242 | self._locate(self.process)
243 |
244 | def running(self):
245 | return self.process.is_alive
246 |
247 | def cmd(self, clas, typ, **kwargs):
248 | if not self._tunnel:
249 | return
250 | cmd = "[%s]" % CMD_CLS.get(clas, None)
251 | if not cmd:
252 | print "Invalid command class"
253 | return
254 | args = {"type": typ}
255 | args.update(kwargs)
256 | cmd += json.dumps(args) + "\n"
257 | self._tunnel.send(cmd)
258 |
259 |
260 | def getSync(session):
261 | sync = session.get("_sync", None)
262 | if not sync:
263 | print "Internal error: _sync not found"
264 | sys.exit(1)
265 | return sync
266 |
267 |
268 | def setSync(session, sync):
269 | session["_sync"] = sync
270 |
271 |
272 | # TODO: factorize with GNU GDB plugin
273 | def loadConfig():
274 | global HOST
275 | global PORT
276 |
277 | locations = [os.path.join(os.path.realpath(os.path.dirname(__file__)), ".sync"),
278 | os.path.join(os.environ['HOME'], ".sync")]
279 |
280 | for confpath in locations:
281 | if os.path.exists(confpath):
282 | config = ConfigParser.SafeConfigParser({'host': HOST, 'port': PORT})
283 | config.read(confpath)
284 | HOST = config.get("INTERFACE", 'host')
285 | PORT = config.getint("INTERFACE", 'port')
286 | print "[sync] configuration file loaded %s:%s" % (HOST, PORT)
287 | break
288 |
289 |
290 | def __lldb_init_module(debugger, session):
291 | loadConfig()
292 | sync = Sync()
293 | setSync(session, sync)
294 |
295 | # ---
296 |
297 |
298 | @lldb.command("sync", "Enable sync with IDA")
299 | def sync(debugger, command, result, session):
300 | sc = getSync(session)
301 | args = command.split()
302 | host = args[0] if args else None
303 |
304 | sc.initialize(host)
305 |
306 |
307 | @lldb.command("syncoff", "Disable sync with IDA")
308 | def syncoff(debugger, command, result, session):
309 | sc = getSync(session)
310 | sc.reset()
311 | print "[sync] sync is now disabled"
312 |
313 |
314 | @lldb.command("bc", "Enable / disable path coloring in IDA")
315 | def bc(debugger, command, result, session):
316 | sc = getSync(session)
317 | if not sc.running():
318 | print "[sync] process is not running, command is dropped"
319 | return
320 |
321 | args = command.split()
322 | arg = args[0] if args else None
323 |
324 | if not arg:
325 | arg = "oneshot"
326 |
327 | if not (arg in ["on", "off", "oneshot"]):
328 | print "[sync] usage: bc <|on|off>"
329 | return
330 | pinfo = sc.procinfo()
331 | if not pinfo:
332 | return
333 | sc.cmd(CMD_NOTICE, "bc", msg=arg, base=pinfo["base"], offset=pinfo["offset"])
334 |
335 |
336 | def addcmt(typ, debugger, command, result, session):
337 | sc = getSync(session)
338 | if not sc.running():
339 | print "[sync] process is not running, command is dropped"
340 | return
341 |
342 | if not command and typ != "rcmt":
343 | print "[sync] usage: %s " % typ
344 | return
345 |
346 | pinfo = sc.procinfo()
347 | if not pinfo:
348 | return
349 | sc.cmd(CMD_SYNC, typ, msg=command, base=pinfo["base"], offset=pinfo["offset"])
350 |
351 |
352 | @lldb.command("cmt", "Add comment in IDA")
353 | def cmt(debugger, command, result, session):
354 | return addcmt("cmt", debugger, command, result, session)
355 |
356 |
357 | @lldb.command("fcmt", "Add function comment in IDA")
358 | def fcmt(debugger, command, result, session):
359 | return addcmt("fcmt", debugger, command, result, session)
360 |
361 |
362 | @lldb.command("rcmt", "Reset comment in IDA")
363 | def rcmt(debugger, command, result, session):
364 | return addcmt("rcmt", debugger, command, result, session)
365 |
366 |
367 | @lldb.command("cmd", "Execute command and add its output as comment")
368 | def cmd(debugger, command, result, session):
369 | sc = getSync(session)
370 | if not sc.running():
371 | print "[sync] process is not running, command is dropped"
372 | return
373 |
374 | if not command:
375 | print "[sync] need a command to execute"
376 | return
377 |
378 | ci = sc._dbg.GetCommandInterpreter()
379 | res = lldb.SBCommandReturnObject()
380 |
381 | ci.HandleCommand(command, res)
382 | if res.Succeeded():
383 | out = res.GetOutput()
384 | if not out:
385 | return
386 | out = base64.b64encode(out)
387 | pinfo = sc.procinfo()
388 | if not pinfo:
389 | return
390 |
391 | sc.cmd(CMD_SYNC, "cmd", msg=out, base=pinfo["base"], offset=pinfo["offset"])
392 |
393 |
394 | @lldb.command("synchelp", "Print sync plugin help")
395 | def synchelp(debugger, command, result, session):
396 | print (
397 | """[sync] extension commands help:
398 | > sync = synchronize with or the default value
399 | > syncoff = stop synchronization
400 | > cmt = add comment at current eip in IDA
401 | > rcmt = reset comments at current eip in IDA
402 | > fcmt = add a function comment for 'f = get_func(eip)' in IDA
403 | > cmd = execute command and add its output as comment at current eip in IDA
404 | > bc = enable/disable path coloring in IDA
405 | color a single instruction at current eip if called without argument\n""")
406 |
--------------------------------------------------------------------------------
/ext_olly2/Ollydbg.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quarkslab/qb-sync/30740ebb5946f8265c82b091ec93302b49219892/ext_olly2/Ollydbg.lib
--------------------------------------------------------------------------------
/ext_olly2/Sync.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 | {FE3AC8DD-4BFC-4445-8D1D-C37502DE48C7}
15 | Win32Proj
16 | qb-sync
17 |
18 |
19 |
20 | DynamicLibrary
21 | Unicode
22 |
23 |
24 | DynamicLibrary
25 | Unicode
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | <_ProjectFileVersion>10.0.40219.1
39 | Debug\
40 | Debug\
41 | false
42 | Release\
43 | Release\
44 | false
45 | AllRules.ruleset
46 |
47 |
48 | AllRules.ruleset
49 |
50 |
51 |
52 |
53 |
54 | Disabled
55 | WIN32;_DEBUG;_WINDOWS;_USRDLL;BOOKMARK_EXPORTS;%(PreprocessorDefinitions)
56 | true
57 | EnableFastChecks
58 | MultiThreaded
59 | 1Byte
60 | /J
61 | NotUsing
62 | Level4
63 | EditAndContinue
64 | CompileAsC
65 |
66 |
67 | user32.lib
68 | %(AdditionalOptions)
69 | C:\Programs\Microsoft SDK\Lib;%(AdditionalLibraryDirectories)
70 | true
71 | Windows
72 | MachineX86
73 |
74 |
75 |
76 |
77 | WIN32;NDEBUG;_WINDOWS;_USRDLL;BOOKMARK_EXPORTS;%(PreprocessorDefinitions)
78 | MultiThreaded
79 | 1Byte
80 | /J
81 | NotUsing
82 | Level3
83 | ProgramDatabase
84 |
85 |
86 | user32.lib
87 | %(AdditionalOptions)
88 | false
89 | Windows
90 | true
91 | true
92 | MachineX86
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/ext_olly2/sync.c:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2014, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #include "plugin.h"
28 | #include "tunnel.h"
29 |
30 | #pragma comment (lib, "ws2_32.lib")
31 | #pragma comment (lib, "crypt32.lib")
32 |
33 |
34 | #define PLUGINNAME L"SyncPlugin" // Unique plugin name
35 | #define VERSION L"1.0.0" // Plugin version
36 |
37 | HINSTANCE hdllinst; // Instance of plugin DLL
38 |
39 | #define VERBOSE 0
40 | #define MAX_NAME 1024
41 | #define MAX_CMD 1024
42 | #define TIMER_PERIOD 100
43 | #define CONF_FILE "\\.sync"
44 |
45 | // Default host value is locahost
46 | static CHAR *g_DefaultHost = "127.0.0.1";
47 | static CHAR *g_DefaultPort = "9100";
48 | BOOL g_ExtConfFile = 0;
49 |
50 | // Buffer used to solve symbol's name
51 | static wchar_t g_NameBuffer[MAX_NAME];
52 | // Buffer used to receive breakpoint command
53 | static wchar_t g_CommandBuffer[MAX_CMD];
54 |
55 | // Debuggee's state
56 | ulong g_Offset = 0;
57 | ulong g_Base = 0;
58 |
59 | // Synchronisation mode
60 | static BOOL g_SyncAuto = TRUE;
61 |
62 | // Command polling feature
63 | static HANDLE g_hPollTimer;
64 | static HANDLE g_hPollCompleteEvent;
65 | static CRITICAL_SECTION g_CritSectPollRelease;
66 |
67 |
68 | HRESULT
69 | LoadConfigurationFile()
70 | {
71 | DWORD count;
72 | HRESULT hRes = S_OK;
73 | HANDLE hFile;
74 | CHAR lpProfile[MAX_PATH] = {0};
75 | LPSTR lpConfHost, lpConfPort;
76 |
77 | count = GetEnvironmentVariableA("userprofile", lpProfile, MAX_PATH);
78 | if ((count == 0) | (count > MAX_PATH))
79 | return E_FAIL;
80 |
81 | hRes = StringCbCatA(lpProfile, MAX_PATH, CONF_FILE);
82 | if FAILED(hRes)
83 | return E_FAIL;
84 |
85 | hFile = CreateFileA(lpProfile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
86 | if (hFile == INVALID_HANDLE_VALUE)
87 | return E_FAIL;
88 |
89 | CloseHandle(hFile);
90 | lpConfHost = (LPSTR) malloc(MAX_PATH);
91 | lpConfPort = (LPSTR) malloc(MAX_PATH);
92 |
93 | count = GetPrivateProfileStringA("INTERFACE", "host", "127.0.0.1", lpConfHost, MAX_PATH, lpProfile);
94 | if ((count == 0) | (count == (MAX_PATH-1)) | (count == (MAX_PATH-2)))
95 | goto failed;
96 |
97 | count = GetPrivateProfileStringA("INTERFACE", "port", "9100", lpConfPort, MAX_PATH, lpProfile);
98 | if ((count == 0) | (count == (MAX_PATH-1)) | (count == (MAX_PATH-2)))
99 | goto failed;
100 |
101 | g_DefaultHost = lpConfHost;
102 | g_DefaultPort = lpConfPort;
103 | g_ExtConfFile = 1;
104 |
105 | return hRes;
106 |
107 | failed:
108 | free(lpConfHost);
109 | free(lpConfPort);
110 |
111 | return E_FAIL;
112 | }
113 |
114 |
115 | // send a combination of WM_KEYDOWN, WM_KEYUP for a given virtual-key code
116 | void MonkeyInput(WORD wVk)
117 | {
118 | unsigned int scanCode, lParam;
119 | BOOL bRes;
120 |
121 | #if VERBOSE >= 2
122 | dbgout("[*] MonkeyInput 0x%x, hwnd 0x%x\n", wVk, hwollymain);
123 | #endif
124 |
125 | scanCode = MapVirtualKey((unsigned int)wVk, MAPVK_VK_TO_VSC);
126 | if (scanCode == 0) {
127 | dbgout("[sync] failed to MapVirtualKey (no translation)\n");
128 | goto Exit;
129 | }
130 |
131 | lParam = 0x00000001 | (scanCode << 16);
132 |
133 | bRes = PostMessage(hwollymain, WM_KEYDOWN, wVk, lParam);
134 | if (!bRes) {
135 | dbgout("[sync] failed to PostMessage (WM_KEYDOWN)\n");
136 | goto Exit;
137 | }
138 |
139 | bRes = PostMessage(hwollymain, WM_KEYUP, wVk, lParam);
140 | if (!bRes) {
141 | dbgout("[sync] failed to PostMessage (WM_KEYUP)\n");
142 | }
143 |
144 | Exit:
145 | return;
146 | }
147 |
148 |
149 | HRESULT
150 | SetBreakpoint(char *command, BOOL oneshot)
151 | {
152 | HRESULT hRes=S_OK;
153 | int res;
154 | t_result result;
155 | wchar_t *address = NULL;
156 | unsigned long type;
157 |
158 | #if VERBOSE >= 2
159 | dbgout("[sync] SetBreakpoint: %s\n", command);
160 | #endif
161 |
162 | Suspendallthreads();
163 |
164 | hRes = convert_tow(command, &address);
165 | if(FAILED(hRes)){
166 | hRes = E_FAIL;
167 | goto Exit;
168 | }
169 |
170 | res = Expression(&result, address, NULL, 0, 0, 0, 0, 0, EMOD_CHKEXTRA);
171 | if (result.datatype == EXPR_INVALID)
172 | {
173 | dbgout("[sync] SetBreakpoint: failed to evaluate Expression (0x%x)\n", res);
174 | hRes = E_FAIL;
175 | goto Exit;
176 | }
177 |
178 | type = BP_BREAK | (oneshot ? BP_ONESHOT : BP_MANUAL);
179 |
180 | res = Setint3breakpoint(result.u, type, 0, 0, 0, BA_PERMANENT, L"", L"", L"");
181 | if (res != 0)
182 | {
183 | dbgout("[sync] failed to Setint3breakpoint\n");
184 | hRes = E_FAIL;
185 | goto Exit;
186 | }
187 |
188 | Flushmemorycache();
189 |
190 | Exit:
191 | Resumeallthreads();
192 |
193 | if (address != NULL){
194 | free(address);
195 | }
196 |
197 | return hRes;
198 | }
199 |
200 |
201 | HRESULT
202 | SetHardwareBreakpoint(char *command, BOOL oneshot)
203 | {
204 | HRESULT hRes=S_OK;
205 | int res, index;
206 | t_result result;
207 | wchar_t *address = NULL;
208 | unsigned long type;
209 |
210 | #if VERBOSE >= 2
211 | dbgout("[sync] SetHardwareBreakpoint: %s\n", command);
212 | #endif
213 |
214 | Suspendallthreads();
215 |
216 | hRes = convert_tow(command, &address);
217 | if(FAILED(hRes)){
218 | hRes = E_FAIL;
219 | goto Exit;
220 | }
221 |
222 | res = Expression(&result, address, NULL, 0, 0, 0, 0, 0, EMOD_CHKEXTRA);
223 | if (result.datatype == EXPR_INVALID)
224 | {
225 | dbgout("[sync] SetHardwareBreakpoint: failed to evaluate Expression (0x%x)\n", res);
226 | hRes = E_FAIL;
227 | goto Exit;
228 | }
229 |
230 | type = BP_BREAK | BP_EXEC | BP_MANUAL;
231 |
232 | index = Findfreehardbreakslot(type);
233 | if (index == -1)
234 | {
235 | dbgout("[sync] failed to Findfreehardbreakslot\n");
236 | hRes = E_FAIL;
237 | goto Exit;
238 | }
239 |
240 | #if VERBOSE >= 2
241 | dbgout("[sync] Findfreehardbreakslot 0x%x\n", index);
242 | #endif
243 |
244 | res = Sethardbreakpoint(index, 1, result.u, type, 0, 0, 0, BA_PERMANENT, L"", L"", L"");
245 | if (res != 0)
246 | {
247 | dbgout("[sync] failed to Sethardbreakpoint\n");
248 | hRes = E_FAIL;
249 | goto Exit;
250 | }
251 |
252 | Flushmemorycache();
253 |
254 | Exit:
255 | Resumeallthreads();
256 |
257 | if (address != NULL){
258 | free(address);
259 | }
260 |
261 | return hRes;
262 | }
263 |
264 |
265 | // Poll socket for incoming commands
266 | HRESULT
267 | PollCmd()
268 | {
269 | HRESULT hRes=S_OK;
270 | int NbBytesRecvd;
271 | int ch = 0xA;
272 | char *msg, *next, *orig = NULL;
273 |
274 | hRes=TunnelPoll(&NbBytesRecvd, &msg);
275 |
276 | if (SUCCEEDED(hRes) & (NbBytesRecvd>0) & (msg != NULL))
277 | {
278 | next = orig = msg;
279 |
280 | while((msg-orig) < NbBytesRecvd)
281 | {
282 | next = strchr(msg, ch);
283 | if( next != NULL)
284 | *next = 0;
285 |
286 | // bp1, hbp, hbp1 disabled for now, thread safety issue ?
287 | // possibly need for a gdb.post_event like feature
288 |
289 | if (strncmp(msg, "si", 2) == 0) {
290 | MonkeyInput(VK_F7);
291 | }
292 | else if (strncmp(msg, "so", 2) == 0) {
293 | MonkeyInput(VK_F8);
294 | }
295 | else if (strncmp(msg, "go", 2) == 0) {
296 | MonkeyInput(VK_F9);
297 | }
298 | else if (strncmp(msg, "bp", 2) == 0) {
299 | SetBreakpoint(msg+2, FALSE);
300 | }
301 | else {
302 | dbgout("[sync] received command: %s (not yet implemented)\n", msg);
303 | }
304 |
305 | // No more command
306 | if( next == NULL)
307 | break;
308 |
309 | msg = next+1;
310 | }
311 |
312 | free(orig);
313 | }
314 |
315 | return hRes;
316 | }
317 |
318 |
319 | void ReleasePollTimer()
320 | {
321 | BOOL bRes;
322 | DWORD dwErr;
323 |
324 | EnterCriticalSection(&g_CritSectPollRelease);
325 |
326 | #if VERBOSE >= 2
327 | dbgout("[sync] ReleasePollTimer called\n");
328 | #endif
329 |
330 | if (!(g_hPollTimer==INVALID_HANDLE_VALUE))
331 | {
332 | ResetEvent(g_hPollCompleteEvent);
333 | bRes = DeleteTimerQueueTimer(NULL, g_hPollTimer, g_hPollCompleteEvent);
334 | if (bRes == 0)
335 | {
336 | // msdn: If the error code is ERROR_IO_PENDING, it is not necessary to
337 | // call this function again. For any other error, you should retry the call.
338 | dwErr = GetLastError();
339 | if (dwErr != ERROR_IO_PENDING)
340 | bRes = DeleteTimerQueueTimer(NULL, g_hPollTimer, g_hPollCompleteEvent);
341 | }
342 |
343 | g_hPollTimer = INVALID_HANDLE_VALUE;
344 | }
345 |
346 | LeaveCriticalSection(&g_CritSectPollRelease);
347 | }
348 |
349 |
350 | // Poll timer callback implementation: call PollCmd and set completion event
351 | void CALLBACK PollTimerCb(PVOID lpParameter, BOOL TimerOrWaitFired)
352 | {
353 | HRESULT hRes;
354 | UNREFERENCED_PARAMETER(lpParameter);
355 | UNREFERENCED_PARAMETER(TimerOrWaitFired);
356 |
357 | hRes = PollCmd();
358 |
359 | // If an error occured in PollCmd() the timer callback is deleted.
360 | // (typically happens when client has closed the connection)
361 | if (FAILED(hRes))
362 | ReleasePollTimer();
363 | }
364 |
365 |
366 | // Setup poll timer callback
367 | void CreatePollTimer()
368 | {
369 | BOOL bRes;
370 |
371 | bRes = CreateTimerQueueTimer(&g_hPollTimer, NULL, (WAITORTIMERCALLBACK)PollTimerCb,
372 | NULL, TIMER_PERIOD, TIMER_PERIOD, WT_EXECUTEINTIMERTHREAD);
373 | if (!(bRes))
374 | dbgout("[sync] failed to CreatePollTimer\n");
375 | }
376 |
377 |
378 | HRESULT
379 | convert_tow(const char * mbstr, PTCH *wcstr)
380 | {
381 | HRESULT hRes = S_OK;
382 | size_t returnValue;
383 | errno_t err;
384 |
385 | err = _mbstowcs_s_l(&returnValue, NULL, 0, mbstr, _TRUNCATE, CP_ACP);
386 | if (err != 0)
387 | {
388 | dbgout("[sync] _mbstowcs_s_l failed: %d\n", GetLastError());
389 | return E_FAIL;
390 | }
391 |
392 | *wcstr = (wchar_t *) malloc(returnValue+1);
393 | if (mbstr == NULL)
394 | {
395 | dbgout("[sync] convert failed to allocate buffer: %d\n", GetLastError());
396 | return E_FAIL;
397 | }
398 |
399 | err = _mbstowcs_s_l(&returnValue, *wcstr, returnValue, mbstr, _TRUNCATE, CP_ACP);
400 | if (err != 0)
401 | {
402 | dbgout("[sync] _mbstowcs_s_l failed: %d\n", GetLastError());
403 | if(!(*wcstr == NULL))
404 | free(*wcstr);
405 |
406 | return E_FAIL;
407 | }
408 |
409 | return hRes;
410 | }
411 |
412 |
413 | HRESULT convert(const wchar_t *wcstr, PSTR * mbstr)
414 | {
415 | HRESULT hRes = S_OK;
416 | size_t returnValue;
417 | errno_t err;
418 |
419 | err = _wcstombs_s_l(&returnValue, NULL, 0, wcstr, _TRUNCATE, CP_ACP);
420 | if (err != 0)
421 | {
422 | dbgout("[sync] _wcstombs_s_l failed: %d\n", GetLastError());
423 | return E_FAIL;
424 | }
425 |
426 | *mbstr = (PSTR) malloc(returnValue+1);
427 | if (mbstr == NULL)
428 | {
429 | dbgout("[sync] convert failed to allocate buffer: %d\n", GetLastError());
430 | return E_FAIL;
431 | }
432 |
433 | err = _wcstombs_s_l(&returnValue, *mbstr, returnValue, wcstr, _TRUNCATE, CP_ACP);
434 | if (err != 0)
435 | {
436 | dbgout("[sync] _wcstombs_s_l failed: %d\n", GetLastError());
437 | if(!(*mbstr == NULL))
438 | free(*mbstr);
439 |
440 | return E_FAIL;
441 | }
442 |
443 | return hRes;
444 | }
445 |
446 |
447 | //Update state and send info to client: eip module's base address, offset, name
448 | HRESULT UpdateState()
449 | {
450 | HRESULT hRes = S_OK;
451 | PSTR modname = NULL;
452 | t_module *pmod;
453 | ulong PrevBase;
454 |
455 | PrevBase = g_Base;
456 | g_Offset = run.eip;
457 | pmod = Findmodule(g_Offset);
458 |
459 | #if VERBOSE >= 2
460 | dbgout("[*] eip %08x - pmod %08x\n", g_Offset, pmod);
461 | #endif
462 |
463 | if (pmod == NULL)
464 | return E_FAIL;
465 |
466 | g_Base = pmod->base;
467 | if (g_Base != PrevBase)
468 | {
469 | wcsncpy_s(g_NameBuffer, MAX_NAME, pmod->path, _TRUNCATE);
470 |
471 | hRes = convert(g_NameBuffer, &modname);
472 | if(FAILED(hRes))
473 | return hRes;
474 |
475 | hRes=TunnelSend("[notice]{\"type\":\"module\",\"path\":\"%s\"}\n", modname);
476 | dbgout("[*] mod path %s\n", modname);
477 |
478 | free(modname);
479 |
480 | if(FAILED(hRes))
481 | return hRes;
482 | }
483 |
484 | hRes=TunnelSend("[sync]{\"type\":\"loc\",\"base\":%lu,\"offset\":%lu}\n", g_Base, g_Offset);
485 | return hRes;
486 | }
487 |
488 |
489 | static void LogSyncState()
490 | {
491 | if (g_Synchronized)
492 | Addtolist(0, DRAW_NORMAL, L"[sync] sync is enabled");
493 | else
494 | Addtolist(0, DRAW_NORMAL, L"[sync] sync is disabled");
495 | };
496 |
497 |
498 | HRESULT sync(PSTR Args)
499 | {
500 | HRESULT hRes=S_OK;
501 | PCSTR Host;
502 | PSTR pszId=NULL;
503 |
504 | // Reset global state
505 | g_Base = 0;
506 | g_Offset = 0;
507 |
508 | #if VERBOSE >= 2
509 | dbgout("[sync] sync function called\n");
510 | #endif
511 |
512 | if(g_Synchronized)
513 | {
514 | dbgout("[sync] sync update\n");
515 | UpdateState();
516 | goto exit;
517 | }
518 |
519 | if (!Args || !*Args) {
520 | dbgout("[sync] No argument found, using default host (%s:%s)\n", g_DefaultHost, g_DefaultPort);
521 | Host=g_DefaultHost;
522 | }else{
523 | Host=Args;
524 | }
525 |
526 | if(FAILED(hRes=TunnelCreate(Host, g_DefaultPort)))
527 | {
528 | dbgout("[sync] sync failed\n");
529 | goto exit;
530 | }
531 |
532 | dbgout("[sync] probing sync\n");
533 |
534 | /* Used a fixed identity
535 | if(FAILED(hRes=Identity(&pszId)))
536 | {
537 | dbgout("[sync] get identity failed\n");
538 | goto exit;
539 | }
540 | */
541 |
542 | hRes=TunnelSend("[notice]{\"type\":\"new_dbg\",\"msg\":\"dbg connect - %s\",\"dialect\":\"ollydbg2\"}\n", "Ollydbg2_sync");
543 | if(SUCCEEDED(hRes))
544 | {
545 | dbgout("[sync] sync is now enabled with host %s\n", Host);
546 | UpdateState();
547 | CreatePollTimer();
548 | LogSyncState();
549 | }
550 | else
551 | dbgout("[sync] sync aborted\n");
552 |
553 |
554 |
555 | exit:
556 | if(!(pszId==NULL))
557 | free(pszId);
558 |
559 | return hRes;
560 | }
561 |
562 |
563 | HRESULT syncoff()
564 | {
565 | HRESULT hRes=S_OK;
566 |
567 | #if VERBOSE >= 2
568 | dbgout("[sync] !syncoff command called\n");
569 | #endif
570 |
571 | if(!g_Synchronized)
572 | return hRes;
573 |
574 | ReleasePollTimer();
575 | hRes=TunnelClose();
576 |
577 | #if VERBOSE >= 2
578 | dbgout("[sync] sync is now disabled\n");
579 | #endif
580 |
581 | LogSyncState();
582 | return hRes;
583 | }
584 |
585 |
586 | // Menu function of about menu, displays About dialog.
587 | static int Mabout(t_table *pt, wchar_t *name, ulong index, int mode)
588 | {
589 | int n;
590 | wchar_t s[TEXTLEN];
591 |
592 | UNREFERENCED_PARAMETER(pt);
593 | UNREFERENCED_PARAMETER(name);
594 | UNREFERENCED_PARAMETER(index);
595 |
596 | if (mode==MENU_VERIFY)
597 | return MENU_NORMAL;
598 |
599 | else if (mode==MENU_EXECUTE)
600 | {
601 | Resumeallthreads();
602 | n=StrcopyW(s,TEXTLEN,L"qb-sync plugin ");
603 | n+=StrcopyW(s+n,TEXTLEN-n,VERSION);
604 | n+=StrcopyW(s+n,TEXTLEN-n,L"\nCopyright (C) 2012-2014 Quarkslab");
605 | Suspendallthreads();
606 |
607 | MessageBox(hwollymain,s, L"Sync plugin", MB_OK|MB_ICONINFORMATION);
608 | return MENU_NOREDRAW;
609 | };
610 | return MENU_ABSENT;
611 | };
612 |
613 |
614 | // Menablesync: enable synchronization
615 | static int Menablesync(t_table *pt, wchar_t *name, ulong index, int mode)
616 | {
617 | UNREFERENCED_PARAMETER(pt);
618 | UNREFERENCED_PARAMETER(name);
619 | UNREFERENCED_PARAMETER(index);
620 |
621 | #if VERBOSE >= 2
622 | dbgout("[sync] Menablesync - mode %x\n", mode);
623 | #endif
624 |
625 | if (mode==MENU_VERIFY)
626 | return MENU_NORMAL;
627 |
628 | else if (mode==MENU_EXECUTE)
629 | {
630 | sync(NULL);
631 | return MENU_NOREDRAW;
632 | };
633 | return MENU_ABSENT;
634 | };
635 |
636 |
637 | // Menablesync: disable synchronization
638 | static int Mdisablesync(t_table *pt, wchar_t *name, ulong index, int mode)
639 | {
640 | UNREFERENCED_PARAMETER(pt);
641 | UNREFERENCED_PARAMETER(name);
642 | UNREFERENCED_PARAMETER(index);
643 |
644 | #if VERBOSE >= 2
645 | dbgout("[sync] Mdisablesync - mode %x\n", mode);
646 | #endif
647 |
648 | if (mode==MENU_VERIFY)
649 | return MENU_NORMAL;
650 |
651 | else if (mode==MENU_EXECUTE)
652 | {
653 | syncoff();
654 | return MENU_NOREDRAW;
655 | };
656 | return MENU_ABSENT;
657 | };
658 |
659 |
660 | //
661 | // Add or edit Comment / Label at address.
662 | //
663 | static int MCommentAndLabel(t_table *pt,wchar_t *name,ulong index,int mode)
664 | {
665 | int retVal = MENU_ABSENT;
666 | wchar_t buffer[TEXTLEN];
667 | wchar_t nameBuffer[TEXTLEN];
668 |
669 | switch(mode)
670 | {
671 | //check if menu applies
672 | case MENU_VERIFY:
673 | // ordinary menu item
674 | retVal = MENU_NORMAL;
675 | break;
676 |
677 | //execute menu item
678 | case MENU_EXECUTE:
679 | {
680 | t_dump* dump;
681 | int findNameResult;
682 | int copiedBytes;
683 | ulong saveType;
684 | int column;
685 | int letter = 0;
686 | POINT point;
687 |
688 | nameBuffer[0] = L'\0';
689 |
690 | if(index == NM_COMMENT)
691 | {
692 | saveType = NM_COMMSAV;
693 | column = 3;
694 | }
695 | else if(index == NM_LABEL)
696 | {
697 | saveType = NM_LABELSAV;
698 | column = 0;
699 | }
700 |
701 | dump = (t_dump*)pt->customdata;
702 |
703 | #if VERBOSE >= 2
704 | dbgout("[*] customdata : %p\n", dump);
705 | #endif
706 |
707 | if(!dump)
708 | {
709 | dbgout("[-] Critical error: no t_dump structure !\n");
710 | break;
711 | }
712 |
713 | //suspend all threads in debuggee
714 | Suspendallthreads();
715 |
716 | #if VERBOSE >= 2
717 | // Note : if 'name' is NULL, then the comment is made by the plugin menu,
718 | // otherwise, if the shortcut key is pressed, name equals the menu name ("Comment")
719 | if(name)
720 | dbgoutW(L"[*] Name : %s\n", name);
721 |
722 | dbgoutW(L"[*] Selection address : %#p\n", dump->sel0);
723 | #endif
724 |
725 | // get table selection coords
726 | if(Gettableselectionxy(&dump->table, column, &point) < 0)
727 | {
728 | point.x = -1;
729 | point.y = -1;
730 | }
731 |
732 | //check to see if the current instruction has already a comment or a label
733 | findNameResult = FindnameW(dump->sel0, index, NULL, 0);
734 | if(findNameResult == 0)
735 | {
736 | if(index == NM_COMMENT)
737 | copiedBytes = StrcopyW(buffer, TEXTLEN, L"Add comment at ");
738 | else if(index == NM_LABEL)
739 | copiedBytes = StrcopyW(buffer, TEXTLEN, L"Add label at ");
740 | }
741 | else
742 | {
743 | FindnameW(dump->sel0, index, nameBuffer, TEXTLEN);
744 | if(index == NM_COMMENT)
745 | copiedBytes = StrcopyW(buffer, TEXTLEN, L"Edit comment at ");
746 | else if(index == NM_LABEL)
747 | copiedBytes = StrcopyW(buffer, TEXTLEN, L"Edit label at ");
748 | }
749 |
750 | // decode chosen address and append decoded address (e.g FOO.DEADBEEF) to dialog box header string. ex: "Add comment at FOO.0DEADBEEF"
751 | Decodeaddress(dump->sel0, 0, DM_MODNAME | DM_WIDEFORM, (wchar_t*)((BYTE*)buffer + (sizeof(wchar_t) * copiedBytes)), TEXTLEN - copiedBytes, NULL);
752 |
753 | // not sure what the 'letter' param for GetString() is... (at least this is a single letter put into the dialog box)
754 | //TODO: need to fix that when doc is available
755 | if(nameBuffer[0] != L'\0')
756 | letter = nameBuffer[0];
757 |
758 | //popup dialog, get user string in "nameBuffer"
759 | if(Getstring(hwollymain, buffer, nameBuffer, TEXTLEN, saveType, letter, point.x, point.y, dump->table.font, DIA_UTF8) > 0)
760 | {
761 | // insert comment or label
762 | InsertnameW(dump->sel0, index, nameBuffer);
763 |
764 | //broadcast change to olly
765 | Broadcast(0x489, 1, 0);
766 |
767 | //send to IDA (iif synch is ON)
768 | if(g_Synchronized)
769 | {
770 | PSTR args = NULL;
771 | PWSTR wargs = NULL;
772 | t_module* module;
773 | HRESULT hRes;
774 |
775 | // get module description according to current selection
776 | module = Findmodule(dump->sel0);
777 | if(!module)
778 | {
779 | dbgout("[-] Couldn't find any module for address: %#p\n", dump->sel0);
780 | goto __resumethreads;
781 | }
782 |
783 | // unicode buffer for args
784 | wargs = (wchar_t*) malloc(TEXTLEN * sizeof(wchar_t));
785 |
786 | // build arguments passed into the tunnel. e.g. "-a 0xdeadbeef this is a superduper comment" / "-a 0xdeadbeef @@my_label"
787 | wcsncpy_s(wargs, TEXTLEN, L"-a ", TEXTLEN);
788 | _snwprintf_s(buffer , TEXTLEN, TEXTLEN, L"%#lx ", dump->sel0);
789 | wcsncat_s(wargs, TEXTLEN, buffer, TEXTLEN);
790 | wcsncat_s(wargs, TEXTLEN, nameBuffer, TEXTLEN);
791 |
792 | hRes = convert(wargs, &args);
793 | if(SUCCEEDED(hRes))
794 | {
795 | // send comment to IDA
796 | if(index == NM_COMMENT)
797 | TunnelSend("[sync]{\"type\":\"cmt\",\"msg\":\"%s\",\"base\":%lu,\"offset\":%lu}\n", args, module->base, dump->sel0);
798 | else if (index == NM_LABEL)// send label to IDA
799 | TunnelSend("[sync]{\"type\":\"lbl\",\"msg\":\"%s\",\"base\":%lu,\"offset\":%lu}\n", args, module->base, dump->sel0);
800 | }
801 |
802 | // whatever happened, free the buffers
803 | if(wargs)
804 | free(wargs); wargs = NULL;
805 | if(args)
806 | free(args); args = NULL;
807 | } //end if(g_Synchronized)
808 | }//end Getstring()
809 |
810 | //resume all threads in debuggee
811 | __resumethreads:
812 | Resumeallthreads();
813 |
814 | // force window to redraw
815 | retVal = MENU_REDRAW;
816 | break;
817 | }// end case MENU_EXECUTE
818 | }//end switch
819 |
820 | return retVal;
821 | };
822 |
823 |
824 | // Plugin menu that will appear in the main OllyDbg menu.
825 | // Define two shortcuts:
826 | // "ctrl+s" to enable synchronization
827 | // "ctrl+u" to disable synchronization
828 | static t_menu mainmenu[] = {
829 | { L"Enable sync (Ctrl+s)",
830 | L"Enable sync (Ctrl+s)",
831 | KK_DIRECT|KK_CTRL|0x53 , Menablesync, NULL, 0 },
832 | { L"Disable sync (Ctrl+u)",
833 | L"Disable sync (Ctrl+u)",
834 | KK_DIRECT|KK_CTRL|0x55, Mdisablesync, NULL, 0 },
835 | { L"|About",
836 | L"About Sync plugin",
837 | K_NONE, Mabout, NULL, 0 },
838 | { NULL, NULL, K_NONE, NULL, NULL, 0 }
839 | };
840 |
841 |
842 | // Plugin menu that will appear in "Disasm" Window
843 | // Define two shortcuts:
844 | // "ctrl + ;" to enable comment synchro
845 | // "ctrl + :" to enable label synchro
846 | static t_menu disasmmenu[] = {
847 | { L"[Sync] Comment",
848 | L"Synchronize comment",
849 | KK_DIRECT /* shortcut appears in menu */| KK_CHAR /* must be processed as char, otherwise ';' is not taken */| KK_CTRL | ';',
850 | MCommentAndLabel,
851 | NULL,
852 | NM_COMMENT
853 | },
854 | { L"[Sync] Label",
855 | L"Synchronize label",
856 | KK_DIRECT | KK_CHAR | KK_CTRL | ':',
857 | MCommentAndLabel,
858 | NULL,
859 | NM_LABEL
860 | },
861 | { NULL, NULL, K_NONE, NULL, NULL, 0 }
862 | };
863 |
864 | // Plugin menu that will appear in "Dump" Window
865 | // Define one shortcut:
866 | // "ctrl + :" to enable label synchro
867 | static t_menu dumpmenu[] = {
868 | { L"[Sync] Label",
869 | L"Synchronize label",
870 | KK_DIRECT | KK_CHAR | KK_CTRL | ':',
871 | MCommentAndLabel,
872 | NULL,
873 | NM_LABEL
874 | },
875 | { NULL, NULL, K_NONE, NULL, NULL, 0 }
876 | };
877 | // Adds items either to main OllyDbg menu (type=PWM_MAIN)
878 | extc t_menu * __cdecl ODBG2_Pluginmenu(wchar_t *type) {
879 | if (wcscmp(type,PWM_MAIN)==0)
880 | return mainmenu;
881 | else if(wcscmp(type, PWM_DISASM) == 0)
882 | return disasmmenu;
883 | else if (wcscmp(type, PWM_DUMP) == 0)
884 | return dumpmenu;
885 | return NULL; // No menu
886 | };
887 |
888 |
889 | // Entry point of the plugin DLL.
890 | BOOL WINAPI DllEntryPoint(HINSTANCE hi, DWORD reason, LPVOID reserved)
891 | {
892 | UNREFERENCED_PARAMETER(reserved);
893 |
894 | if (reason==DLL_PROCESS_ATTACH)
895 | hdllinst=hi;
896 | return 1;
897 | };
898 |
899 | // ODBG2_Pluginquery:
900 | // - check whether given OllyDbg version is correctly supported
901 | // - fill plugin name and plugin version (as UNICODE strings) and
902 | // return version of expected plugin interface.
903 | extc int __cdecl ODBG2_Pluginquery(int ollydbgversion, ulong *features,
904 | wchar_t pluginname[SHORTNAME], wchar_t pluginversion[SHORTNAME])
905 | {
906 | UNREFERENCED_PARAMETER(features);
907 |
908 | #if VERBOSE >= 2
909 | dbgout("[*] ODBG2_Pluginquery\n");
910 | #endif
911 | if (ollydbgversion<201)
912 | return 0;
913 |
914 | wcscpy_s(pluginname, SHORTNAME, PLUGINNAME);
915 | wcscpy_s(pluginversion, SHORTNAME, VERSION);
916 | return PLUGIN_VERSION;
917 | };
918 |
919 | // ODBG2_Plugininit: one-time initializations and resources allocation
920 | extc int __cdecl ODBG2_Plugininit(void)
921 | {
922 | #if VERBOSE >= 2
923 | dbgout("[*] ODBG2_Plugininit\n");
924 | #endif
925 |
926 | g_Synchronized = 0;
927 | g_Base = 0;
928 |
929 | g_hPollCompleteEvent = CreateEvent(NULL, 1, 0, NULL);
930 | if (g_hPollCompleteEvent == NULL)
931 | {
932 | dbgout("[sync] Command polling feature init failed\n");
933 | return E_FAIL;
934 | }
935 |
936 | InitializeCriticalSection(&g_CritSectPollRelease);
937 | if(SUCCEEDED(LoadConfigurationFile()))
938 | dbgout("[sync] Configuration file loaded\n -> set HOST to %s:%s\n", g_DefaultHost, g_DefaultPort);
939 |
940 | return 0;
941 | };
942 |
943 |
944 | // ODBG2_Pluginreset: called when user opens new or restarts current application.
945 | // Plugin should reset internal variables and data structures to the initial
946 | // state.
947 | extc void __cdecl ODBG2_Pluginreset(void)
948 | {
949 | #if VERBOSE >= 2
950 | dbgout("[*] ODBG2_Pluginclose\n");
951 | #endif
952 |
953 | g_Base = 0;
954 | };
955 |
956 |
957 | // ODBG2_Pluginclose: called when user wants to terminate OllyDbg.
958 | extc int __cdecl ODBG2_Pluginclose(void)
959 | {
960 | #if VERBOSE >= 2
961 | dbgout("[*] ODBG2_Pluginclose\n");
962 | #endif
963 |
964 | return 0;
965 | };
966 |
967 |
968 | // ODBG2_Plugindestroy: called once on exit.
969 | extc void __cdecl ODBG2_Plugindestroy(void)
970 | {
971 | #if VERBOSE >= 2
972 | dbgout("[*] ODBG2_Plugindestroy\n");
973 | #endif
974 |
975 | ReleasePollTimer();
976 | DeleteCriticalSection(&g_CritSectPollRelease);
977 | TunnelClose();
978 |
979 | if(g_ExtConfFile)
980 | {
981 | free(g_DefaultHost);
982 | free(g_DefaultPort);
983 | }
984 | }
985 |
986 |
987 | // ODBG2_Pluginnotify: notifies plugin on relatively infrequent events.
988 | extc void __cdecl ODBG2_Pluginnotify(int code, void *data, ulong param1, ulong param2)
989 | {
990 | UNREFERENCED_PARAMETER(param1);
991 | UNREFERENCED_PARAMETER(param2);
992 | UNREFERENCED_PARAMETER(data);
993 |
994 | #if VERBOSE >= 2
995 | dbgout("[*] ODBG2_Pluginnotify\n");
996 | #endif
997 |
998 | switch (code) {
999 | case PN_STATUS:
1000 | #if VERBOSE >= 2
1001 | dbgout("[*] PN_STATUS, status %x\n", run.status);
1002 | #endif
1003 |
1004 | if (run.status == STAT_PAUSED)
1005 | {
1006 | if (SUCCEEDED(TunnelIsUp()))
1007 | {
1008 | UpdateState();
1009 | CreatePollTimer();
1010 | }
1011 | }
1012 | break;
1013 |
1014 | case PN_RUN:
1015 | #if VERBOSE >= 2
1016 | dbgout("[*] status PN_RUN\n");
1017 | #endif
1018 |
1019 | ReleasePollTimer();
1020 | break;
1021 |
1022 | case PN_NEWPROC:
1023 | // uncomment to sync by default
1024 | //sync(NULL);
1025 | break;
1026 |
1027 | case PN_ENDPROC:
1028 | syncoff();
1029 | break;
1030 |
1031 | default:
1032 | break;
1033 | };
1034 | };
1035 |
1036 |
--------------------------------------------------------------------------------
/ext_olly2/tunnel.c:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2014, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "tunnel.h"
27 |
28 | #define MAX_SEND 8192
29 | #define MAX_OUT 1024
30 |
31 | static CHAR SendBuffer[MAX_SEND];
32 | static CHAR RecvBuffer[MAX_SEND];
33 | BOOL g_Synchronized;
34 | SOCKET g_Sock = INVALID_SOCKET;
35 | WSADATA wsaData;
36 |
37 |
38 | void dbgout(char *fmt, ...)
39 | {
40 | char buffer[MAX_OUT] = {0};
41 |
42 | va_list args;
43 | va_start(args, fmt);
44 | vsprintf_s(buffer, MAX_OUT, fmt, args);
45 | OutputDebugStringA(buffer);
46 | va_end(args);
47 | }
48 |
49 |
50 | void dbgoutW(wchar_t* fmt, ...)
51 | {
52 | wchar_t buffer[MAX_OUT] = {0};
53 |
54 | va_list args;
55 | va_start(args, fmt);
56 | vswprintf_s(buffer, MAX_OUT, fmt, args);
57 | OutputDebugStringW(buffer);
58 | va_end(args);
59 | }
60 |
61 |
62 | #if _NT_TARGET_VERSION_WINXPOR2K3
63 | void
64 | trimcrlf(LPSTR pszSrcString)
65 | {
66 | LPSTR pszDestString = pszSrcString;
67 |
68 | while(*pszSrcString)
69 | {
70 | if (*pszSrcString == 0x0D)
71 | {
72 | pszSrcString++;
73 | pszSrcString++;
74 | }
75 | else
76 | {
77 | *pszDestString=*pszSrcString;
78 | pszDestString++;
79 | pszSrcString++;
80 | }
81 | }
82 |
83 | *pszDestString= *pszSrcString;
84 | }
85 | #endif
86 |
87 |
88 | HRESULT
89 | FromBase64(LPSTR pszString, BYTE **ppbBinary)
90 | {
91 | HRESULT hRes=S_OK;
92 | DWORD cbBinary;
93 |
94 | hRes = CryptStringToBinaryA(pszString, 0, CRYPT_STRING_BASE64, NULL, &cbBinary, NULL, NULL);
95 | if(FAILED(hRes)){
96 | dbgout("[sync] failed at CryptStringToBinaryA: %d\n", GetLastError());
97 | return E_FAIL;
98 | }
99 |
100 | *ppbBinary = (BYTE *) malloc(cbBinary+1);
101 |
102 | if (ppbBinary==NULL){
103 | dbgout("[sync] failed at allocate buffer: %d\n", GetLastError());
104 | return E_FAIL;
105 | }
106 |
107 | hRes = CryptStringToBinaryA(pszString, 0, CRYPT_STRING_BASE64, *ppbBinary, &cbBinary, NULL, NULL);
108 | if(FAILED(hRes)){
109 | dbgout("[sync] send failed at CryptStringToBinaryA: %d\n", GetLastError());
110 | return E_FAIL;
111 | }
112 |
113 | *((char *)((*ppbBinary)+cbBinary)) = 0;
114 |
115 | return hRes;
116 | }
117 |
118 |
119 |
120 | HRESULT
121 | ToBase64(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString)
122 | {
123 | HRESULT hRes=S_OK;
124 | DWORD cchString;
125 |
126 | hRes = CryptBinaryToStringA(pbBinary, cbBinary, CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF, NULL, &cchString);
127 | if(FAILED(hRes)){
128 | dbgout("[sync] send failed at CryptBinaryToString: %d\n", GetLastError());
129 | return E_FAIL;
130 | }
131 |
132 | *pszString = (LPSTR) malloc(cchString);
133 |
134 | if (pszString==NULL){
135 | dbgout("[sync] failed at allocate buffer: %d\n", GetLastError());
136 | return E_FAIL;
137 | }
138 |
139 | hRes = CryptBinaryToStringA(pbBinary, cbBinary, CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF, *pszString, &cchString);
140 | if(FAILED(hRes)){
141 | dbgout("[sync] send failed at CryptBinaryToString: %d\n", GetLastError());
142 | return E_FAIL;
143 | }
144 |
145 | /*
146 | CRYPT_STRING_NOCRLF 0x40000000
147 | Windows Server 2003 and Windows XP: This value is not supported
148 | */
149 |
150 | #if _NT_TARGET_VERSION_WINXPOR2K3
151 | trimcrlf(*pszString);
152 | #endif
153 |
154 | return hRes;
155 | }
156 |
157 |
158 | // return S_OK if socket is created and synchronized
159 | HRESULT TunnelIsUp()
160 | {
161 | HRESULT hRes=S_OK;
162 |
163 | if( (g_Sock==INVALID_SOCKET) | (!g_Synchronized))
164 | hRes = E_FAIL;
165 |
166 | return hRes;
167 | }
168 |
169 |
170 | HRESULT
171 | TunnelCreate(PCSTR Host, PCSTR Port)
172 | {
173 | HRESULT hRes=S_OK;
174 | struct addrinfo *result = NULL, *ptr = NULL, hints;
175 | int iResult;
176 | int bOptLen = sizeof (BOOL);
177 | BOOL bOptVal = FALSE;
178 |
179 | if (FAILED(hRes = WSAStartup(MAKEWORD(2,2), &wsaData))) {
180 | dbgout("[sync] WSAStartup failed with error %d\n", hRes);
181 | goto err_clean;
182 | }
183 |
184 | if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 )
185 | {
186 | dbgout("[sync] WSAStartup failed, Winsock version not supported\n");
187 | hRes = E_FAIL;
188 | goto err_clean;
189 | }
190 |
191 | ZeroMemory( &hints, sizeof(hints) );
192 | hints.ai_family = AF_UNSPEC;
193 | hints.ai_socktype = SOCK_STREAM;
194 | hints.ai_protocol = IPPROTO_TCP;
195 |
196 | // Resolve the server address and port
197 | iResult = getaddrinfo(Host, Port, &hints, &result);
198 | if ( iResult != 0 ) {
199 | dbgout("[sync] getaddrinfo failed with error: %d\n", iResult);
200 | hRes = E_FAIL;
201 | goto err_clean;
202 | }
203 |
204 | #if VERBOSE >= 2
205 | dbgout("[sync] getaddrinfo ok\n");
206 | #endif
207 |
208 | // Attempt to connect to an address until one succeeds
209 | for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
210 |
211 | // Create a SOCKET for connecting to server
212 | g_Sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
213 | if (g_Sock == INVALID_SOCKET) {
214 | dbgout("[sync] socket failed with error: %ld\n", WSAGetLastError());
215 | hRes = E_FAIL;
216 | goto err_clean;
217 | }
218 |
219 | #if VERBOSE >= 2
220 | dbgout("[sync] socket ok\n");
221 | #endif
222 |
223 | bOptVal = TRUE;
224 | iResult = setsockopt(g_Sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &bOptVal, bOptLen);
225 | if (iResult == SOCKET_ERROR)
226 | {
227 | dbgout("[sync] setsockopt for SO_KEEPALIVE failed with error: %u\n", WSAGetLastError());
228 | }
229 |
230 | #if VERBOSE >= 2
231 | dbgout("[sync] Set SO_KEEPALIVE: ON\n");
232 | #endif
233 |
234 | iResult = setsockopt(g_Sock, IPPROTO_TCP, TCP_NODELAY, (char *) &bOptVal, bOptLen);
235 | if (iResult == SOCKET_ERROR)
236 | {
237 | dbgout("[sync] setsockopt for IPPROTO_TCP failed with error: %u\n", WSAGetLastError());
238 | }
239 |
240 | #if VERBOSE >= 2
241 | dbgout("[sync] Set TCP_NODELAY: ON\n");
242 | #endif
243 |
244 | // Connect to server.
245 | iResult = connect(g_Sock, ptr->ai_addr, (int)ptr->ai_addrlen);
246 | if (iResult == SOCKET_ERROR) {
247 | closesocket(g_Sock);
248 | g_Sock = INVALID_SOCKET;
249 | dbgout("[sync] connect failed (check if broker is running)\n");
250 | continue;
251 | }
252 |
253 | dbgout("[sync] sync success, sock 0x%x\n", g_Sock);
254 | break;
255 | }
256 |
257 | if (g_Sock == INVALID_SOCKET){
258 | goto err_clean;
259 | }
260 |
261 | freeaddrinfo(result);
262 | g_Synchronized = TRUE;
263 |
264 | return S_OK;
265 |
266 | err_clean:
267 | WSACleanup();
268 | return hRes;
269 | }
270 |
271 |
272 | HRESULT TunnelClose()
273 | {
274 | HRESULT hRes=S_OK;
275 | int iResult;
276 |
277 | if(SUCCEEDED(TunnelIsUp()))
278 | {
279 | hRes=TunnelSend("[notice]{\"type\":\"dbg_quit\",\"msg\":\"dbg disconnected\"}\n");
280 | if(FAILED(hRes))
281 | return hRes;
282 | }
283 |
284 | if (!(g_Sock == INVALID_SOCKET))
285 | {
286 | iResult = closesocket(g_Sock);
287 | g_Sock = INVALID_SOCKET;
288 |
289 | if (iResult == SOCKET_ERROR){
290 | dbgout("[sync] closesocket failed with error %d\n", WSAGetLastError());
291 | }
292 | }
293 |
294 | dbgout("[sync] sync is off\n");
295 | g_Synchronized = FALSE;
296 | WSACleanup();
297 | return hRes;
298 | }
299 |
300 |
301 | HRESULT TunnelPoll(int *lpNbBytesRecvd, LPSTR *lpBuffer)
302 | {
303 | HRESULT hRes=S_OK;
304 | int iResult;
305 | u_long iMode = 1;
306 |
307 | iResult = ioctlsocket(g_Sock, FIONBIO, &iMode);
308 | if (iResult != NO_ERROR)
309 | {
310 | printf("[sync] TunnelPoll ioctlsocket failed with error: %ld\n", iResult);
311 | return E_FAIL;
312 | }
313 |
314 | hRes = TunnelReceive(lpNbBytesRecvd, lpBuffer);
315 | if (FAILED(hRes)){
316 | return hRes;
317 | }
318 |
319 | iMode = 0;
320 | iResult = ioctlsocket(g_Sock, FIONBIO, &iMode);
321 | if (iResult != NO_ERROR)
322 | {
323 | printf("[sync] TunnelPoll ioctlsocket failed with error: %ld\n", iResult);
324 | return E_FAIL;
325 | }
326 |
327 | return hRes;
328 | }
329 |
330 | HRESULT TunnelReceive(int *lpNbBytesRecvd, LPSTR *lpBuffer)
331 | {
332 | HRESULT hRes=S_OK;
333 | int iResult;
334 | errno_t err;
335 | *lpNbBytesRecvd = 0;
336 |
337 | if(FAILED(hRes=TunnelIsUp()))
338 | {
339 | dbgout("[sync] TunnelReceive: tunnel is not available\n");
340 | return hRes;
341 | }
342 |
343 | iResult = recv(g_Sock, RecvBuffer, MAX_SEND, 0);
344 | if ( iResult == SOCKET_ERROR )
345 | {
346 | iResult = WSAGetLastError();
347 | if (iResult == WSAEWOULDBLOCK)
348 | {
349 | return hRes;
350 | }
351 | else
352 | {
353 | dbgout("[sync] recv failed with error: %d, 0x%x\n", iResult, g_Sock);
354 | WsaErrMsg(iResult);
355 | goto error_close;
356 | }
357 | }
358 | else if ( iResult == 0 ) {
359 | dbgout("[sync] recv: connection closed\n");
360 | goto error_close;
361 | }
362 |
363 | *lpBuffer = (LPSTR) calloc(iResult+1, sizeof(CHAR));
364 | if (lpBuffer == NULL) {
365 | dbgout("[sync] failed at allocate buffer: %d\n", GetLastError());
366 | return E_FAIL;
367 | }
368 |
369 | err = memcpy_s(*lpBuffer, iResult+1, RecvBuffer, iResult);
370 | if (err) {
371 | dbgout("[sync] memcpy_s failed to copy received buffer\n");
372 | free(*lpBuffer);
373 | *lpBuffer = NULL;
374 | hRes = E_FAIL;
375 | } else {
376 | *lpNbBytesRecvd = iResult;
377 | }
378 |
379 | return hRes;
380 |
381 | error_close:
382 | g_Synchronized = FALSE;
383 | TunnelClose();
384 | return E_FAIL;
385 | }
386 |
387 |
388 | HRESULT TunnelSend(PCSTR Format, ...)
389 | {
390 | HRESULT hRes=S_OK;
391 | va_list Args;
392 | int iResult;
393 | size_t cbRemaining;
394 |
395 | if(FAILED(hRes=TunnelIsUp()))
396 | {
397 | dbgout("[sync] TunnelSend: tunnel is unavailable\n");
398 | return hRes;
399 | }
400 |
401 | va_start(Args, Format);
402 | hRes = StringCbVPrintfExA(SendBuffer, MAX_SEND, NULL, &cbRemaining, STRSAFE_NULL_ON_FAILURE, Format, Args);
403 | va_end(Args);
404 |
405 | if (FAILED(hRes))
406 | return hRes;
407 |
408 | #if VERBOSE >= 2
409 | dbgout("[sync] send 0x%x bytes, %s\n", MAX_SEND-cbRemaining, SendBuffer);
410 | #endif
411 |
412 | iResult = send(g_Sock, (const char *)SendBuffer, MAX_SEND-((unsigned int)cbRemaining), 0);
413 | if(iResult == SOCKET_ERROR)
414 | {
415 | iResult = WSAGetLastError();
416 | dbgout("[sync] send failed with error %d, 0x%x\n", iResult, g_Sock);
417 | WsaErrMsg(iResult);
418 | g_Synchronized = FALSE;
419 | TunnelClose();
420 | hRes=E_FAIL;
421 | }
422 |
423 | return hRes;
424 | }
425 |
426 | HRESULT WsaErrMsg(int LastError)
427 | {
428 | HRESULT hRes=S_OK;
429 |
430 | switch(LastError){
431 | case WSAECONNRESET:
432 | dbgout(" -> Connection reset by peer\n");
433 | break;
434 | case WSAENOTCONN:
435 | dbgout(" -> Socket is not connected\n");
436 | break;
437 | case WSAECONNABORTED:
438 | dbgout(" -> Software caused connection abort\n");
439 | break;
440 | default:
441 | break;
442 | }
443 |
444 | return hRes;
445 | }
446 |
--------------------------------------------------------------------------------
/ext_olly2/tunnel.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2014, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | extern BOOL g_Synchronized;
21 |
22 | extern void dbgout(char *fmt, ...);
23 |
24 | extern void dbgoutW(wchar_t* fmt, ...);
25 |
26 | HRESULT TunnelIsUp();
27 |
28 | HRESULT TunnelCreate(PCSTR Host, PCSTR Port);
29 |
30 | HRESULT TunnelClose();
31 |
32 | HRESULT TunnelPoll(int *lpNbBytesRecvd, LPSTR *lpBuffer);
33 |
34 | HRESULT TunnelReceive(int *lpNbBytesRecvd, LPSTR *lpBuffer);
35 |
36 | HRESULT TunnelSend(PCSTR Format, ...);
37 |
38 | HRESULT ToBase64(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString);
39 |
40 | HRESULT FromBase64(LPSTR pszString, BYTE **ppbBinary);
41 |
42 | HRESULT WsaErrMsg(int LastError);
43 |
44 | HRESULT convert_tow(const char * mbstr, PTCH *wcstr);
--------------------------------------------------------------------------------
/ext_windbg/sync/make.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2012-2014, Quarkslab.
3 | #
4 | # This file is part of qb-sync.
5 | #
6 | # qb-sync is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # This program is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with this program. If not, see .
18 | #
19 |
20 | #!/usr/bin/ruby
21 | # encoding: ASCII-8BIT
22 |
23 | TMP = 'tmp.bat'
24 | BUILD = 'fre'
25 | DDKPATH = 'C:\WinDDK\7600.16385.1'
26 | ARCHS = ['x86', 'x64']
27 | TARGETS = ['WXP', 'WIN7']
28 |
29 | # grab your panties
30 | abort("\n\nShit out of luck") unless File.exists? DDKPATH
31 |
32 | # disable oacr
33 | File.open("#{DDKPATH}\\bin\\setenv.bat",'r'){|ifd|
34 | File.open('setenv.bat','w'){|ofd| ofd << ifd.read().sub!(/_RunOacr=TRUE/, '_RunOacr=FALSE')}
35 | } unless File.exists? 'setenv.bat'
36 |
37 | # build all
38 | ARCHS.product(TARGETS).each{|arch, target|
39 | puts "\n\n[+] building #{BUILD} #{arch} #{target}"
40 | File.open(TMP, 'w'){|fd|
41 | fd << <.
18 | */
19 |
20 | /*
21 | Based on out.cpp from WinDDK's dumpstk sample
22 | */
23 |
24 | #include
25 | #include "outputcallbacks.h"
26 | #include "sync.h"
27 | #include "tunnel.h"
28 |
29 | StdioOutputCallbacks g_OutputCb;
30 | bool g_OutputCbLocal = false;
31 |
32 |
33 | STDMETHODIMP
34 | StdioOutputCallbacks::QueryInterface(
35 | THIS_
36 | IN REFIID InterfaceId,
37 | OUT PVOID* Interface
38 | )
39 | {
40 | *Interface = NULL;
41 |
42 | if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
43 | IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
44 | {
45 | *Interface = (IDebugOutputCallbacks *)this;
46 | AddRef();
47 | return S_OK;
48 | }
49 | else
50 | {
51 | return E_NOINTERFACE;
52 | }
53 | }
54 |
55 |
56 | STDMETHODIMP_(ULONG)
57 | StdioOutputCallbacks::AddRef(THIS)
58 | {
59 | return 1;
60 | }
61 |
62 |
63 | STDMETHODIMP_(ULONG)
64 | StdioOutputCallbacks::Release(THIS)
65 | {
66 | return 0;
67 | }
68 |
69 |
70 | STDMETHODIMP
71 | StdioOutputCallbacks::Output(
72 | THIS_
73 | IN ULONG Mask,
74 | IN PCSTR Text
75 | )
76 | {
77 | UNREFERENCED_PARAMETER(Mask);
78 | HRESULT hRes;
79 | errno_t err;
80 | size_t cbBinary;
81 | LPTSTR pszString;
82 |
83 | cbBinary = strlen(Text);
84 |
85 | if (g_OutputCbLocal)
86 | {
87 |
88 | if ((g_CmdBuffer.len + cbBinary) < (MAX_CMD-2))
89 | {
90 | err = strcpy_s(g_CmdBuffer.buffer+g_CmdBuffer.len, MAX_CMD-g_CmdBuffer.len, Text);
91 | if (err)
92 | {
93 | g_CmdBuffer.hRes = E_FAIL;
94 | g_CmdBuffer.len = 0;
95 | }
96 | else
97 | {
98 | g_CmdBuffer.hRes = S_OK;
99 | g_CmdBuffer.len += cbBinary;
100 | }
101 | }
102 | }
103 | else
104 | {
105 | hRes = ToBase64((const byte *)Text, (unsigned int)cbBinary, &pszString);
106 | if (SUCCEEDED(hRes))
107 | {
108 | TunnelSend("[sync] {\"type\":\"cmd\",\"msg\":\"%s\", \"base\":%llu,\"offset\":%llu}\n", pszString, g_Base, g_Offset);
109 | free(pszString);
110 | }
111 | }
112 |
113 | return S_OK;
114 | }
115 |
--------------------------------------------------------------------------------
/ext_windbg/sync/outputcallbacks.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | /*
21 | Based on out.cpp from WinDDK's dumpstk sample
22 | */
23 |
24 | #define MAX_CMD 8192
25 | #define CB_OUTPUTCTRL DEBUG_OUTCTL_THIS_CLIENT
26 | #define CB_FLAGS DEBUG_EXECUTE_ECHO | DEBUG_EXECUTE_NO_REPEAT
27 |
28 | class StdioOutputCallbacks : public IDebugOutputCallbacks
29 | {
30 | public:
31 | // IUnknown.
32 | STDMETHOD(QueryInterface)(
33 | THIS_
34 | IN REFIID InterfaceId,
35 | OUT PVOID* Interface
36 | );
37 | STDMETHOD_(ULONG, AddRef)(
38 | THIS
39 | );
40 | STDMETHOD_(ULONG, Release)(
41 | THIS
42 | );
43 |
44 | // IDebugOutputCallbacks.
45 | STDMETHOD(Output)(
46 | THIS_
47 | IN ULONG Mask,
48 | IN PCSTR Text
49 | );
50 | };
51 |
52 |
53 | typedef struct _CMD_BUFFER
54 | {
55 | HRESULT hRes;
56 | size_t len;
57 | CHAR buffer[MAX_CMD];
58 | } CMD_BUFFER, *PCMD_BUFFER;
59 |
60 | extern StdioOutputCallbacks g_OutputCb;
61 | extern bool g_OutputCbLocal;
62 | extern CMD_BUFFER g_CmdBuffer;
63 |
--------------------------------------------------------------------------------
/ext_windbg/sync/sources:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2012-2015, Quarkslab.
3 | #
4 | # This file is part of qb-sync.
5 | #
6 | # qb-sync is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # This program is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with this program. If not, see .
18 | #
19 |
20 | TARGETNAME=sync
21 | TARGETTYPE=DYNLINK
22 |
23 | DLLENTRY=_DllMainCRTStartup
24 |
25 | !IF "$(DBGSDK_INC_PATH)" != ""
26 | INCLUDES = $(DBGSDK_INC_PATH);$(INCLUDES)
27 | !endif
28 |
29 | !IF "$(DBGSDK_LIB_PATH)" == ""
30 | DBGSDK_LIB_PATH = $(SDK_LIB_PATH)
31 | !ELSE
32 | DBGSDK_LIB_PATH = $(DBGSDK_LIB_PATH)\$(TARGET_DIRECTORY)
33 | !endif
34 |
35 | !IF ($(_NT_TARGET_VERSION) == $(_NT_TARGET_VERSION_WINXP))
36 | C_DEFINES=$(C_DEFINES) -D_NT_TARGET_VERSION_WINXPOR2K3=1
37 | !ELSE IF ($(_NT_TARGET_VERSION) == $(_NT_TARGET_VERSION_WS03))
38 | C_DEFINES=$(C_DEFINES) -D_NT_TARGET_VERSION_WINXPOR2K3=1
39 | !endif
40 |
41 | USE_LIBCMT=1
42 |
43 | TARGETLIBS= \
44 | $(SDK_LIB_PATH)\kernel32.lib \
45 | $(SDK_LIB_PATH)\shlwapi.lib \
46 | $(SDK_LIB_PATH)\ws2_32.lib \
47 | $(SDK_LIB_PATH)\advapi32.lib \
48 | $(SDK_LIB_PATH)\crypt32.lib \
49 | $(DBGSDK_LIB_PATH)\dbgeng.lib
50 |
51 | UMTYPE=windows
52 | MSC_OPTIMIZATION=/Ox
53 | LINKER_FLAGS=$(LINKER_FLAGS) /OPT:REF
54 |
55 | SOURCES= sync.cpp \
56 | tunnel.cpp \
57 | outputcallback.cpp
58 |
--------------------------------------------------------------------------------
/ext_windbg/sync/sources.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | sync
5 | DYNLINK
6 | _DllMainCRTStartup
7 |
8 |
9 |
10 |
11 | $(DBGSDK_INC_PATH);$(INCLUDES)
12 |
13 |
14 |
15 |
16 |
17 |
18 | $(SDK_LIB_PATH)
19 |
20 |
21 |
22 |
23 | $(DBGSDK_LIB_PATH)\$(TARGET_DIRECTORY)
24 |
25 |
26 |
27 |
28 |
29 |
30 | $(C_DEFINES) -D_NT_TARGET_VERSION_WINXPOR2K3=1
31 |
32 |
33 |
34 |
35 | $(C_DEFINES) -D_NT_TARGET_VERSION_WINXPOR2K3=1
36 |
37 |
38 |
39 |
40 | 1
41 | $(SDK_LIB_PATH)\kernel32.lib $(SDK_LIB_PATH)\shlwapi.lib $(SDK_LIB_PATH)\ws2_32.lib $(SDK_LIB_PATH)\advapi32.lib $(SDK_LIB_PATH)\crypt32.lib $(DBGSDK_LIB_PATH)\dbgeng.lib
42 | windows
43 | /Ox
44 | $(LINKER_FLAGS) /OPT:REF
45 | sync.cpp tunnel.cpp outputcallback.cpp
46 |
47 |
--------------------------------------------------------------------------------
/ext_windbg/sync/sync.def:
--------------------------------------------------------------------------------
1 | ;
2 | ; Copyright (C) 2012-2015, Quarkslab.
3 | ;
4 | ; This file is part of qb-sync.
5 | ;
6 | ; qb-sync is free software: you can redistribute it and/or modify
7 | ; it under the terms of the GNU General Public License as published by
8 | ; the Free Software Foundation, either version 3 of the License, or
9 | ; (at your option) any later version.
10 | ;
11 | ; This program is distributed in the hope that it will be useful,
12 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | ; GNU General Public License for more details.
15 | ;
16 | ; You should have received a copy of the GNU General Public License
17 | ; along with this program. If not, see .
18 | ;
19 |
20 | EXPORTS
21 |
22 | DebugExtensionNotify
23 | DebugExtensionInitialize
24 | DebugExtensionUninitialize
25 |
26 | sync
27 | syncoff
28 | synchelp
29 | syncmodauto
30 | cmd
31 | cmt
32 | rcmt
33 | rln
34 | fcmt
35 | raddr
36 | lbl
37 | bc
38 | idblist
39 | idbn
40 | jmpto
41 | modmap
42 | modunmap
43 | modcheck
44 | bpcmds
45 | ks
46 | translate
47 |
--------------------------------------------------------------------------------
/ext_windbg/sync/sync.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #define _WINSOCKAPI_
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #define KDEXT_64BIT
28 | #include
29 | #include
30 |
31 | #ifdef __cplusplus
32 | extern "C" {
33 | #endif
34 |
35 |
36 | #define INIT_API() \
37 | HRESULT Status; \
38 | if ((Status = ExtQuery(Client)) != S_OK) return Status;
39 |
40 | #define EXT_RELEASE(Unk) \
41 | ((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL)
42 |
43 | #define EXIT_API ExtRelease
44 |
45 | // Extension information
46 | #define EXT_MAJOR_VER 1
47 | #define EXT_MINOR_VER 0
48 |
49 | // Global variables initialized by query
50 | extern PDEBUG_CLIENT4 g_ExtClient;
51 | extern PDEBUG_CONTROL g_ExtControl;
52 | extern PDEBUG_SYMBOLS3 g_ExtSymbols;
53 | extern PDEBUG_REGISTERS g_ExtRegisters;
54 |
55 | extern ULONG64 g_Offset, g_Base;
56 |
57 | HRESULT
58 | ExtQuery(PDEBUG_CLIENT4 Client);
59 |
60 | void
61 | ExtRelease(void);
62 |
63 | HRESULT
64 | NotifyOnTargetAccessible(PDEBUG_CONTROL Control);
65 |
66 | #ifdef __cplusplus
67 | }
68 | #endif
69 |
--------------------------------------------------------------------------------
/ext_windbg/sync/sync.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 12.0
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sync", "sync.vcxproj", "{F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Win7 Debug|Win32 = Win7 Debug|Win32
11 | Win7 Debug|x64 = Win7 Debug|x64
12 | Win7 Release|Win32 = Win7 Release|Win32
13 | Win7 Release|x64 = Win7 Release|x64
14 | Win8 Debug|Win32 = Win8 Debug|Win32
15 | Win8 Debug|x64 = Win8 Debug|x64
16 | Win8 Release|Win32 = Win8 Release|Win32
17 | Win8 Release|x64 = Win8 Release|x64
18 | Win8.1 Debug|Win32 = Win8.1 Debug|Win32
19 | Win8.1 Debug|x64 = Win8.1 Debug|x64
20 | Win8.1 Release|Win32 = Win8.1 Release|Win32
21 | Win8.1 Release|x64 = Win8.1 Release|x64
22 | WinXP Debug|Win32 = WinXP Debug|Win32
23 | WinXP Debug|x64 = WinXP Debug|x64
24 | WinXP Release|Win32 = WinXP Release|Win32
25 | WinXP Release|x64 = WinXP Release|x64
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
29 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
30 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64
31 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Debug|x64.Build.0 = Win7 Debug|x64
32 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Release|Win32.ActiveCfg = Win7 Release|Win32
33 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Release|Win32.Build.0 = Win7 Release|Win32
34 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Release|x64.ActiveCfg = Win7 Release|x64
35 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win7 Release|x64.Build.0 = Win7 Release|x64
36 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Debug|Win32.ActiveCfg = Win8 Debug|Win32
37 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Debug|Win32.Build.0 = Win8 Debug|Win32
38 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Debug|x64.ActiveCfg = Win8 Debug|x64
39 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Debug|x64.Build.0 = Win8 Debug|x64
40 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Release|Win32.ActiveCfg = Win8 Release|Win32
41 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Release|Win32.Build.0 = Win8 Release|Win32
42 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Release|x64.ActiveCfg = Win8 Release|x64
43 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8 Release|x64.Build.0 = Win8 Release|x64
44 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Debug|Win32.ActiveCfg = Win8.1 Debug|Win32
45 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Debug|Win32.Build.0 = Win8.1 Debug|Win32
46 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Debug|x64.ActiveCfg = Win8.1 Debug|x64
47 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Debug|x64.Build.0 = Win8.1 Debug|x64
48 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Release|Win32.ActiveCfg = Win8.1 Release|Win32
49 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Release|Win32.Build.0 = Win8.1 Release|Win32
50 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Release|x64.ActiveCfg = Win8.1 Release|x64
51 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.Win8.1 Release|x64.Build.0 = Win8.1 Release|x64
52 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Debug|Win32.ActiveCfg = WinXP Debug|Win32
53 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Debug|Win32.Build.0 = WinXP Debug|Win32
54 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Debug|Win32.Deploy.0 = WinXP Debug|Win32
55 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Debug|x64.ActiveCfg = WinXP Debug|x64
56 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Debug|x64.Build.0 = WinXP Debug|x64
57 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Release|Win32.ActiveCfg = WinXP Release|Win32
58 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Release|Win32.Build.0 = WinXP Release|Win32
59 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Release|Win32.Deploy.0 = WinXP Release|Win32
60 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Release|x64.ActiveCfg = WinXP Release|x64
61 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}.WinXP Release|x64.Build.0 = WinXP Release|x64
62 | EndGlobalSection
63 | GlobalSection(SolutionProperties) = preSolution
64 | HideSolutionNode = FALSE
65 | EndGlobalSection
66 | EndGlobal
67 |
--------------------------------------------------------------------------------
/ext_windbg/sync/sync.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Win8.1 Debug
6 | Win32
7 |
8 |
9 | Win8 Debug
10 | Win32
11 |
12 |
13 | Win7 Debug
14 | Win32
15 |
16 |
17 | Win8.1 Release
18 | Win32
19 |
20 |
21 | Win8 Release
22 | Win32
23 |
24 |
25 | Win7 Release
26 | Win32
27 |
28 |
29 | Win8.1 Debug
30 | x64
31 |
32 |
33 | Win8 Debug
34 | x64
35 |
36 |
37 | Win7 Debug
38 | x64
39 |
40 |
41 | Win8.1 Release
42 | x64
43 |
44 |
45 | Win8 Release
46 | x64
47 |
48 |
49 | Win7 Release
50 | x64
51 |
52 |
53 | WinXP Debug
54 | Win32
55 |
56 |
57 | WinXP Debug
58 | x64
59 |
60 |
61 | WinXP Release
62 | Win32
63 |
64 |
65 | WinXP Release
66 | x64
67 |
68 |
69 |
70 | WindowsApplicationForDrivers8.1
71 | DynamicLibrary
72 |
73 | sync
74 | Win8.1 Debug
75 | Win32
76 |
77 |
78 |
79 | 1.0
80 | $(Configuration.Replace(' ',''))
81 | $(BUILD_ALT_DIR)\$(Platform)\
82 | $(BUILD_ALT_DIR)\x86\
83 | $(IntDir)
84 |
85 |
86 |
87 |
88 |
89 | {F50A0131-A5E6-4DB8-8627-A355CCC3E6AC}
90 | $(MSBuildProjectName)
91 |
92 |
93 | WindowsV6.3
94 | True
95 |
96 |
97 | Win8
98 | True
99 |
100 |
101 | Win7
102 | True
103 |
104 |
105 | Win7
106 | True
107 |
108 |
109 | WindowsV6.3
110 | False
111 |
112 |
113 | Win8
114 | False
115 |
116 |
117 | Win7
118 | False
119 |
120 |
121 | Win7
122 | False
123 |
124 |
125 | WindowsV6.3
126 | True
127 |
128 |
129 | Win8
130 | True
131 |
132 |
133 | Win7
134 | True
135 |
136 |
137 | Win7
138 | True
139 |
140 |
141 | WindowsV6.3
142 | False
143 |
144 |
145 | Win8
146 | False
147 |
148 |
149 | Win7
150 | False
151 |
152 |
153 | Win7
154 | False
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
211 |
212 |
213 |
214 | _NT_TARGET_VERSION_WINXPOR2K3=1;%(PreprocessorDefinitions)
215 |
216 |
217 |
218 |
219 | _NT_TARGET_VERSION_WINXPOR2K3=1;%(PreprocessorDefinitions)
220 | Disabled
221 |
222 |
223 |
224 |
225 | Disabled
226 |
227 |
228 |
229 |
230 | Disabled
231 |
232 |
233 |
234 |
235 | Disabled
236 |
237 |
238 |
239 |
240 | Disabled
241 |
242 |
243 |
244 |
245 | Disabled
246 |
247 |
248 |
249 |
250 | Disabled
251 |
252 |
253 |
254 |
255 | Disabled
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 | $(KIT_SHARED_INC_PATH)
271 | true
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
--------------------------------------------------------------------------------
/ext_windbg/sync/tunnel.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "sync.h"
27 | #include "tunnel.h"
28 |
29 | #define MAX_SEND 8192
30 |
31 | static CHAR SendBuffer[MAX_SEND];
32 | static CHAR RecvBuffer[MAX_SEND];
33 | BOOL g_Synchronized;
34 | SOCKET g_Sock = INVALID_SOCKET;
35 | WSADATA wsaData;
36 |
37 | #if _NT_TARGET_VERSION_WINXPOR2K3
38 | void
39 | trimcrlf(LPSTR pszSrcString)
40 | {
41 | LPSTR pszDestString = pszSrcString;
42 |
43 | while(*pszSrcString)
44 | {
45 | if (*pszSrcString == 0x0D)
46 | {
47 | pszSrcString++;
48 | pszSrcString++;
49 | }
50 | else
51 | {
52 | *pszDestString=*pszSrcString;
53 | pszDestString++;
54 | pszSrcString++;
55 | }
56 | }
57 |
58 | *pszDestString= *pszSrcString;
59 | }
60 | #endif
61 |
62 |
63 | HRESULT
64 | FromBase64(LPCSTR pszString, BYTE **ppbBinary)
65 | {
66 | BOOL bRes = FALSE;
67 | HRESULT hRes = S_OK;
68 | DWORD cbBinary = 0;
69 |
70 | bRes = CryptStringToBinary(pszString, 0, CRYPT_STRING_BASE64, NULL, &cbBinary, NULL, NULL);
71 | if (!bRes){
72 | dprintf("[sync] failed at CryptStringToBinaryA: %d\n", GetLastError());
73 | return E_FAIL;
74 | }
75 |
76 | *ppbBinary = (BYTE *) malloc(cbBinary+1);
77 |
78 | if (ppbBinary==NULL){
79 | dprintf("[sync] failed at allocate buffer: %d\n", GetLastError());
80 | return E_FAIL;
81 | }
82 |
83 | bRes = CryptStringToBinaryA(pszString, 0, CRYPT_STRING_BASE64, *ppbBinary, &cbBinary, NULL, NULL);
84 | if (!bRes){
85 | dprintf("[sync] failed at CryptStringToBinaryA: %d\n", GetLastError());
86 | return E_FAIL;
87 | }
88 |
89 | *((char *)((*ppbBinary)+cbBinary)) = 0;
90 |
91 | return hRes;
92 | }
93 |
94 |
95 | HRESULT
96 | ToStringEnc(DWORD dwFlags, const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString)
97 | {
98 | BOOL bRes = FALSE;
99 | HRESULT hRes=S_OK;
100 | DWORD cchString = 0;
101 |
102 | bRes = CryptBinaryToStringA(pbBinary, cbBinary, dwFlags, NULL, &cchString);
103 | if (!bRes){
104 | dprintf("[sync] send failed at CryptBinaryToString: %d\n", GetLastError());
105 | return E_FAIL;
106 | }
107 |
108 | *pszString = (LPSTR) malloc(cchString);
109 |
110 | if (*pszString==NULL){
111 | dprintf("[sync] failed at allocate buffer: %d\n", GetLastError());
112 | return E_FAIL;
113 | }
114 |
115 | bRes = CryptBinaryToStringA(pbBinary, cbBinary, dwFlags, *pszString, &cchString);
116 | if (!bRes){
117 | dprintf("[sync] failed at CryptBinaryToString: %d\n", GetLastError());
118 | if (*pszString)
119 | {
120 | free(*pszString);
121 | *pszString = NULL;
122 | }
123 | return E_FAIL;
124 | }
125 |
126 | /*
127 | CRYPT_STRING_NOCRLF 0x40000000
128 | Windows Server 2003 and Windows XP: This value is not supported
129 | */
130 |
131 | #if _NT_TARGET_VERSION_WINXPOR2K3
132 | trimcrlf(*pszString);
133 | #endif
134 |
135 | return hRes;
136 | }
137 |
138 |
139 | HRESULT
140 | ToBase64(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString)
141 | {
142 | HRESULT hRes;
143 |
144 | hRes = ToStringEnc(CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF, pbBinary, cbBinary, pszString);
145 | return hRes;
146 | }
147 |
148 |
149 | HRESULT
150 | ToHexString(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString)
151 | {
152 | HRESULT hRes;
153 |
154 | hRes = ToStringEnc(CRYPT_STRING_HEX|CRYPT_STRING_NOCRLF, pbBinary, cbBinary, pszString);
155 | return hRes;
156 | }
157 |
158 |
159 | HRESULT
160 | NextChunk(char *cmd, char **nextc)
161 | {
162 | char *tmp;
163 |
164 | tmp = strchr(cmd, 0x20);
165 | if (tmp == NULL)
166 | return E_FAIL;
167 |
168 | *tmp = 0;
169 | *nextc = tmp+1;
170 |
171 | if (**nextc == 0x3a){
172 | NextChunk(*nextc, nextc);
173 | }
174 |
175 | return S_OK;
176 | }
177 |
178 |
179 | // return S_OK if socket is created and synchronized
180 | HRESULT TunnelIsUp()
181 | {
182 | HRESULT hRes=S_OK;
183 |
184 | if ((g_Sock==INVALID_SOCKET) | (!g_Synchronized)){
185 | hRes = E_FAIL;
186 | }
187 |
188 | return hRes;
189 | }
190 |
191 |
192 | HRESULT
193 | TunnelCreate(PCSTR Host, PCSTR Port)
194 | {
195 | HRESULT hRes=S_OK;
196 | struct addrinfo *result = NULL, *ptr = NULL, hints;
197 | int iResult;
198 | int bOptLen = sizeof (BOOL);
199 | BOOL bOptVal = FALSE;
200 |
201 | if (FAILED(hRes = WSAStartup(MAKEWORD(2,2), &wsaData))) {
202 | dprintf("[sync] WSAStartup failed with error %d\n", hRes);
203 | goto err_clean;
204 | }
205 |
206 | if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 )
207 | {
208 | dprintf("[sync] WSAStartup failed, Winsock version not supported\n");
209 | hRes = E_FAIL;
210 | goto err_clean;
211 | }
212 |
213 | ZeroMemory( &hints, sizeof(hints) );
214 | hints.ai_family = AF_UNSPEC;
215 | hints.ai_socktype = SOCK_STREAM;
216 | hints.ai_protocol = IPPROTO_TCP;
217 |
218 | // Resolve the server address and port
219 | iResult = getaddrinfo(Host, Port, &hints, &result);
220 | if ( iResult != 0 ) {
221 | dprintf("[sync] getaddrinfo failed with error: %d\n", iResult);
222 | hRes = E_FAIL;
223 | goto err_clean;
224 | }
225 |
226 | #if VERBOSE >= 2
227 | dprintf("[sync] getaddrinfo ok\n");
228 | #endif
229 |
230 | // Attempt to connect to an address until one succeeds
231 | for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
232 |
233 | // Create a SOCKET for connecting to server
234 | g_Sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
235 | if (g_Sock == INVALID_SOCKET) {
236 | dprintf("[sync] socket failed with error: %ld\n", WSAGetLastError());
237 | hRes = E_FAIL;
238 | goto err_clean;
239 | }
240 |
241 | #if VERBOSE >= 2
242 | dprintf("[sync] socket ok\n");
243 | #endif
244 |
245 | bOptVal = TRUE;
246 | iResult = setsockopt(g_Sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &bOptVal, bOptLen);
247 | if (iResult == SOCKET_ERROR)
248 | {
249 | dprintf("[sync] setsockopt for SO_KEEPALIVE failed with error: %u\n", WSAGetLastError());
250 | }
251 |
252 | #if VERBOSE >= 2
253 | dprintf("[sync] Set SO_KEEPALIVE: ON\n");
254 | #endif
255 |
256 | iResult = setsockopt(g_Sock, IPPROTO_TCP, TCP_NODELAY, (char *) &bOptVal, bOptLen);
257 | if (iResult == SOCKET_ERROR)
258 | {
259 | dprintf("[sync] setsockopt for IPPROTO_TCP failed with error: %u\n", WSAGetLastError());
260 | }
261 |
262 | #if VERBOSE >= 2
263 | dprintf("[sync] Set TCP_NODELAY: ON\n");
264 | #endif
265 |
266 | // Connect to server.
267 | iResult = connect(g_Sock, ptr->ai_addr, (int)ptr->ai_addrlen);
268 | if (iResult == SOCKET_ERROR)
269 | {
270 | closesocket(g_Sock);
271 | g_Sock = INVALID_SOCKET;
272 | dprintf("[sync] connect failed (check if broker is running)\n");
273 | continue;
274 | }
275 |
276 | dprintf("[sync] sync success, sock 0x%x\n", g_Sock);
277 | break;
278 | }
279 |
280 | if (g_Sock == INVALID_SOCKET){
281 | goto err_clean;
282 | }
283 |
284 | freeaddrinfo(result);
285 | g_Synchronized = TRUE;
286 |
287 | return S_OK;
288 |
289 | err_clean:
290 | WSACleanup();
291 | return hRes;
292 | }
293 |
294 |
295 | HRESULT TunnelClose()
296 | {
297 | HRESULT hRes=S_OK;
298 | int iResult;
299 |
300 | if (SUCCEEDED(TunnelIsUp()))
301 | {
302 | hRes=TunnelSend("[notice]{\"type\":\"dbg_quit\",\"msg\":\"dbg disconnected\"}\n");
303 | if (FAILED(hRes)){
304 | return hRes;
305 | }
306 | }
307 |
308 | if (!(g_Sock == INVALID_SOCKET))
309 | {
310 | iResult = closesocket(g_Sock);
311 | g_Sock = INVALID_SOCKET;
312 |
313 | if (iResult == SOCKET_ERROR){
314 | dprintf("[sync] closesocket failed with error %d\n", WSAGetLastError());
315 | }
316 | }
317 |
318 | dprintf("[sync] sync is off\n");
319 | g_Synchronized = FALSE;
320 | WSACleanup();
321 | return hRes;
322 | }
323 |
324 |
325 | HRESULT TunnelPoll(int *lpNbBytesRecvd, LPSTR *lpBuffer)
326 | {
327 | HRESULT hRes=S_OK;
328 | int iResult;
329 | u_long iMode = 1;
330 |
331 | iResult = ioctlsocket(g_Sock, FIONBIO, &iMode);
332 | if (iResult != NO_ERROR)
333 | {
334 | printf("[sync] TunnelPoll ioctlsocket failed with error: %ld\n", iResult);
335 | return E_FAIL;
336 | }
337 |
338 | hRes = TunnelReceive(lpNbBytesRecvd, lpBuffer);
339 | if (FAILED(hRes)){
340 | return hRes;
341 | }
342 |
343 | iMode = 0;
344 | iResult = ioctlsocket(g_Sock, FIONBIO, &iMode);
345 |
346 | if (iResult != NO_ERROR)
347 | {
348 | printf("[sync] TunnelPoll ioctlsocket failed with error: %ld\n", iResult);
349 | return E_FAIL;
350 | }
351 | return hRes;
352 | }
353 |
354 |
355 | HRESULT TunnelReceive(int *lpNbBytesRecvd, LPSTR *lpBuffer)
356 | {
357 | HRESULT hRes=S_OK;
358 | int iResult;
359 | errno_t err;
360 | *lpNbBytesRecvd = 0;
361 |
362 | if (FAILED(hRes=TunnelIsUp()))
363 | {
364 | dprintf("[sync] TunnelReceive: tunnel is not available\n");
365 | return hRes;
366 | }
367 |
368 | iResult = recv(g_Sock, RecvBuffer, MAX_SEND, 0);
369 | if ( iResult == SOCKET_ERROR )
370 | {
371 | iResult = WSAGetLastError();
372 | if (iResult == WSAEWOULDBLOCK)
373 | {
374 | return hRes;
375 | }
376 | else
377 | {
378 | dprintf("[sync] recv failed with error: %d, 0x%x\n", iResult, g_Sock);
379 | WsaErrMsg(iResult);
380 | goto error_close;
381 | }
382 | }
383 | else if ( iResult == 0 ) {
384 | dprintf("[sync] recv: connection closed\n");
385 | goto error_close;
386 | }
387 |
388 | *lpBuffer = (LPSTR) calloc(iResult+1, sizeof(CHAR));
389 | if (lpBuffer == NULL) {
390 | dprintf("[sync] failed at allocate buffer: %d\n", GetLastError());
391 | return E_FAIL;
392 | }
393 |
394 | err = memcpy_s(*lpBuffer, iResult+1, RecvBuffer, iResult);
395 | if (err) {
396 | dprintf("[sync] memcpy_s failed to copy received buffer\n");
397 | free(*lpBuffer);
398 | *lpBuffer = NULL;
399 | hRes = E_FAIL;
400 | } else {
401 | *lpNbBytesRecvd = iResult;
402 | }
403 |
404 | return hRes;
405 |
406 | error_close:
407 | g_Synchronized = FALSE;
408 | TunnelClose();
409 | return E_FAIL;
410 | }
411 |
412 |
413 | HRESULT TunnelSend(PCSTR Format, ...)
414 | {
415 | HRESULT hRes=S_OK;
416 | va_list Args;
417 | int iResult;
418 | size_t cbRemaining;
419 |
420 | if (FAILED(hRes=TunnelIsUp()))
421 | {
422 | dprintf("[sync] TunnelSend: tunnel is unavailable\n");
423 | return hRes;
424 | }
425 |
426 | va_start(Args, Format);
427 | hRes = StringCbVPrintfEx(SendBuffer, MAX_SEND, NULL, &cbRemaining, STRSAFE_NULL_ON_FAILURE, Format, Args);
428 | va_end(Args);
429 |
430 | if (FAILED(hRes)){
431 | return hRes;
432 | }
433 |
434 | #if VERBOSE >= 2
435 | dprintf("[sync] send 0x%x bytes, %s\n", MAX_SEND-cbRemaining, SendBuffer);
436 | #endif
437 |
438 | iResult = send(g_Sock, (const char *)SendBuffer, MAX_SEND-((unsigned int)cbRemaining), 0);
439 | if (iResult == SOCKET_ERROR)
440 | {
441 | iResult = WSAGetLastError();
442 | dprintf("[sync] send failed with error %d, 0x%x\n", iResult, g_Sock);
443 | WsaErrMsg(iResult);
444 | g_Synchronized = FALSE;
445 | TunnelClose();
446 | hRes=E_FAIL;
447 | }
448 |
449 | return hRes;
450 | }
451 |
452 |
453 | HRESULT WsaErrMsg(int LastError)
454 | {
455 | HRESULT hRes=S_OK;
456 |
457 | switch(LastError){
458 | case WSAECONNRESET:
459 | dprintf(" -> Connection reset by peer\n");
460 | break;
461 | case WSAENOTCONN:
462 | dprintf(" -> Socket is not connected\n");
463 | break;
464 | case WSAECONNABORTED:
465 | dprintf(" -> Software caused connection abort\n");
466 | break;
467 | default:
468 | break;
469 | }
470 |
471 | return hRes;
472 | }
473 |
--------------------------------------------------------------------------------
/ext_windbg/sync/tunnel.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #undef UNICODE
21 |
22 | extern BOOL g_Synchronized;
23 |
24 | HRESULT TunnelIsUp();
25 |
26 | HRESULT TunnelCreate(PCSTR Host, PCSTR Port);
27 |
28 | HRESULT TunnelClose();
29 |
30 | HRESULT TunnelPoll(int *lpNbBytesRecvd, LPSTR *lpBuffer);
31 |
32 | HRESULT TunnelReceive(int *lpNbBytesRecvd, LPSTR *lpBuffer);
33 |
34 | HRESULT TunnelSend(PCSTR Format, ...);
35 |
36 | HRESULT ToHexString(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString);
37 |
38 | HRESULT ToBase64(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString);
39 |
40 | HRESULT FromBase64(LPCSTR pszString, BYTE **ppbBinary);
41 |
42 | HRESULT NextChunk(char *cmd, char **nextc);
43 |
44 | HRESULT WsaErrMsg(int LastError);
45 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "x64dbg_sync", "x64dbg_sync\x64dbg_sync.vcxproj", "{BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Win32 = Debug|Win32
11 | Debug|x64 = Debug|x64
12 | Release|Win32 = Release|Win32
13 | Release|x64 = Release|x64
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Debug|Win32.ActiveCfg = Debug|Win32
17 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Debug|Win32.Build.0 = Debug|Win32
18 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Debug|x64.ActiveCfg = Debug|x64
19 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Debug|x64.Build.0 = Debug|x64
20 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Release|Win32.ActiveCfg = Release|Win32
21 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Release|Win32.Build.0 = Release|Win32
22 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Release|x64.ActiveCfg = Release|x64
23 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}.Release|x64.Build.0 = Release|x64
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/core.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2014-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #include "core.h"
21 |
22 | #include "pluginsdk\TitanEngine\TitanEngine.h"
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include "tunnel.h"
28 |
29 | #define VERBOSE 0
30 |
31 | // Default host value is locahost
32 | static CHAR *g_DefaultHost = "127.0.0.1";
33 | static CHAR *g_DefaultPort = "9100";
34 | BOOL g_ExtConfFile = false;
35 |
36 | // Command polling feature
37 | static HANDLE g_hPollTimer;
38 | static HANDLE g_hPollCompleteEvent;
39 | static CRITICAL_SECTION g_CritSectPollRelease;
40 |
41 | // Debuggee's state;
42 | ULONG64 g_Offset = NULL;
43 | ULONG64 g_Base = NULL;
44 |
45 | // Synchronisation mode
46 | static BOOL g_SyncAuto = true;
47 |
48 | // Buffer used to solve symbol's name
49 | static CHAR g_NameBuffer[MAX_MODULE_SIZE];
50 |
51 |
52 | HRESULT
53 | LoadConfigurationFile()
54 | {
55 | DWORD count = 0;
56 | HRESULT hRes = S_OK;
57 | HANDLE hFile;
58 | CHAR lpProfile[MAX_PATH] = { 0 };
59 | LPTSTR lpConfHost = NULL;
60 | LPTSTR lpConfPort = NULL;
61 |
62 | count = GetEnvironmentVariable("userprofile", lpProfile, MAX_PATH);
63 | if (count == 0 || count > MAX_PATH){
64 | return E_FAIL;
65 | }
66 |
67 | hRes = StringCbCat(lpProfile, MAX_PATH, CONF_FILE);
68 | if FAILED(hRes){
69 | return E_FAIL;
70 | }
71 |
72 | hFile = CreateFile(lpProfile, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
73 | if (hFile == INVALID_HANDLE_VALUE){
74 | return E_FAIL;
75 | }
76 |
77 | CloseHandle(hFile);
78 |
79 | lpConfHost = (LPTSTR)malloc(MAX_PATH);
80 | lpConfPort = (LPTSTR)malloc(MAX_PATH);
81 | if (lpConfHost == NULL || lpConfPort == NULL){
82 | goto failed;
83 | }
84 |
85 | count = GetPrivateProfileString("INTERFACE", "host", "127.0.0.1", lpConfHost, MAX_PATH, lpProfile);
86 | if ((count == 0) || (count >= (MAX_PATH - 2))){
87 | goto failed;
88 | }
89 |
90 | count = GetPrivateProfileString("INTERFACE", "port", "9100", lpConfPort, MAX_PATH, lpProfile);
91 | if ((count == 0) || (count >= (MAX_PATH - 2))){
92 | goto failed;
93 | }
94 |
95 | g_DefaultHost = lpConfHost;
96 | g_DefaultPort = lpConfPort;
97 | g_ExtConfFile = true;
98 |
99 | return hRes;
100 |
101 | failed:
102 | if (lpConfHost != NULL){ free(lpConfHost); }
103 | if (lpConfPort != NULL){ free(lpConfPort); }
104 |
105 | return E_FAIL;
106 | }
107 |
108 |
109 | // Update state and send info to client: eip module's base address, offset, name
110 | HRESULT
111 | UpdateState()
112 | {
113 | bool bRes = FALSE;
114 | HRESULT hRes = E_FAIL;
115 | DWORD dwRes = 0;
116 | ULONG64 PrevBase = g_Base;
117 | ULONG NameSize = 0;
118 | HANDLE hProcess;
119 |
120 | g_Offset = GetContextData(UE_CIP);
121 |
122 | bRes = DbgGetModuleAt((duint)g_Offset, g_NameBuffer);
123 | if (!bRes)
124 | {
125 | _plugin_logprintf("[sync] UpdateState: no module at %p...\n", g_Offset);
126 | return hRes;
127 | }
128 |
129 | g_Base = DbgModBaseFromName(g_NameBuffer);
130 | if (!g_Base)
131 | {
132 | _plugin_logputs("[sync] UpdateState: could not get module base...");
133 | return hRes;
134 | }
135 |
136 | // Check if we are in a new module
137 | if ((g_Base != PrevBase) & g_SyncAuto)
138 | {
139 | hProcess = ((PROCESS_INFORMATION*)TitanGetProcessInformation())->hProcess;
140 |
141 | dwRes = GetModuleBaseNameA(hProcess, (HMODULE)g_Base, g_NameBuffer, MAX_MODULE_SIZE);
142 | if (dwRes==0)
143 | {
144 | _plugin_logputs("[sync] could not get module base name...");
145 | return hRes;
146 | }
147 |
148 | #if VERBOSE >= 2
149 | _plugin_logprintf("[sync] UpdateState: module : \"%s\"\n", g_NameBuffer);
150 | #endif
151 |
152 | hRes = TunnelSend("[notice]{\"type\":\"module\",\"path\":\"%s\"}\n", g_NameBuffer);
153 | if (FAILED(hRes)){
154 |
155 | return hRes;
156 | }
157 | }
158 |
159 | hRes = TunnelSend("[sync]{\"type\":\"loc\",\"base\":%llu,\"offset\":%llu}\n", g_Base, g_Offset);
160 |
161 | return hRes;
162 | }
163 |
164 |
165 | // Poll socket for incoming commands
166 | HRESULT
167 | PollCmd()
168 | {
169 | bool bRes = FALSE;
170 | HRESULT hRes = S_OK;
171 | int NbBytesRecvd;
172 | int ch = 0xA;
173 | char *msg, *next, *orig = NULL;
174 |
175 | hRes = TunnelPoll(&NbBytesRecvd, &msg);
176 |
177 | if (SUCCEEDED(hRes) & (NbBytesRecvd > 0) & (msg != NULL))
178 | {
179 | next = orig = msg;
180 |
181 | while ((msg - orig) < NbBytesRecvd)
182 | {
183 | next = strchr(msg, ch);
184 | if (next != NULL)
185 | *next = 0;
186 |
187 | bRes = DbgCmdExec(msg);
188 | if (!bRes){
189 | dbgout("[sync] received command: %s (not yet implemented)\n", msg);
190 | }
191 |
192 | // No more command
193 | if (next == NULL)
194 | break;
195 |
196 | msg = next + 1;
197 | }
198 |
199 | free(orig);
200 | }
201 |
202 | return hRes;
203 | }
204 |
205 |
206 | void ReleasePollTimer()
207 | {
208 | BOOL bRes;
209 | DWORD dwErr;
210 |
211 | EnterCriticalSection(&g_CritSectPollRelease);
212 |
213 | #if VERBOSE >= 2
214 | _plugin_logputs("[sync] ReleasePollTimer called\n");
215 | #endif
216 |
217 | if (!(g_hPollTimer == INVALID_HANDLE_VALUE))
218 | {
219 | ResetEvent(g_hPollCompleteEvent);
220 | bRes = DeleteTimerQueueTimer(NULL, g_hPollTimer, g_hPollCompleteEvent);
221 | if (bRes == 0)
222 | {
223 | // msdn: If the error code is ERROR_IO_PENDING, it is not necessary to
224 | // call this function again. For any other error, you should retry the call.
225 | dwErr = GetLastError();
226 | if (dwErr != ERROR_IO_PENDING){
227 | bRes = DeleteTimerQueueTimer(NULL, g_hPollTimer, g_hPollCompleteEvent);
228 | if (!bRes){
229 | #if VERBOSE >= 2
230 | _plugin_logputs("[sync] ReleasePollTimer called\n");
231 | #endif
232 | }
233 |
234 | }
235 | }
236 |
237 | g_hPollTimer = INVALID_HANDLE_VALUE;
238 | }
239 |
240 | LeaveCriticalSection(&g_CritSectPollRelease);
241 | }
242 |
243 |
244 | // Poll timer callback implementation: call PollCmd and set completion event
245 | VOID
246 | CALLBACK PollTimerCb(PVOID lpParameter, BOOL TimerOrWaitFired)
247 | {
248 | HRESULT hRes;
249 | UNREFERENCED_PARAMETER(lpParameter);
250 | UNREFERENCED_PARAMETER(TimerOrWaitFired);
251 |
252 | hRes = PollCmd();
253 |
254 | // If an error occured in PollCmd() the timer callback is deleted.
255 | // (typically happens when client has closed the connection)
256 | if (FAILED(hRes)){
257 | ReleasePollTimer();
258 | }
259 | }
260 |
261 |
262 | // Setup poll timer callback
263 | VOID
264 | CreatePollTimer()
265 | {
266 | BOOL bRes;
267 |
268 | bRes = CreateTimerQueueTimer(&g_hPollTimer, NULL, (WAITORTIMERCALLBACK)PollTimerCb,
269 | NULL, TIMER_PERIOD, TIMER_PERIOD, WT_EXECUTEINTIMERTHREAD);
270 |
271 | if (!(bRes)){
272 | _plugin_logputs("[sync] failed to CreatePollTimer\n");
273 | }
274 | }
275 |
276 |
277 | HRESULT sync(PSTR Args)
278 | {
279 | HRESULT hRes = S_OK;
280 |
281 | // Reset global state
282 | g_Base = NULL;
283 | g_Offset = NULL;
284 |
285 | if (g_Synchronized)
286 | {
287 | _plugin_logputs("[sync] sync update\n");
288 | UpdateState();
289 | goto Exit;
290 | }
291 |
292 | if (FAILED(hRes = TunnelCreate(g_DefaultHost, g_DefaultPort)))
293 | {
294 | _plugin_logputs("[sync] sync failed\n");
295 | goto Exit;
296 | }
297 |
298 | _plugin_logputs("[sync] probing sync\n");
299 |
300 | hRes = TunnelSend("[notice]{\"type\":\"new_dbg\",\"msg\":\"dbg connect - x64_dbg\",\"dialect\":\"x64_dbg\"}\n");
301 | if (FAILED(hRes))
302 | {
303 | _plugin_logputs("[sync] sync aborted\n");
304 | goto Exit;
305 | }
306 |
307 | _plugin_logprintf("[sync] sync is now enabled with host %s\n", g_DefaultHost);
308 | UpdateState();
309 | CreatePollTimer();
310 |
311 | Exit:
312 |
313 | return hRes;
314 | }
315 |
316 |
317 | HRESULT syncoff()
318 | {
319 | HRESULT hRes = S_OK;
320 |
321 | if (!g_Synchronized){
322 | return hRes;
323 | }
324 |
325 | ReleasePollTimer();
326 | hRes = TunnelClose();
327 | _plugin_logputs("[sync] sync is now disabled\n");
328 |
329 | return hRes;
330 | }
331 |
332 |
333 | extern "C" __declspec(dllexport) void CBINITDEBUG(CBTYPE cbType, PLUG_CB_INITDEBUG* info)
334 | {
335 | _plugin_logprintf("[sync] debugging of file %s started!\n", (const char*)info->szFileName);
336 | }
337 |
338 |
339 | extern "C" __declspec(dllexport) void CBSTOPDEBUG(CBTYPE cbType, PLUG_CB_STOPDEBUG* info)
340 | {
341 |
342 | #if VERBOSE >= 2
343 | _plugin_logputs("[sync] debugging stopped!");
344 | #endif
345 | syncoff();
346 | }
347 |
348 |
349 | extern "C" __declspec(dllexport) void CBPAUSEDEBUG(CBTYPE cbType, PLUG_CB_PAUSEDEBUG* info)
350 | {
351 | #if VERBOSE >= 2
352 | _plugin_logputs("[sync] debugging paused!");
353 | #endif
354 |
355 | if (SUCCEEDED(TunnelIsUp()))
356 | {
357 | UpdateState();
358 | CreatePollTimer();
359 | }
360 |
361 | }
362 |
363 |
364 | extern "C" __declspec(dllexport) void CBRESUMEDEBUG(CBTYPE cbType, PLUG_CB_RESUMEDEBUG* info)
365 | {
366 | #if VERBOSE >= 2
367 | _plugin_logputs("[sync] debugging resumed!");
368 | #endif
369 |
370 | ReleasePollTimer();
371 | }
372 |
373 |
374 | extern "C" __declspec(dllexport) void CBDEBUGEVENT(CBTYPE cbType, PLUG_CB_DEBUGEVENT* info)
375 | {
376 | if (info->DebugEvent->dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
377 | {
378 | //_plugin_logprintf("[sync] DebugEvent->EXCEPTION_DEBUG_EVENT->%.8X\n", info->DebugEvent->u.Exception.ExceptionRecord.ExceptionCode);
379 | }
380 | }
381 |
382 |
383 | extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info)
384 | {
385 | switch (info->hEntry)
386 | {
387 | case MENU_ENABLE_SYNC:
388 | {
389 | _plugin_logputs("[sync] enable sync");
390 | sync(NULL);
391 | }
392 | break;
393 |
394 | case MENU_DISABLE_SYNC:
395 | {
396 | _plugin_logputs("[sync] disable sync");
397 | syncoff();
398 | }
399 | break;
400 |
401 | break;
402 | }
403 | }
404 |
405 |
406 | static bool cbSyncCommand(int argc, char* argv[])
407 | {
408 | _plugin_logputs("[sync] sync command!");
409 | sync(NULL);
410 | return true;
411 | }
412 |
413 |
414 | static bool cbSyncoffCommand(int argc, char* argv[])
415 | {
416 | _plugin_logputs("[sync] syncoff command!");
417 | syncoff();
418 | return true;
419 | }
420 |
421 |
422 | void coreInit(PLUG_INITSTRUCT* initStruct)
423 | {
424 | // register commands
425 | _plugin_logprintf("[sync] pluginHandle: %d\n", pluginHandle);
426 |
427 | if (!_plugin_registercommand(pluginHandle, "!sync", cbSyncCommand, false))
428 | _plugin_logputs("[sync] error registering the \"!sync\" command!");
429 |
430 | if (!_plugin_registercommand(pluginHandle, "!syncoff", cbSyncoffCommand, true))
431 | _plugin_logputs("[sync] error registering the \"!syncoff\" command!");
432 |
433 | // initialize globals
434 | g_Synchronized = FALSE;
435 |
436 | g_hPollCompleteEvent = CreateEvent(NULL, true, false, NULL);
437 | if (g_hPollCompleteEvent == NULL)
438 | {
439 | _plugin_logputs("[sync] Command polling feature init failed\n");
440 | return;
441 | }
442 |
443 | InitializeCriticalSection(&g_CritSectPollRelease);
444 |
445 | if (SUCCEEDED(LoadConfigurationFile())){
446 | _plugin_logprintf("[sync] Configuration file loaded\n -> set HOST to %s:%s\n", g_DefaultHost, g_DefaultPort);
447 | }
448 |
449 | }
450 |
451 |
452 | void coreStop()
453 | {
454 | _plugin_unregistercommand(pluginHandle, "!sync");
455 | _plugin_unregistercommand(pluginHandle, "!syncoff");
456 | _plugin_menuclear(hMenu);
457 | }
458 |
459 |
460 | void coreSetup()
461 | {
462 | _plugin_menuaddentry(hMenu, MENU_ENABLE_SYNC, "&Enable sync");
463 | _plugin_menuaddentry(hMenu, MENU_DISABLE_SYNC, "&Disable sync");
464 | }
465 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/core.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2014-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #ifndef _CORE_H
21 | #define _CORE_H
22 |
23 | #include "sync.h"
24 |
25 |
26 | #define TIMER_PERIOD 100
27 | #define CONF_FILE "\\.sync"
28 |
29 | //menu identifiers
30 | #define MENU_ENABLE_SYNC 0
31 | #define MENU_DISABLE_SYNC 1
32 |
33 |
34 | //functions
35 | HRESULT sync(PSTR Args);
36 | HRESULT syncoff();
37 | void coreInit(PLUG_INITSTRUCT* initStruct);
38 | void coreStop();
39 | void coreSetup();
40 |
41 | #endif // _CORE_H
42 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/sync.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2014-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #include "sync.h"
21 | #include "core.h"
22 |
23 | #pragma comment (lib, "ws2_32.lib")
24 | #pragma comment (lib, "crypt32.lib")
25 |
26 |
27 | #define plugin_name "SyncPlugin"
28 | #define plugin_version 1
29 | #define VERBOSE 0
30 |
31 |
32 | int pluginHandle;
33 | HWND hwndDlg;
34 | int hMenu;
35 |
36 | DLL_EXPORT bool pluginit(PLUG_INITSTRUCT* initStruct)
37 | {
38 | initStruct->pluginVersion = plugin_version;
39 | initStruct->sdkVersion = PLUG_SDKVERSION;
40 | strcpy_s(initStruct->pluginName, plugin_name);
41 | pluginHandle = initStruct->pluginHandle;
42 | coreInit(initStruct);
43 | return true;
44 | }
45 |
46 | DLL_EXPORT bool plugstop()
47 | {
48 | coreStop();
49 | return true;
50 | }
51 |
52 | DLL_EXPORT void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
53 | {
54 | hwndDlg = setupStruct->hwndDlg;
55 | hMenu = setupStruct->hMenu;
56 | coreSetup();
57 | }
58 |
59 | extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
60 | {
61 | return TRUE;
62 | }
63 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/sync.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2014-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #ifndef _PLUGINMAIN_H
21 | #define _PLUGINMAIN_H
22 |
23 | #include
24 | #include "pluginsdk\_plugins.h"
25 |
26 |
27 | #ifndef DLL_EXPORT
28 | #define DLL_EXPORT __declspec(dllexport)
29 | #endif //DLL_EXPORT
30 |
31 | //superglobal variables
32 | extern int pluginHandle;
33 | extern HWND hwndDlg;
34 | extern int hMenu;
35 |
36 | #ifdef __cplusplus
37 | extern "C"
38 | {
39 | #endif
40 |
41 | DLL_EXPORT bool pluginit(PLUG_INITSTRUCT* initStruct);
42 | DLL_EXPORT bool plugstop();
43 | DLL_EXPORT void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
44 |
45 | #ifdef __cplusplus
46 | }
47 | #endif
48 |
49 | #endif //_PLUGINMAIN_H
50 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/tunnel.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2014-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "tunnel.h"
27 |
28 | #define MAX_SEND 8192
29 | #define MAX_OUT 1024
30 |
31 | static CHAR SendBuffer[MAX_SEND];
32 | static CHAR RecvBuffer[MAX_SEND];
33 | BOOL g_Synchronized;
34 | SOCKET g_Sock = INVALID_SOCKET;
35 | WSADATA wsaData;
36 |
37 |
38 | void dbgout(char *fmt, ...)
39 | {
40 | char buffer[MAX_OUT] = {0};
41 |
42 | va_list args;
43 | va_start(args, fmt);
44 | vsprintf_s(buffer, MAX_OUT, fmt, args);
45 | OutputDebugStringA(buffer);
46 | va_end(args);
47 | }
48 |
49 |
50 | void dbgoutW(wchar_t* fmt, ...)
51 | {
52 | wchar_t buffer[MAX_OUT] = {0};
53 |
54 | va_list args;
55 | va_start(args, fmt);
56 | vswprintf_s(buffer, MAX_OUT, fmt, args);
57 | OutputDebugStringW(buffer);
58 | va_end(args);
59 | }
60 |
61 |
62 | #if _NT_TARGET_VERSION_WINXPOR2K3
63 | void
64 | trimcrlf(LPSTR pszSrcString)
65 | {
66 | LPSTR pszDestString = pszSrcString;
67 |
68 | while(*pszSrcString)
69 | {
70 | if (*pszSrcString == 0x0D)
71 | {
72 | pszSrcString++;
73 | pszSrcString++;
74 | }
75 | else
76 | {
77 | *pszDestString=*pszSrcString;
78 | pszDestString++;
79 | pszSrcString++;
80 | }
81 | }
82 |
83 | *pszDestString= *pszSrcString;
84 | }
85 | #endif
86 |
87 |
88 | HRESULT
89 | FromBase64(LPSTR pszString, BYTE **ppbBinary)
90 | {
91 | BOOL bRes = FALSE;
92 | HRESULT hRes = S_OK;
93 | DWORD cbBinary = 0;
94 |
95 | bRes = CryptStringToBinaryA(pszString, 0, CRYPT_STRING_BASE64, NULL, &cbBinary, NULL, NULL);
96 | if (!bRes){
97 | dbgout("[sync] failed at CryptStringToBinaryA: %d\n", GetLastError());
98 | return E_FAIL;
99 | }
100 |
101 | *ppbBinary = (BYTE *) malloc(cbBinary+1);
102 |
103 | if (ppbBinary==NULL){
104 | dbgout("[sync] failed at allocate buffer: %d\n", GetLastError());
105 | return E_FAIL;
106 | }
107 |
108 | bRes = CryptStringToBinaryA(pszString, 0, CRYPT_STRING_BASE64, *ppbBinary, &cbBinary, NULL, NULL);
109 | if (!bRes){
110 | dbgout("[sync] send failed at CryptStringToBinaryA: %d\n", GetLastError());
111 | return E_FAIL;
112 | }
113 |
114 | *((char *)((*ppbBinary)+cbBinary)) = 0;
115 |
116 | return hRes;
117 | }
118 |
119 |
120 |
121 | HRESULT
122 | ToBase64(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString)
123 | {
124 | BOOL bRes=FALSE;
125 | HRESULT hRes=S_OK;
126 | DWORD cchString = 0;
127 |
128 | bRes = CryptBinaryToStringA(pbBinary, cbBinary, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &cchString);
129 | if (!bRes){
130 | dbgout("[sync] send failed at CryptBinaryToString: %d\n", GetLastError());
131 | return E_FAIL;
132 | }
133 |
134 | *pszString = (LPSTR) malloc(cchString);
135 |
136 | if (pszString==NULL){
137 | dbgout("[sync] failed at allocate buffer: %d\n", GetLastError());
138 | return E_FAIL;
139 | }
140 |
141 | bRes = CryptBinaryToStringA(pbBinary, cbBinary, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, *pszString, &cchString);
142 | if (!bRes){
143 | dbgout("[sync] send failed at CryptBinaryToString: %d\n", GetLastError());
144 | return E_FAIL;
145 | }
146 |
147 | /*
148 | CRYPT_STRING_NOCRLF 0x40000000
149 | Windows Server 2003 and Windows XP: This value is not supported
150 | */
151 |
152 | #if _NT_TARGET_VERSION_WINXPOR2K3
153 | trimcrlf(*pszString);
154 | #endif
155 |
156 | return hRes;
157 | }
158 |
159 |
160 | // return S_OK if socket is created and synchronized
161 | HRESULT TunnelIsUp()
162 | {
163 | HRESULT hRes=S_OK;
164 |
165 | if( (g_Sock==INVALID_SOCKET) | (!g_Synchronized))
166 | hRes = E_FAIL;
167 |
168 | return hRes;
169 | }
170 |
171 |
172 | HRESULT
173 | TunnelCreate(PCSTR Host, PCSTR Port)
174 | {
175 | HRESULT hRes=S_OK;
176 | struct addrinfo *result = NULL, *ptr = NULL, hints;
177 | int iResult;
178 | int bOptLen = sizeof (BOOL);
179 | BOOL bOptVal = FALSE;
180 |
181 | if (FAILED(hRes = WSAStartup(MAKEWORD(2,2), &wsaData))) {
182 | dbgout("[sync] WSAStartup failed with error %d\n", hRes);
183 | goto err_clean;
184 | }
185 |
186 | if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 )
187 | {
188 | dbgout("[sync] WSAStartup failed, Winsock version not supported\n");
189 | hRes = E_FAIL;
190 | goto err_clean;
191 | }
192 |
193 | ZeroMemory( &hints, sizeof(hints) );
194 | hints.ai_family = AF_UNSPEC;
195 | hints.ai_socktype = SOCK_STREAM;
196 | hints.ai_protocol = IPPROTO_TCP;
197 |
198 | // Resolve the server address and port
199 | iResult = getaddrinfo(Host, Port, &hints, &result);
200 | if ( iResult != 0 ) {
201 | dbgout("[sync] getaddrinfo failed with error: %d\n", iResult);
202 | hRes = E_FAIL;
203 | goto err_clean;
204 | }
205 |
206 | #if VERBOSE >= 2
207 | dbgout("[sync] getaddrinfo ok\n");
208 | #endif
209 |
210 | // Attempt to connect to an address until one succeeds
211 | for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
212 |
213 | // Create a SOCKET for connecting to server
214 | g_Sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
215 | if (g_Sock == INVALID_SOCKET) {
216 | dbgout("[sync] socket failed with error: %ld\n", WSAGetLastError());
217 | hRes = E_FAIL;
218 | goto err_clean;
219 | }
220 |
221 | #if VERBOSE >= 2
222 | dbgout("[sync] socket ok\n");
223 | #endif
224 |
225 | bOptVal = TRUE;
226 | iResult = setsockopt(g_Sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &bOptVal, bOptLen);
227 | if (iResult == SOCKET_ERROR)
228 | {
229 | dbgout("[sync] setsockopt for SO_KEEPALIVE failed with error: %u\n", WSAGetLastError());
230 | }
231 |
232 | #if VERBOSE >= 2
233 | dbgout("[sync] Set SO_KEEPALIVE: ON\n");
234 | #endif
235 |
236 | iResult = setsockopt(g_Sock, IPPROTO_TCP, TCP_NODELAY, (char *) &bOptVal, bOptLen);
237 | if (iResult == SOCKET_ERROR)
238 | {
239 | dbgout("[sync] setsockopt for IPPROTO_TCP failed with error: %u\n", WSAGetLastError());
240 | }
241 |
242 | #if VERBOSE >= 2
243 | dbgout("[sync] Set TCP_NODELAY: ON\n");
244 | #endif
245 |
246 | // Connect to server.
247 | iResult = connect(g_Sock, ptr->ai_addr, (int)ptr->ai_addrlen);
248 | if (iResult == SOCKET_ERROR) {
249 | closesocket(g_Sock);
250 | g_Sock = INVALID_SOCKET;
251 | dbgout("[sync] connect failed (check if broker is running)\n");
252 | continue;
253 | }
254 |
255 | dbgout("[sync] sync success, sock 0x%x\n", g_Sock);
256 | break;
257 | }
258 |
259 | if (g_Sock == INVALID_SOCKET){
260 | goto err_clean;
261 | }
262 |
263 | freeaddrinfo(result);
264 | g_Synchronized = TRUE;
265 |
266 | return S_OK;
267 |
268 | err_clean:
269 | WSACleanup();
270 | return hRes;
271 | }
272 |
273 |
274 | HRESULT TunnelClose()
275 | {
276 | HRESULT hRes=S_OK;
277 | int iResult;
278 |
279 | if(SUCCEEDED(TunnelIsUp()))
280 | {
281 | hRes=TunnelSend("[notice]{\"type\":\"dbg_quit\",\"msg\":\"dbg disconnected\"}\n");
282 | if(FAILED(hRes))
283 | return hRes;
284 | }
285 |
286 | if (!(g_Sock == INVALID_SOCKET))
287 | {
288 | iResult = closesocket(g_Sock);
289 | g_Sock = INVALID_SOCKET;
290 |
291 | if (iResult == SOCKET_ERROR){
292 | dbgout("[sync] closesocket failed with error %d\n", WSAGetLastError());
293 | }
294 | }
295 |
296 | dbgout("[sync] sync is off\n");
297 | g_Synchronized = FALSE;
298 | WSACleanup();
299 | return hRes;
300 | }
301 |
302 |
303 | HRESULT TunnelPoll(int *lpNbBytesRecvd, LPSTR *lpBuffer)
304 | {
305 | HRESULT hRes=S_OK;
306 | int iResult;
307 | u_long iMode = 1;
308 |
309 | iResult = ioctlsocket(g_Sock, FIONBIO, &iMode);
310 | if (iResult != NO_ERROR)
311 | {
312 | printf("[sync] TunnelPoll ioctlsocket failed with error: %ld\n", iResult);
313 | return E_FAIL;
314 | }
315 |
316 | hRes = TunnelReceive(lpNbBytesRecvd, lpBuffer);
317 | if (FAILED(hRes)){
318 | return hRes;
319 | }
320 |
321 | iMode = 0;
322 | iResult = ioctlsocket(g_Sock, FIONBIO, &iMode);
323 | if (iResult != NO_ERROR)
324 | {
325 | printf("[sync] TunnelPoll ioctlsocket failed with error: %ld\n", iResult);
326 | return E_FAIL;
327 | }
328 |
329 | return hRes;
330 | }
331 |
332 | HRESULT TunnelReceive(int *lpNbBytesRecvd, LPSTR *lpBuffer)
333 | {
334 | HRESULT hRes=S_OK;
335 | int iResult;
336 | errno_t err;
337 | *lpNbBytesRecvd = 0;
338 |
339 | if(FAILED(hRes=TunnelIsUp()))
340 | {
341 | dbgout("[sync] TunnelReceive: tunnel is not available\n");
342 | return hRes;
343 | }
344 |
345 | iResult = recv(g_Sock, RecvBuffer, MAX_SEND, 0);
346 | if ( iResult == SOCKET_ERROR )
347 | {
348 | iResult = WSAGetLastError();
349 | if (iResult == WSAEWOULDBLOCK)
350 | {
351 | return hRes;
352 | }
353 | else
354 | {
355 | dbgout("[sync] recv failed with error: %d, 0x%x\n", iResult, g_Sock);
356 | WsaErrMsg(iResult);
357 | goto error_close;
358 | }
359 | }
360 | else if ( iResult == 0 ) {
361 | dbgout("[sync] recv: connection closed\n");
362 | goto error_close;
363 | }
364 |
365 | *lpBuffer = (LPSTR) calloc(iResult+1, sizeof(CHAR));
366 | if (lpBuffer == NULL) {
367 | dbgout("[sync] failed at allocate buffer: %d\n", GetLastError());
368 | return E_FAIL;
369 | }
370 |
371 | err = memcpy_s(*lpBuffer, iResult+1, RecvBuffer, iResult);
372 | if (err) {
373 | dbgout("[sync] memcpy_s failed to copy received buffer\n");
374 | free(*lpBuffer);
375 | *lpBuffer = NULL;
376 | hRes = E_FAIL;
377 | } else {
378 | *lpNbBytesRecvd = iResult;
379 | }
380 |
381 | return hRes;
382 |
383 | error_close:
384 | g_Synchronized = FALSE;
385 | TunnelClose();
386 | return E_FAIL;
387 | }
388 |
389 |
390 | HRESULT TunnelSend(PCSTR Format, ...)
391 | {
392 | HRESULT hRes=S_OK;
393 | va_list Args;
394 | int iResult;
395 | size_t cbRemaining;
396 |
397 | if(FAILED(hRes=TunnelIsUp()))
398 | {
399 | dbgout("[sync] TunnelSend: tunnel is unavailable\n");
400 | return hRes;
401 | }
402 |
403 | va_start(Args, Format);
404 | hRes = StringCbVPrintfExA(SendBuffer, MAX_SEND, NULL, &cbRemaining, STRSAFE_NULL_ON_FAILURE, Format, Args);
405 | va_end(Args);
406 |
407 | if (FAILED(hRes))
408 | return hRes;
409 |
410 | #if VERBOSE >= 2
411 | dbgout("[sync] send 0x%x bytes, %s\n", MAX_SEND-cbRemaining, SendBuffer);
412 | #endif
413 |
414 | iResult = send(g_Sock, (const char *)SendBuffer, MAX_SEND-((unsigned int)cbRemaining), 0);
415 | if(iResult == SOCKET_ERROR)
416 | {
417 | iResult = WSAGetLastError();
418 | dbgout("[sync] send failed with error %d, 0x%x\n", iResult, g_Sock);
419 | WsaErrMsg(iResult);
420 | g_Synchronized = FALSE;
421 | TunnelClose();
422 | hRes=E_FAIL;
423 | }
424 |
425 | return hRes;
426 | }
427 |
428 | HRESULT WsaErrMsg(int LastError)
429 | {
430 | HRESULT hRes=S_OK;
431 |
432 | switch(LastError){
433 | case WSAECONNRESET:
434 | dbgout(" -> Connection reset by peer\n");
435 | break;
436 | case WSAENOTCONN:
437 | dbgout(" -> Socket is not connected\n");
438 | break;
439 | case WSAECONNABORTED:
440 | dbgout(" -> Software caused connection abort\n");
441 | break;
442 | default:
443 | break;
444 | }
445 |
446 | return hRes;
447 | }
448 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/tunnel.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2012-2015, Quarkslab.
3 |
4 | This file is part of qb-sync.
5 |
6 | qb-sync is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #ifndef _TUNNEL_H
21 | #define _TUNNEL_H
22 |
23 | extern BOOL g_Synchronized;
24 |
25 | extern void dbgout(char *fmt, ...);
26 |
27 | extern void dbgoutW(wchar_t* fmt, ...);
28 |
29 | HRESULT TunnelIsUp();
30 |
31 | HRESULT TunnelCreate(PCSTR Host, PCSTR Port);
32 |
33 | HRESULT TunnelClose();
34 |
35 | HRESULT TunnelPoll(int *lpNbBytesRecvd, LPSTR *lpBuffer);
36 |
37 | HRESULT TunnelReceive(int *lpNbBytesRecvd, LPSTR *lpBuffer);
38 |
39 | HRESULT TunnelSend(PCSTR Format, ...);
40 |
41 | HRESULT ToBase64(const BYTE *pbBinary, DWORD cbBinary, LPSTR *pszString);
42 |
43 | HRESULT FromBase64(LPSTR pszString, BYTE **ppbBinary);
44 |
45 | HRESULT WsaErrMsg(int LastError);
46 |
47 | HRESULT convert_tow(const char * mbstr, PTCH *wcstr);
48 |
49 |
50 | #endif // _TUNNEL_H
51 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/x64dbg_sync.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {BDD87FFE-A2CA-4CB6-859E-7058A59C24C6}
23 | Win32Proj
24 | x64dbg_sync
25 |
26 |
27 |
28 | DynamicLibrary
29 | true
30 | v120
31 | MultiByte
32 |
33 |
34 | DynamicLibrary
35 | true
36 | v120
37 | MultiByte
38 |
39 |
40 | DynamicLibrary
41 | false
42 | v120
43 | false
44 | MultiByte
45 |
46 |
47 | DynamicLibrary
48 | false
49 | v120
50 | false
51 | MultiByte
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | false
71 | .dp32
72 |
73 |
74 | false
75 | .dp64
76 |
77 |
78 | false
79 | .dp32
80 |
81 |
82 | false
83 | .dp64
84 |
85 |
86 |
87 | NotUsing
88 | Level3
89 | Disabled
90 | WIN32;_DEBUG;_WINDOWS;_USRDLL;X64DBG_SYNC_EXPORTS;%(PreprocessorDefinitions)
91 | true
92 |
93 |
94 | MultiThreaded
95 | ProgramDatabase
96 |
97 |
98 | Windows
99 | false
100 | psapi.lib;pluginsdk\x32_dbg.lib;pluginsdk\x32_bridge.lib;pluginsdk\TitanEngine\TitanEngine_x86.lib;%(AdditionalDependencies)
101 |
102 |
103 | Default
104 | NotSet
105 | true
106 |
107 |
108 | false
109 |
110 |
111 |
112 |
113 | NotUsing
114 | Level3
115 | Disabled
116 | WIN32;_DEBUG;_WINDOWS;_USRDLL;X64DBG_SYNC_EXPORTS;%(PreprocessorDefinitions)
117 | true
118 |
119 |
120 |
121 |
122 | MultiThreaded
123 |
124 |
125 | Windows
126 | false
127 | psapi.lib;pluginsdk\x64_dbg.lib;pluginsdk\x64_bridge.lib;pluginsdk\TitanEngine\TitanEngine_x64.lib;%(AdditionalDependencies)
128 |
129 |
130 | Default
131 |
132 |
133 | false
134 |
135 |
136 |
137 |
138 | Level3
139 | NotUsing
140 | Full
141 | true
142 | true
143 | WIN32;NDEBUG;_WINDOWS;_USRDLL;X64DBG_SYNC_EXPORTS;%(PreprocessorDefinitions)
144 | true
145 |
146 |
147 | MultiThreaded
148 |
149 |
150 | Windows
151 | false
152 | true
153 | true
154 | psapi.lib;pluginsdk\x32_dbg.lib;pluginsdk\x32_bridge.lib;pluginsdk\TitanEngine\TitanEngine_x86.lib;%(AdditionalDependencies)
155 |
156 |
157 | Default
158 | NotSet
159 |
160 |
161 | false
162 |
163 |
164 |
165 |
166 | Level3
167 | NotUsing
168 | Full
169 | true
170 | true
171 | WIN32;NDEBUG;_WINDOWS;_USRDLL;X64DBG_SYNC_EXPORTS;%(PreprocessorDefinitions)
172 | true
173 |
174 |
175 |
176 |
177 | MultiThreaded
178 |
179 |
180 | Windows
181 | false
182 | true
183 | true
184 | psapi.lib;pluginsdk\x64_dbg.lib;pluginsdk\x64_bridge.lib;pluginsdk\TitanEngine\TitanEngine_x64.lib;%(AdditionalDependencies)
185 |
186 |
187 | Default
188 |
189 |
190 | false
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/x64dbg_sync.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 | Header Files
26 |
27 |
28 |
29 |
30 | Source Files
31 |
32 |
33 | Source Files
34 |
35 |
36 | Source Files
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ext_x64dbg/x64dbg_sync/x64dbg_sync.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------