Repository: zeropointdynamics/zelos (language: python)

Issue:
Plugin yarascan error _xref_cnts at YaraMatch
**Describe the bug**
I'm testing zelos with yarascan plugin that matches "Hello World" string in memory. I got this error during the test:
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/zelos/engine.py", line 593, in close
    closure()
  File "/usr/local/lib/python3.8/dist-packages/zelos/ext/plugins/yarascan/yarascan.py", line 307, in closure
    list(
  File "/usr/local/lib/python3.8/dist-packages/zelos/ext/plugins/yarascan/yarascan.py", line 378, in matches
    self._log(match.info(brief))
  File "/usr/local/lib/python3.8/dist-packages/zelos/ext/plugins/yarascan/yarascan.py", line 234, in info
    for i, s in enumerate(self.strings):
  File "/usr/local/lib/python3.8/dist-packages/zelos/ext/plugins/yarascan/yarascan.py", line 184, in strings
    self._yara_strings = [
  File "/usr/local/lib/python3.8/dist-packages/zelos/ext/plugins/yarascan/yarascan.py", line 185, in <listcomp>
    YaraString(self.region_address + s[0], s, self._xref_cnts[i])
AttributeError: 'YaraMatch' object has no attribute '_xref_cnts'
```

**To Reproduce**
Steps to reproduce the behavior:
1. Generate test file
```
#include <stdio.h>

int main() {
  printf("Hello world\n");
  return 0;
}
```
compile `gcc test.c -o run`
2. Generate yara rule
```
rule hello_world: zelos {
  strings:
    $1 = "Hello world"
  condition:
    $1
}
```
(test binary file with yara has no error)
3. Run command `zelos --yara_file basic_rule.yar /tmp/run`
4. See error

**Expected behavior**
I think program should show `Matched` message.

**Screenshots**
![Screenshot at 2020-11-10 07-55-32](https://user-images.githubusercontent.com/29118926/98614019-7aefdc00-22ef-11eb-81f8-d1e061f3c956.png)


**Additional context**
- I installed zelos via pip3 `sudo pip3 install zelos`.



Discussion / hints:

Fix patch:
diff --git a/src/zelos/ext/platforms/linux/syscalls/syscalls_socket.py b/src/zelos/ext/platforms/linux/syscalls/syscalls_socket.py
index a2637e9..03f196f 100644
--- a/src/zelos/ext/platforms/linux/syscalls/syscalls_socket.py
+++ b/src/zelos/ext/platforms/linux/syscalls/syscalls_socket.py
@@ -33,6 +33,7 @@
     SocketOptionsTcp,
     SocketProtocol,
 )
+from zelos.handles import SocketHandle
 from zelos.util import dumpstruct, str2struct, struct2str
 
 from . import syscall_structs as structs
@@ -205,6 +206,9 @@ def bind(k, p, args_addr):
     )
     _parse_sockaddr(p, args.addr, args.addrlen)
     socket_handle = k.z.handles.get(args.sockfd)
+    if not isinstance(socket_handle, SocketHandle):
+        k.logger.error("Invalid socket handle")
+        return -1
     sock = socket_handle.socket
     addr = bytes(p.memory.read(args.addr, args.addrlen))
     (host, port) = get_host_and_port(sock.domain, addr)
@@ -215,7 +219,7 @@ def bind(k, p, args_addr):
 def connect(k, p, args_addr):
     def print_addr(args):
         socket_handle = k.z.handles.get(args.sockfd)
-        if socket_handle is None:
+        if not isinstance(socket_handle, SocketHandle):
             return "{0}=0x{1:x}".format("addr", args.addr)
         sock = socket_handle.socket
         sockaddr = bytes(p.memory.read(args.addr, args.addrlen))
@@ -235,7 +239,7 @@ def print_addr(args):
     )
     # _parse_sockaddr(p, args.addr, args.addrlen)
     socket_handle = k.z.handles.get(args.sockfd)
-    if socket_handle is None:
+    if not isinstance(socket_handle, SocketHandle):
         k.logger.error("Invalid socket handle")
         return -1
     socket = socket_handle.socket
@@ -256,7 +260,7 @@ def listen(k, p, args_addr):
     )
 
     socket_handle = k.z.handles.get(args.sockfd)
-    if socket_handle is None:
+    if not isinstance(socket_handle, SocketHandle):
         k.logger.error("Invalid socket handle")
         return -1
     socket = socket_handle.socket
@@ -279,7 +283,7 @@ def accept(k, p, args_addr):
     )
 
     socket_handle = k.z.handles.get(args.sockfd)
-    if socket_handle is None:
+    if not isinstance(socket_handle, SocketHandle):
         k.logger.error("Invalid socket handle")
         return -1
     socket = socket_handle.socket
@@ -360,7 +364,7 @@ def print_buf(args):
 
     def print_dst(args):
         socket_handle = k.z.handles.get(args.sockfd)
-        if socket_handle is None:
+        if not isinstance(socket_handle, SocketHandle):
             return "{0}=0x{1:x}".format("dest_addr", args.dest_addr)
         sock = socket_handle.socket
         sockaddr = bytes(p.memory.read(args.dest_addr, args.addrlen))
@@ -382,7 +386,7 @@ def print_dst(args):
         arg_string_overrides={"buf": print_buf, "dest_addr": print_dst},
     )
     socket_handle = k.z.handles.get(args.sockfd)
-    if socket_handle is None:
+    if not isinstance(socket_handle, SocketHandle):
         k.logger.notice(f"Could not find socket {args.sockfd}")
         return -1
     sock = socket_handle.socket
@@ -401,7 +405,7 @@ def print_dst(args):
 
 def _send(k, p, sockfd, payload, flags=0):
     socket_handle = k.z.handles.get(sockfd)
-    if socket_handle is None:
+    if not isinstance(socket_handle, SocketHandle):
         k.logger.notice(f"Invalid socket fd 0x{sockfd:x}")
         return -1
     sock = socket_handle.socket
@@ -446,6 +450,9 @@ def recvfrom(k, p, args_addr):
         ],
     )
     socket_handle = k.z.handles.get(args.sockfd)
+    if not isinstance(socket_handle, SocketHandle):
+        k.logger.error("Invalid socket handle")
+        return -1
     sock = socket_handle.socket
 
     try:
@@ -467,7 +474,7 @@ def recvfrom(k, p, args_addr):
 
 def _recv(k, p, sockfd, buf, _len, flags=0):
     socket_handle = k.z.handles.get(sockfd)
-    if socket_handle is None:
+    if not isinstance(socket_handle, SocketHandle):
         return -1
     sock = socket_handle.socket
     has_data = sock.peek()
@@ -497,7 +504,7 @@ def setsockopt(k, p, args_addr):
     args = k._get_socketcall_args(p, "setsockopt", args_addr, arg_list)
 
     socket_handle = k.z.handles.get(args.sockfd)
-    if socket_handle is None:
+    if not isinstance(socket_handle, SocketHandle):
         k.logger.error("Invalid socket handle")
         return -1
     socket = socket_handle.socket
diff --git a/src/zelos/ext/plugins/yarascan/yarascan.py b/src/zelos/ext/plugins/yarascan/yarascan.py
index 519b6e9..25d7aac 100644
--- a/src/zelos/ext/plugins/yarascan/yarascan.py
+++ b/src/zelos/ext/plugins/yarascan/yarascan.py
@@ -139,9 +139,9 @@ def __init__(
         self._strings = yara_match.strings
         self._yara_strings = None
         self._count_xrefs = count_xrefs
+        self._xref_cnts = []
+        self._xref_total = 0
         if count_xrefs:
-            self._xref_cnts = []
-            self._xref_total = 0
             for s in self._strings:
                 cnt = self._do_xref_count(mr.start + s[0], process.memory)
                 self._xref_total += cnt
@@ -181,10 +181,19 @@ def xrefs(self) -> int:
     @property
     def strings(self) -> List[YaraString]:
         if self._yara_strings is None:
-            self._yara_strings = [
-                YaraString(self.region_address + s[0], s, self._xref_cnts[i])
-                for i, s in enumerate(self._strings)
-            ]
+            if self._count_xrefs:
+                self._yara_strings = [
+                    YaraString(
+                        self.region_address + s[0], s, xrefs=self._xref_cnts[i]
+                    )
+                    for i, s in enumerate(self._strings)
+                ]
+            else:
+                self._yara_strings = [
+                    YaraString(self.region_address + s[0], s)
+                    for i, s in enumerate(self._strings)
+                ]
+
         return self._yara_strings
 
     def yaml(self, brief: bool = False) -> str:


Test patch:
diff --git a/tests/plugins/test_yarascan.py b/tests/plugins/test_yarascan.py
index 0f192ed..93befab 100644
--- a/tests/plugins/test_yarascan.py
+++ b/tests/plugins/test_yarascan.py
@@ -198,6 +198,23 @@ def test_yarascan(self):
             for expected in expected_yaml:
                 self.assertTrue(expected in yaml)
 
+    def test_yarascan_no_xrefs_issue_129(self):
+        # Create temp directory to store yarascan output files
+        # Init `zelos` and request yara scanning post-emulation.
+        z = Zelos(
+            path.join(DATA_DIR, "static_elf_helloworld"),
+            trace_off=True,
+            yara_rule=r"/hello\sworld!/nocase",
+        )
+        z.start(timeout=10)
+
+        yara = z.plugins.yarascan
+        yara.compile(rules=["Hello World"], files=[], glob_string=None)
+        matches = list(yara.matches(pid=z.process.pid, xrefs=False))
+        # Check whether we are handling the strings method correctly,
+        # specifically when xrefs is false.
+        matches[0].strings
+
 
 if __name__ == "__main__":
     unittest.main()