├── 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 | --------------------------------------------------------------------------------