Move inspection tools into their own subdirectory.
authorMarcus Brinkmann <marcus.brinkmann@ruhr-uni-bochum.de>
Fri, 13 Aug 2010 11:51:42 +0000 (13:51 +0200)
committerMarcus Brinkmann <marcus.brinkmann@ruhr-uni-bochum.de>
Fri, 13 Aug 2010 11:51:42 +0000 (13:51 +0200)
inspection/dump-active-process.c [new file with mode: 0644]
inspection/get-system-info.c [new file with mode: 0644]
inspection/global-memory-status.c [new file with mode: 0644]
inspection/memory-layout-stuff.c [new file with mode: 0644]
inspection/virtual-alloc.c [new file with mode: 0644]
inspection/virtual-query-imager.py [new file with mode: 0644]
inspection/virtual-query.c [new file with mode: 0644]

diff --git a/inspection/dump-active-process.c b/inspection/dump-active-process.c
new file mode 100644 (file)
index 0000000..3eb132b
--- /dev/null
@@ -0,0 +1,184 @@
+#include <stdio.h>
+#include <windows.h>
+#include <ctype.h>
+
+void
+dump_mbi_header ()
+{
+  printf ("alc-base   alc-prot address    size       state    protect  type     \n");
+} 
+
+
+int
+dump_protect_flags (DWORD flags)
+{
+  DWORD pr = flags & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE
+                     | PAGE_EXECUTE_WRITECOPY | PAGE_READONLY
+                     | PAGE_READWRITE | PAGE_WRITECOPY);
+  DWORD pw = flags & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY
+                     | PAGE_READWRITE | PAGE_WRITECOPY);
+  DWORD pc = flags & (PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
+  DWORD px = flags & (PAGE_EXECUTE | PAGE_EXECUTE_READ
+                     | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
+  
+  printf ("%c%c%c %c%c%c  ",
+         pr ? 'r' : '-', pc ? 'c' : (pw ? 'w' : '-'), px ? 'x' : '-',
+         (flags & PAGE_GUARD) ? 'g' : '-',
+         (flags & PAGE_NOCACHE) ? 'n' : '-',
+#ifdef PAGE_PHYSICAL
+         (flags & PAGE_PHYSICAL) ? 'p' : 
+#endif
+         '-');
+  return pr;
+}
+
+
+void
+dump_state (DWORD state)
+{
+  switch (state)
+    {
+    case MEM_COMMIT:
+      printf ("commit   ");
+      return;
+    case MEM_FREE:
+      printf ("free     ");
+      return;
+    case MEM_RESERVE:
+      printf ("reserve  ");
+      return;
+    default:
+      printf ("unknown  ");
+    }
+}
+
+
+void
+dump_type (DWORD mtype)
+{
+  switch (mtype)
+    {
+    case MEM_IMAGE:
+      printf ("image    ");
+      return;
+    case MEM_MAPPED:
+      printf ("mapped   ");
+      return;
+    case MEM_PRIVATE:
+      printf ("private  ");
+      return;
+    default:
+      printf ("unknown  ");
+    }
+}
+
+
+void
+dump_region (unsigned char *base, unsigned int size)
+{
+  int i;
+  int in_nulls = 0;
+  /* Base and size are page-aligned.  */
+  while (size != 0)
+    {
+      for (i = 0; i < 16; i++)
+       if (base[i])
+         break;
+      if (i == 16)
+       {
+         /* Only zeroes.  */
+         if (! in_nulls)
+           {
+             printf ("*\n");
+             in_nulls = 1;
+           }
+         goto next;
+       }
+      in_nulls = 0;
+      printf ("0x%08x:", base);
+      for (i = 0; i < 16; i++)
+       {
+         if (i == 8)
+           printf (" ");
+         printf (" %02x", base[i]);
+       }
+      printf ("  ");
+      for (i = 0; i < 16; i++)
+       {
+         if (i == 8)
+           printf (" ");
+         printf ("%c", isprint(base[i]) ? base[i] : '.');
+       }
+      printf ("\n");
+    next:
+      base += 16;
+      size -= 16;
+    }
+}
+
+
+void
+dump_mbi (PMEMORY_BASIC_INFORMATION mbi)
+{
+  int pr;
+  printf ("0x%08x ", mbi->AllocationBase);
+  dump_protect_flags (mbi->AllocationProtect);
+  printf ("0x%08x ", mbi->BaseAddress);
+  printf ("0x%08x ", mbi->RegionSize);
+  dump_state (mbi->State);
+  pr = dump_protect_flags (mbi->Protect);
+  dump_type (mbi->Type);
+  printf ("\n");
+  if (pr)
+    dump_region (mbi->BaseAddress, mbi->RegionSize);
+}
+
+
+int
+main (int argc, char* argv[])
+{
+  MEMORY_BASIC_INFORMATION mbi;
+  SYSTEM_INFO si;
+  void *addr;
+  
+  memset (&si, '\0', sizeof (si));
+  GetSystemInfo (&si);
+  dump_mbi_header ();
+
+  addr = 0;
+  do
+    {
+      DWORD res;
+      void *new_addr;
+
+      memset (&mbi, '\0', sizeof (mbi));
+      res = VirtualQuery (addr, &mbi, sizeof (mbi));
+      if (res == 0)
+       {
+          printf ("Skipping over %p\n", addr);
+         new_addr = addr + si.dwPageSize;
+         if (new_addr < addr)
+           break;
+         addr = new_addr;
+          continue;
+        }
+      if (res != sizeof (mbi))
+       {
+         printf ("Unexpected return size: %i (expected %i)\n",
+                 res, sizeof (mbi));
+       }
+      dump_mbi (&mbi);
+      /* Check for overflow.  */
+      new_addr = addr + mbi.RegionSize;
+      if (new_addr < addr)
+       break;
+      addr = new_addr;
+    }
+  while (1);
+
+  /* Give ssh time to flush buffers.  */
+  fflush (stdout);
+  Sleep (300);
+  return 0;
+}
diff --git a/inspection/get-system-info.c b/inspection/get-system-info.c
new file mode 100644 (file)
index 0000000..3eba7fd
--- /dev/null
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <windows.h>
+
+char const*
+get_proc_arch (WORD proc_arch)
+{
+  switch (proc_arch)
+    {
+    case PROCESSOR_ARCHITECTURE_INTEL:
+      return "INTEL";
+
+    case PROCESSOR_ARCHITECTURE_MIPS:
+      return "MIPS";
+
+    case PROCESSOR_ARCHITECTURE_ALPHA:
+      return "ALPHA";
+
+    case PROCESSOR_ARCHITECTURE_PPC:
+      return "PPC";
+
+    case PROCESSOR_ARCHITECTURE_SHX:
+      return "SHX";
+
+    case PROCESSOR_ARCHITECTURE_ARM:
+      return "ARM";
+
+    case PROCESSOR_ARCHITECTURE_IA64:
+      return "IA64";
+
+    case PROCESSOR_ARCHITECTURE_ALPHA64:
+      return "ALPHA64";
+
+    case PROCESSOR_ARCHITECTURE_MSIL:
+      return "MSIL";
+
+    case PROCESSOR_ARCHITECTURE_AMD64:
+      return "AMD64";
+
+    case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
+      return "WIN64";
+
+    case PROCESSOR_ARCHITECTURE_UNKNOWN:
+      return "UNKNOWN";
+
+    default:
+      return "(unknown)";
+    }
+};
+
+
+int
+main (int argc, char* argv[])
+{
+  SYSTEM_INFO si;
+  memset (&si, '\0', sizeof (si));
+
+  GetSystemInfo (&si);
+  printf ("Processor Architecture/Type/Level/Revision: %s/%i/%i/%i\n",
+         get_proc_arch (si.wProcessorArchitecture),
+         si.dwProcessorType, (int) si.wProcessorLevel, (int)si.wProcessorRevision);
+  printf ("Page Size: %i\n", si.dwPageSize);
+  printf ("Application Virtual Address Space: %p - %p\n",
+         si.lpMinimumApplicationAddress,
+         si.lpMaximumApplicationAddress);
+  printf ("Allocation Granularity: %i\n",
+         si.dwAllocationGranularity);
+
+  /* Give ssh time to flush buffers.  */
+  fflush (stdout);
+  Sleep (300);
+  return 0;
+}
diff --git a/inspection/global-memory-status.c b/inspection/global-memory-status.c
new file mode 100644 (file)
index 0000000..7816b81
--- /dev/null
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <windows.h>
+
+int
+main (int argc, char* argv[])
+{
+  MEMORYSTATUS ms;
+  memset (&ms, '\0', sizeof (ms));
+  ms.dwLength = sizeof (ms);
+
+  GlobalMemoryStatus (&ms);
+  printf ("Overall memory load (0-100): %i\n",
+         ms.dwMemoryLoad);
+  printf ("Physical memory available/total: 0x%08x/0x%08x (%i)\n",
+         ms.dwAvailPhys, ms.dwTotalPhys);
+  printf ("Pagefile memory available/total: 0x%08x/0x%08x (%i)\n",
+         ms.dwAvailPageFile, ms.dwTotalPageFile);
+  printf ("Virtual  memory available/total: 0x%08x/0x%08x (%i)\n",
+         ms.dwAvailVirtual, ms.dwTotalVirtual);
+
+  /* Give ssh time to flush buffers.  */
+  fflush (stdout);
+  Sleep (300);
+  return 0;
+}
diff --git a/inspection/memory-layout-stuff.c b/inspection/memory-layout-stuff.c
new file mode 100644 (file)
index 0000000..3629db6
--- /dev/null
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <windows.h>
+
+int some_data;
+char *str = "test";
+int some_more_data = 10;
+
+int
+main (int argc, char* argv[])
+{
+  int a = argc;
+  void *p = malloc (128);
+
+  printf ("Code:  %p\n", &main);
+  printf ("RO D:  %p\n", str);
+  printf ("RW D:  %p\n", &some_more_data);
+  printf ("BSS:   %p\n", &some_data);  
+  printf ("Stack: %p\n", &a);
+  printf ("Heap:  %p\n", p);
+  printf ("DLL:   %p\n", GetProcAddress(GetModuleHandle(TEXT("coredll.dll")), 
+       TEXT("Sleep")));
+
+  /* Give ssh time to flush buffers.  */
+  fflush (stdout);
+  Sleep (300);
+  return 0;
+}
diff --git a/inspection/virtual-alloc.c b/inspection/virtual-alloc.c
new file mode 100644 (file)
index 0000000..b2f8444
--- /dev/null
@@ -0,0 +1,36 @@
+#include <windows.h>
+
+int
+main (int argc, char *argv[])
+{
+  int asize = 1024*1024*1024;
+  int total_high = 0;
+  int total_low = 0;
+
+  printf ("Trying size 0x%08x\n", asize);
+
+  while (asize >= 4096)
+    {
+      void *ptr;
+
+      ptr = VirtualAlloc (NULL, asize, MEM_RESERVE, PAGE_NOACCESS);
+      if (ptr != NULL)
+       {
+         printf ("Allocated region of size 0x%08x at 0x%p\n", asize, ptr);
+         if (ptr >= (void*)0x40000000)
+           total_high += asize;
+         else
+           total_low += asize;
+       }
+      else
+       {
+         asize /= 2;
+         printf ("Trying size 0x%08x\n", asize);
+       }
+    }
+  printf ("Total High: 0x%08x\n", total_high);
+  printf ("Total Low:  0x%08x\n", total_low);
+  Sleep (300);
+  return 0;
+}
diff --git a/inspection/virtual-query-imager.py b/inspection/virtual-query-imager.py
new file mode 100644 (file)
index 0000000..39a12e0
--- /dev/null
@@ -0,0 +1,194 @@
+import fileinput
+import Image, ImageDraw, ImageFont, ImageOps
+
+
+# Pagesize
+psize = 4096
+
+# A column is 32MB
+p_per_col = 32*1024*1024/psize
+# 4 GB address space.
+slots = 2*1024*1024*1024/psize/p_per_col
+
+slotwidth = 60
+pheight = 1
+
+# colors:
+
+size = p_per_col * pheight, slotwidth * slots
+
+im = Image.new ("RGB", size)
+
+draw = ImageDraw.Draw(im)
+draw.rectangle ((0,0) + im.size, fill="#ffffff")
+
+
+def getcolor (state, prot, prot_, type):
+    if state == "free":
+        return "#cccccc"
+    if state == "reserve":
+        if type == "image":
+            return "#88ff88"
+        if type == "private":
+            return "#ff8888"
+        if type == "mapped":
+            return "#8888ff"
+        return "#ffff00"
+    if state == "commit":
+        if type == "image":
+            return "#44dd44"
+        if type == "private":
+            return "#dd4444"
+        if type == "mapped":
+            return "#4444dd"
+        return "#ffff00"
+    return "#ffff00"
+
+    # alc-base   alc-prot address    size       state    protect  type     
+    # 0x00000000 --- ---  0x00001000 0x0000f000 free     --- ---  unknown  
+    # 0x00010000 --- ---  0x00014000 0x0000a000 reserve  --- ---  image    
+    # 0x00000000 --- ---  0x0001e000 0x017a2000 free     --- ---  unknown  
+    # 0x017c0000 --- ---  0x017c0000 0x000fe000 reserve  --- ---  private  
+    # 0x017c0000 --- ---  0x018be000 0x00002000 commit   rw- ---  private  
+    # 0x018c0000 --- ---  0x018c0000 0x00002000 commit   rw- -n-  private  
+
+
+def upperleft (col, row):
+    col = slots - col - 1
+    return (row * pheight, col * slotwidth)
+
+def lowerright (col, row):
+    col = slots - col - 1
+    return ((row + 1) * pheight - 1, (col + 1) * slotwidth - 1)
+
+
+def drawit_ (draw, pstart, pstop, state, prot, prot_, type):
+    col = pstart / p_per_col
+    pstart = pstart - col * p_per_col
+    # inclusive now
+    pstop = (pstop - col * p_per_col) - 1
+
+    # Same col for pstop, ensured by drawit
+    color = getcolor (state, prot, prot_, type)
+    draw.rectangle (upperleft(col, pstop) + lowerright (col, pstart),
+                    color)
+   
+
+def drawit (draw, addr, size, state, prot, prot_, type):
+    if addr >= 2*1024*1024*1024:
+        return
+    
+    end = addr + size
+    while addr < end:
+        next = ((addr + p_per_col) / p_per_col) * p_per_col
+        if next > end:
+            next = end
+        drawit_ (draw, addr, next, state, prot, prot_, type)
+        addr = next
+
+for line in fileinput.input():
+    if line[0] != '0':
+        continue
+    # alc-base   alc-prot address    size       state    protect  type     
+    # 0x00000000 --- ---  0x00001000 0x0000f000 free     --- ---  unknown  
+    # 0x00010000 --- ---  0x00014000 0x0000a000 reserve  --- ---  image    
+    # 0x00000000 --- ---  0x0001e000 0x017a2000 free     --- ---  unknown  
+    # 0x017c0000 --- ---  0x017c0000 0x000fe000 reserve  --- ---  private  
+    # 0x017c0000 --- ---  0x018be000 0x00002000 commit   rw- ---  private  
+    # 0x018c0000 --- ---  0x018c0000 0x00002000 commit   rw- -n-  private  
+
+    fields = line.split()
+    addr, size, state, prot, prot_, type = fields[3:]
+    addr = int(addr, 16) / 4096
+    size = int(size, 16) / 4096
+
+    drawit (draw, addr, size, state, prot, prot_, type)
+
+
+# Create grid.
+for col in xrange(slots):
+    draw.line ((0, col*slotwidth) + (im.size[0], col*slotwidth), fill="#666666")
+for col in xrange(3):
+    draw.rectangle ((0, (col+1)*(slots/4)*slotwidth - slotwidth/16)
+                    + (im.size[0], (col+1)*(slots/4)*slotwidth + slotwidth/16), fill="#666666")
+for row in xrange(31):
+    draw.line (((row+1)*(p_per_col/32)*pheight, 0) + (((row+1)*(p_per_col/32))*pheight, im.size[1]), fill="#666666")
+
+del draw
+
+# Compose documented image.
+fsize = (im.size[0] + 30 * slotwidth, im.size[1] + 6 * slotwidth)
+ulpaste = (28*slotwidth, 3*slotwidth)
+fim = Image.new ("RGB", fsize)
+draw = ImageDraw.Draw(fim)
+
+draw.rectangle ((0,0) + fim.size, fill="#ffffff")
+draw.rectangle ((ulpaste[0]-2, ulpaste[1]-2) + (ulpaste[0] + im.size[0] + 2, ulpaste[1] + im.size[1] + 2), fill="#000000")
+fim.paste (im, ulpaste)
+
+fs = int (slotwidth * 2 / 3)
+dpf = ImageFont.truetype ("datapro.ttf", int(fs * 1.5))
+draw.text((slotwidth/2,slotwidth), "Virtual Memory Map of Windows CE", fill="#000000", font=dpf)
+
+dpf = ImageFont.truetype ("datapro.ttf", fs)
+
+def getrow(i):
+    return 5 + ulpaste[1] + im.size[1] - slotwidth * (i + 1)
+
+for i in xrange(slots):
+    draw.text ((ulpaste[0] - 6 * slotwidth, getrow(i) ),
+               "0x%08x" % (i * 0x02000000), fill="#444444", font=dpf)
+
+for row in xrange(32):
+    txt=Image.new("L", (600,60))
+    d = ImageDraw.Draw(txt)
+    d.text ((0,0), "0x%08x" % (row * 0x00100000), fill=255, font=dpf)
+    del d
+    rtxt = txt.rotate (17.5, expand=1)
+    fim.paste (ImageOps.colorize(rtxt, "#ffffff", "#444444"),
+               (ulpaste[0] + (row*(p_per_col/32)*pheight), ulpaste[1] - 4*slotwidth), rtxt)
+    del txt
+    del rtxt
+
+#draw.text ((ulpaste[0] + 0x00011000 * p_per_col*pheight / (32*1024*1024), ulpaste[1] + 65*slotwidth),
+#           "Code/Data", fill="#000000", font=dpf)
+#draw.text ((ulpaste[0] + 0x018C0000 * p_per_col*pheight / (32*1024*1024), ulpaste[1] + 65*slotwidth),
+#           "Stack/Heap", fill="#000000", font=dpf)
+
+              
+def writerow(i, str):
+    draw.text ((10 * slotwidth, getrow(i) ), str, fill="#000000", font=dpf)
+
+writerow (0, "Slot  0: Active Process")
+writerow (1, "Slot  1: ROM Image")
+for i in xrange (31):
+    writerow (2 + i, "Slot %2i: Process %i" % (i + 2, i))
+for i in xrange (26):
+    writerow (33 + i, "Slot %2i: Shared Area" % (33 + i))
+writerow (59, "Slot 59: Driver Stacks")
+writerow (60, "Slot 60: Large DLLs")
+writerow (61, "Slot 61: Large DLLs")
+writerow (62, "Slot 62: Shared Heaps")
+writerow (63, "Slot 63: Resource DLLs")
+
+def writelegend(i, col, str):
+    draw.rectangle ((1 * slotwidth, getrow(63-i), 2 * slotwidth - 10, getrow(63-i - 1) - 10), fill=col)
+    draw.rectangle ((1 * slotwidth, getrow(63-i), 2 * slotwidth - 10, getrow(63-i - 1) - 10), outline="#444444")
+    draw.text ((2 * slotwidth, getrow(63-i) ), str, fill="#000000", font=dpf)
+
+writelegend(0, "#ffffff", "unused")
+writelegend(1, getcolor("free", 0, 0, ""), "free")
+writelegend(2, getcolor("reserve", 0, 0, "image"), "image")
+writelegend(3, getcolor("commit", 0, 0, "image"), "... committed")
+writelegend(4, getcolor("reserve", 0, 0, "private"), "private")
+writelegend(5, getcolor("commit", 0, 0, "private"), "... committed")
+writelegend(6, getcolor("reserve", 0, 0, "mapped"), "mapped")
+writelegend(7, getcolor("commit", 0, 0, "mapped"), "... committed")
+
+def writeextra(i, str):
+    draw.text ((1 * slotwidth, getrow(63-i) ), str, fill="#000000", font=dpf)
+writeextra(9, "1px = 4 KB")
+
+
+del draw 
+fim.save("output.png", "PNG")
diff --git a/inspection/virtual-query.c b/inspection/virtual-query.c
new file mode 100644 (file)
index 0000000..f47ddf5
--- /dev/null
@@ -0,0 +1,138 @@
+#include <stdio.h>
+#include <windows.h>
+
+void
+dump_mbi_header ()
+{
+  printf ("alc-base   alc-prot address    size       state    protect  type     \n");
+} 
+
+
+void
+dump_protect_flags (DWORD flags)
+{
+  DWORD pr = flags & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE
+                     | PAGE_EXECUTE_WRITECOPY | PAGE_READONLY
+                     | PAGE_READWRITE | PAGE_WRITECOPY);
+  DWORD pw = flags & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY
+                     | PAGE_READWRITE | PAGE_WRITECOPY);
+  DWORD pc = flags & (PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
+  DWORD px = flags & (PAGE_EXECUTE | PAGE_EXECUTE_READ
+                     | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
+  
+  printf ("%c%c%c %c%c%c  ",
+         pr ? 'r' : '-', pc ? 'c' : (pw ? 'w' : '-'), px ? 'x' : '-',
+         (flags & PAGE_GUARD) ? 'g' : '-',
+         (flags & PAGE_NOCACHE) ? 'n' : '-',
+#ifdef PAGE_PHYSICAL
+         (flags & PAGE_PHYSICAL) ? 'p' : 
+#endif
+         '-');
+}
+
+
+void
+dump_state (DWORD state)
+{
+  switch (state)
+    {
+    case MEM_COMMIT:
+      printf ("commit   ");
+      return;
+    case MEM_FREE:
+      printf ("free     ");
+      return;
+    case MEM_RESERVE:
+      printf ("reserve  ");
+      return;
+    default:
+      printf ("unknown  ");
+    }
+}
+
+
+void
+dump_type (DWORD mtype)
+{
+  switch (mtype)
+    {
+    case MEM_IMAGE:
+      printf ("image    ");
+      return;
+    case MEM_MAPPED:
+      printf ("mapped   ");
+      return;
+    case MEM_PRIVATE:
+      printf ("private  ");
+      return;
+    default:
+      printf ("unknown  ");
+    }
+}
+
+
+void
+dump_mbi (PMEMORY_BASIC_INFORMATION mbi)
+{
+  printf ("0x%08x ", mbi->AllocationBase);
+  dump_protect_flags (mbi->AllocationProtect);
+  printf ("0x%08x ", mbi->BaseAddress);
+  printf ("0x%08x ", mbi->RegionSize);
+  dump_state (mbi->State);
+  dump_protect_flags (mbi->Protect);
+  dump_type (mbi->Type);
+  printf ("\n");
+}
+
+
+int
+main (int argc, char* argv[])
+{
+  MEMORY_BASIC_INFORMATION mbi;
+  SYSTEM_INFO si;
+  void *addr;
+  int skipping = 0;
+
+  memset (&si, '\0', sizeof (si));
+  GetSystemInfo (&si);
+  dump_mbi_header ();
+
+  addr = 0;
+  do
+    {
+      DWORD res;
+      void *new_addr;
+
+      memset (&mbi, '\0', sizeof (mbi));
+      res = VirtualQuery (addr, &mbi, sizeof (mbi));
+      if (res == 0)
+       {
+         if (!skipping)
+           printf ("Skipping over %p...\n", addr);
+         skipping = 1;
+         new_addr = addr + si.dwPageSize;
+         if (new_addr < addr)
+           break;
+          addr = new_addr; 
+         continue;
+        }
+      if (res != sizeof (mbi))
+       {
+         printf ("Unexpected return size: %i (expected %i)\n",
+                 res, sizeof (mbi));
+       }
+      skipping = 0;
+      dump_mbi (&mbi);
+      /* Check for overflow.  */
+      new_addr = addr + mbi.RegionSize;
+      if (new_addr < addr)
+       break;
+      addr = new_addr;
+    }
+  while (1);
+
+  /* Give ssh time to flush buffers.  */
+  fflush (stdout);
+  Sleep (300);
+  return 0;
+}