1*a03ca8b9SKrzysztof Kosiński // Copyright 2017 The Chromium Authors. All rights reserved.
2*a03ca8b9SKrzysztof Kosiński // Use of this source code is governed by a BSD-style license that can be
3*a03ca8b9SKrzysztof Kosiński // found in the LICENSE file.
4*a03ca8b9SKrzysztof Kosiński
5*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/element_detection.h"
6*a03ca8b9SKrzysztof Kosiński
7*a03ca8b9SKrzysztof Kosiński #include <map>
8*a03ca8b9SKrzysztof Kosiński #include <vector>
9*a03ca8b9SKrzysztof Kosiński
10*a03ca8b9SKrzysztof Kosiński #include "base/bind.h"
11*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/buffer_view.h"
12*a03ca8b9SKrzysztof Kosiński #include "testing/gtest/include/gtest/gtest.h"
13*a03ca8b9SKrzysztof Kosiński
14*a03ca8b9SKrzysztof Kosiński namespace zucchini {
15*a03ca8b9SKrzysztof Kosiński namespace {
16*a03ca8b9SKrzysztof Kosiński // This test uses a mock archive format where regions are determined by their
17*a03ca8b9SKrzysztof Kosiński // consecutive byte values rather than parsing real executables.
18*a03ca8b9SKrzysztof Kosiński //
19*a03ca8b9SKrzysztof Kosiński // 0 - Padding or raw data (not mapped to an executable).
20*a03ca8b9SKrzysztof Kosiński // 1 - A Win32x86 executable.
21*a03ca8b9SKrzysztof Kosiński // 2 - A Win32x64 executable.
22*a03ca8b9SKrzysztof Kosiński //
23*a03ca8b9SKrzysztof Kosiński // So an example archive file of;
24*a03ca8b9SKrzysztof Kosiński // 0 1 1 1 0 1 1 0 0 2 2 2 2
25*a03ca8b9SKrzysztof Kosiński // contains (in order left to right):
26*a03ca8b9SKrzysztof Kosiński // - One padding byte
27*a03ca8b9SKrzysztof Kosiński // - Three byte Win32x86 executable
28*a03ca8b9SKrzysztof Kosiński // - One padding byte
29*a03ca8b9SKrzysztof Kosiński // - Two byte Win32x86 executable
30*a03ca8b9SKrzysztof Kosiński // - Two padding bytes
31*a03ca8b9SKrzysztof Kosiński // - Four byte Win32x64 executable
32*a03ca8b9SKrzysztof Kosiński
33*a03ca8b9SKrzysztof Kosiński class ElementDetectionTest : public ::testing::Test {
34*a03ca8b9SKrzysztof Kosiński protected:
35*a03ca8b9SKrzysztof Kosiński using ElementVector = std::vector<Element>;
36*a03ca8b9SKrzysztof Kosiński using ExeTypeMap = std::map<uint8_t, ExecutableType>;
37*a03ca8b9SKrzysztof Kosiński
ElementDetectionTest()38*a03ca8b9SKrzysztof Kosiński ElementDetectionTest()
39*a03ca8b9SKrzysztof Kosiński : exe_map_({{1, kExeTypeWin32X86}, {2, kExeTypeWin32X64}}) {}
40*a03ca8b9SKrzysztof Kosiński
TestElementFinder(std::vector<uint8_t> buffer)41*a03ca8b9SKrzysztof Kosiński ElementVector TestElementFinder(std::vector<uint8_t> buffer) {
42*a03ca8b9SKrzysztof Kosiński ConstBufferView image(buffer.data(), buffer.size());
43*a03ca8b9SKrzysztof Kosiński
44*a03ca8b9SKrzysztof Kosiński ElementFinder finder(
45*a03ca8b9SKrzysztof Kosiński image,
46*a03ca8b9SKrzysztof Kosiński base::BindRepeating(
47*a03ca8b9SKrzysztof Kosiński [](ExeTypeMap exe_map, ConstBufferView image,
48*a03ca8b9SKrzysztof Kosiński ConstBufferView region) -> std::optional<Element> {
49*a03ca8b9SKrzysztof Kosiński EXPECT_GE(region.begin(), image.begin());
50*a03ca8b9SKrzysztof Kosiński EXPECT_LE(region.end(), image.end());
51*a03ca8b9SKrzysztof Kosiński EXPECT_GE(region.size(), 0U);
52*a03ca8b9SKrzysztof Kosiński
53*a03ca8b9SKrzysztof Kosiński if (region[0] != 0) {
54*a03ca8b9SKrzysztof Kosiński offset_t length = 1;
55*a03ca8b9SKrzysztof Kosiński while (length < region.size() && region[length] == region[0])
56*a03ca8b9SKrzysztof Kosiński ++length;
57*a03ca8b9SKrzysztof Kosiński return Element{{0, length}, exe_map[region[0]]};
58*a03ca8b9SKrzysztof Kosiński }
59*a03ca8b9SKrzysztof Kosiński return std::nullopt;
60*a03ca8b9SKrzysztof Kosiński },
61*a03ca8b9SKrzysztof Kosiński exe_map_, image));
62*a03ca8b9SKrzysztof Kosiński std::vector<Element> elements;
63*a03ca8b9SKrzysztof Kosiński for (auto element = finder.GetNext(); element; element = finder.GetNext()) {
64*a03ca8b9SKrzysztof Kosiński elements.push_back(*element);
65*a03ca8b9SKrzysztof Kosiński }
66*a03ca8b9SKrzysztof Kosiński return elements;
67*a03ca8b9SKrzysztof Kosiński }
68*a03ca8b9SKrzysztof Kosiński
69*a03ca8b9SKrzysztof Kosiński // Translation map from mock archive bytes to actual types used in Zucchini.
70*a03ca8b9SKrzysztof Kosiński ExeTypeMap exe_map_;
71*a03ca8b9SKrzysztof Kosiński };
72*a03ca8b9SKrzysztof Kosiński
TEST_F(ElementDetectionTest,ElementFinderEmpty)73*a03ca8b9SKrzysztof Kosiński TEST_F(ElementDetectionTest, ElementFinderEmpty) {
74*a03ca8b9SKrzysztof Kosiński std::vector<uint8_t> buffer(10, 0);
75*a03ca8b9SKrzysztof Kosiński ElementFinder finder(
76*a03ca8b9SKrzysztof Kosiński ConstBufferView(buffer.data(), buffer.size()),
77*a03ca8b9SKrzysztof Kosiński base::BindRepeating([](ConstBufferView image) -> std::optional<Element> {
78*a03ca8b9SKrzysztof Kosiński return std::nullopt;
79*a03ca8b9SKrzysztof Kosiński }));
80*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(std::nullopt, finder.GetNext());
81*a03ca8b9SKrzysztof Kosiński }
82*a03ca8b9SKrzysztof Kosiński
TEST_F(ElementDetectionTest,ElementFinder)83*a03ca8b9SKrzysztof Kosiński TEST_F(ElementDetectionTest, ElementFinder) {
84*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(ElementVector(), TestElementFinder({}));
85*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(ElementVector(), TestElementFinder({0, 0}));
86*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(ElementVector({{{0, 2}, kExeTypeWin32X86}}),
87*a03ca8b9SKrzysztof Kosiński TestElementFinder({1, 1}));
88*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(
89*a03ca8b9SKrzysztof Kosiński ElementVector({{{0, 2}, kExeTypeWin32X86}, {{2, 2}, kExeTypeWin32X64}}),
90*a03ca8b9SKrzysztof Kosiński TestElementFinder({1, 1, 2, 2}));
91*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(ElementVector({{{1, 2}, kExeTypeWin32X86}}),
92*a03ca8b9SKrzysztof Kosiński TestElementFinder({0, 1, 1, 0}));
93*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(
94*a03ca8b9SKrzysztof Kosiński ElementVector({{{1, 2}, kExeTypeWin32X86}, {{3, 3}, kExeTypeWin32X64}}),
95*a03ca8b9SKrzysztof Kosiński TestElementFinder({0, 1, 1, 2, 2, 2}));
96*a03ca8b9SKrzysztof Kosiński EXPECT_EQ(
97*a03ca8b9SKrzysztof Kosiński ElementVector({{{1, 2}, kExeTypeWin32X86}, {{4, 3}, kExeTypeWin32X64}}),
98*a03ca8b9SKrzysztof Kosiński TestElementFinder({0, 1, 1, 0, 2, 2, 2}));
99*a03ca8b9SKrzysztof Kosiński }
100*a03ca8b9SKrzysztof Kosiński
101*a03ca8b9SKrzysztof Kosiński } // namespace
102*a03ca8b9SKrzysztof Kosiński } // namespace zucchini
103