Issues (195)

lib/Analyzer/EntropyAnalyzer.php (5 issues)

Labels
Severity
1
<?php
2
3
/**
4
 * @copyright Copyright (c) 2017 Matthias Held <[email protected]>
5
 * @author Matthias Held <[email protected]>
6
 * @license GNU AGPL version 3 or any later version
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License as
10
 * published by the Free Software Foundation, either version 3 of the
11
 * License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace OCA\RansomwareDetection\Analyzer;
23
24
use OCA\RansomwareDetection\AppInfo\Application;
25
use OCA\RansomwareDetection\Entropy\Entropy;
26
use OCP\Files\File;
0 ignored issues
show
The type OCP\Files\File was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
use OCP\Files\IRootFolder;
0 ignored issues
show
The type OCP\Files\IRootFolder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use OCP\Files\Storage\IStorage;
0 ignored issues
show
The type OCP\Files\Storage\IStorage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
use OCP\Files\NotFoundException;
0 ignored issues
show
The type OCP\Files\NotFoundException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
use OCP\ILogger;
0 ignored issues
show
The type OCP\ILogger was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
32
class EntropyAnalyzer
33
{
34
    /**
35
     * Entropy cut-off point between normal and compressed or encrypted files.
36
     *
37
     * @var float
38
     */
39
    const ENTROPY_CUT_OFF = 7.69;
40
41
    /**
42
     * Size of the data blocks in bytes.
43
     *
44
     * @var int
45
     */
46
    const BLOCK_SIZE = 256;
47
48
    /**
49
     * Standard deviation cut-off point between compressed and encrypted files.
50
     *
51
     * @var float
52
     */
53
    const SD_CUT_OFF = 0.06;
54
55
    /** @var ILogger */
56
    protected $logger;
57
58
    /** @var IRootFolder */
59
    protected $rootFolder;
60
61
    /** @var Entropy */
62
    protected $entropy;
63
64
    /** @var string */
65
    protected $userId;
66
67
    /**
68
     * @param ILogger     $logger
69
     * @param IRootFolder $rootFolder
70
     * @param Entropy     $entropy
71
     * @param int         $userId
72
     */
73
    public function __construct(
74
        ILogger $logger,
75
        IRootFolder $rootFolder,
76
        Entropy $entropy,
77
        $userId
78
    ) {
79
        $this->logger = $logger;
80
        $this->rootFolder = $rootFolder;
81
        $this->entropy = $entropy;
82
        $this->userId = $userId;
83
    }
84
85
    /**
86
     * Classifies a file using entropy measurements. It first calculates the
87
     * native entropy of the file to decide wether it's a normal file with
88
     * low entropy or a compressed or encrypted file with high entropy.
89
     *
90
     * If the file is identified as class B, it measures the
91
     * standard deviation of the entropy of all blocks with a size of 256 bytes.
92
     * To decide if the file is compressed or encrypted.
93
     *
94
     * The results classifies the file in the following three classes:
95
     * ENCRYPTED
96
     * COMPRESSED
97
     * NORMAL
98
     *
99
     * @param File     $node
100
     *
101
     * @return EntropyResult
102
     */
103
    public function analyze($node)
104
    {
105
        $entropy = $this->calculateEntropyOfFile($node);
106
        if ($entropy > self::ENTROPY_CUT_OFF) {
107
            $standardDeviation = $this->calculateStandardDeviationOfEntropy($node, self::BLOCK_SIZE);
108
            if ($standardDeviation > self::SD_CUT_OFF) {
109
                return new EntropyResult(EntropyResult::COMPRESSED, $entropy, $standardDeviation);
110
            }
111
112
            return new EntropyResult(EntropyResult::ENCRYPTED, $entropy, $standardDeviation);
113
        }
114
115
        return new EntropyResult(EntropyResult::NORMAL, $entropy, 0.0);
116
    }
117
118
    /**
119
     * Creates an array with the entropy of the data blocks.
120
     *
121
     * @param File   $node
122
     * @param int    $blockSize
123
     *
124
     * @return float
125
     */
126
    protected function calculateStandardDeviationOfEntropy($node, $blockSize)
127
    {
128
        $sum = 0.0;
129
        $standardDeviation = 0.0;
130
        $mean = 1;
131
        $step = 1;
132
133
        $handle = $node->fopen('r');
134
        if (!$handle) {
135
            $this->logger->debug('createEntropyArrayFromFile: Getting file handle failed.', array('app' => Application::APP_ID));
136
137
            return 0.0;
138
        }
139
140
        while (!feof($handle)) {
141
            $data = fread($handle, $blockSize);
142
            $step = $step + 1;
143
            if (strlen($data) === $blockSize) {
144
                $entropy = $this->entropy->calculateEntropy($data);
145
                $sum = $sum + pow($entropy, 2);
146
                $mean = $this->entropy->calculateMeanOfSeries($mean, $entropy, $step);
147
                $standardDeviation = $this->entropy->calculateStandardDeviationOfSeries($step, $sum, $mean);
148
            }
149
        }
150
        fclose($handle);
151
152
        return $standardDeviation;
153
    }
154
155
    /**
156
     * Calculates the entropy of a file.
157
     *
158
     * @param File $node
159
     *
160
     * @return float
161
     */
162
    protected function calculateEntropyOfFile($node)
163
    {
164
        $handle = $node->fopen('r');
165
        if (!$handle) {
166
            $this->logger->warning('calculateEntropyOfFile: Getting data failed.', array('app' => Application::APP_ID));
167
168
            return 0.0;
169
        }
170
171
        $entropy = 0.0;
172
        $total = 0;
173
174
        while (!feof($handle)) {
175
            $data = fread($handle, 1024);
176
            $total = $total + 1;
177
            if (strlen($data) === 1024) {
178
                $entropy = $entropy + $this->entropy->calculateEntropy($data);
179
            }
180
        }
181
        fclose($handle);
182
183
        $entropy = $entropy / $total;
184
185
        if ($entropy >= 0) {
186
            return $entropy;
187
        } else {
188
            return -$entropy;
189
        }
190
    }
191
}
192