forked from win32ss/supermium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rel32_finder_unittest.cc
171 lines (151 loc) · 5.59 KB
/
rel32_finder_unittest.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <memory>
#include <sstream>
#include <string>
#include "base/notreached.h"
#include "courgette/base_test_unittest.h"
#include "courgette/image_utils.h"
#include "courgette/rel32_finder_x64.h"
#include "courgette/rel32_finder_x86.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace courgette {
namespace {
// Helper class to load and execute a Rel32Finder test case.
class Rel32FinderTestCase {
public:
Rel32FinderTestCase(const std::string& test_data)
: text_start_rva_(0),
text_end_rva_(0),
relocs_start_rva_(0),
relocs_end_rva_(0),
image_end_rva_(0) {
LoadTestFromString(test_data);
}
void RunTestBasic(std::string name) {
ASSERT_FALSE(text_data_.empty());
finder_->Find(&text_data_[0], &text_data_[0] + text_data_.size(),
text_start_rva_, text_end_rva_, abs32_locations_);
std::vector<RVA> rel32_locations;
finder_->SwapRel32Locations(&rel32_locations);
EXPECT_EQ(expected_rel32_locations_, rel32_locations)
<< "From test case " << name << " (addresses are in hex)";
}
void CreateFinder(const std::string& processor_type) {
if (processor_type == "x64") {
finder_ = std::unique_ptr<Rel32Finder>(new Rel32FinderX64(
relocs_start_rva_, relocs_end_rva_, image_end_rva_));
} else if (processor_type == "x86") {
finder_ = std::unique_ptr<Rel32Finder>(
new Rel32FinderX86(relocs_start_rva_, relocs_end_rva_));
} else {
NOTREACHED();
}
}
private:
std::unique_ptr<Rel32Finder> finder_;
RVA text_start_rva_;
RVA text_end_rva_;
RVA relocs_start_rva_;
RVA relocs_end_rva_;
RVA image_end_rva_;
std::vector<uint8_t> text_data_;
std::vector<RVA> abs32_locations_;
std::vector<RVA> expected_rel32_locations_;
// Scans |iss| for the next non-empty line, after removing "#"-style comments
// and stripping trailing spaces. On success, returns true and writes the
// result to |line_out|. Otherwise returns false.
bool ReadNonEmptyLine(std::istringstream& iss, std::string* line_out) {
std::string line;
while (std::getline(iss, line)) {
// Trim comments and trailing spaces.
size_t end_pos = std::min(line.find("#"), line.length());
while (end_pos > 0 && line[end_pos - 1] == ' ')
--end_pos;
line.resize(end_pos);
if (!line.empty())
break;
}
if (line.empty())
return false;
line_out->swap(line);
return true;
}
// Scans |iss| for the next non-empty line, and reads (hex) uint32_t into |v|.
// Returns true iff successful.
bool ReadHexUInt32(std::istringstream& iss, uint32_t* v) {
std::string line;
if (!ReadNonEmptyLine(iss, &line))
return false;
return sscanf(line.c_str(), "%X", v) == 1;
}
// Initializes the test case by parsing the multi-line string |test_data|
// to extract Rel32Finder parameters, and read expected values.
void LoadTestFromString(const std::string& test_data) {
// The first lines (ignoring empty ones) specify RVA bounds.
std::istringstream iss(test_data);
std::string processor_type;
ASSERT_TRUE(ReadNonEmptyLine(iss, &processor_type));
ASSERT_TRUE(ReadHexUInt32(iss, &text_start_rva_));
ASSERT_TRUE(ReadHexUInt32(iss, &text_end_rva_));
ASSERT_TRUE(ReadHexUInt32(iss, &relocs_start_rva_));
ASSERT_TRUE(ReadHexUInt32(iss, &relocs_end_rva_));
ASSERT_TRUE(ReadHexUInt32(iss, &image_end_rva_));
std::string line;
// The Program section specifies instruction bytes. We require lines to be
// formatted in "DUMPBIN /DISASM" style, i.e.,
// "00401003: E8 00 00 00 00 call 00401008"
// ^ ^ ^ ^ ^ ^
// We extract up to 6 bytes per line. The remaining are ignored.
const int kBytesBegin = 12;
const int kBytesEnd = 17;
ReadNonEmptyLine(iss, &line);
ASSERT_EQ("Program:", line);
while (ReadNonEmptyLine(iss, &line) && line != "Abs32:") {
std::string toks = line.substr(kBytesBegin, kBytesEnd);
uint32_t vals[6];
int num_read = sscanf(toks.c_str(), "%X %X %X %X %X %X", &vals[0],
&vals[1], &vals[2], &vals[3], &vals[4], &vals[5]);
for (int i = 0; i < num_read; ++i)
text_data_.push_back(static_cast<uint8_t>(vals[i] & 0xFF));
}
ASSERT_FALSE(text_data_.empty());
// The Abs32 section specifies hex RVAs, one per line.
ASSERT_EQ("Abs32:", line);
while (ReadNonEmptyLine(iss, &line) && line != "Expected:") {
RVA abs32_location;
ASSERT_EQ(1, sscanf(line.c_str(), "%X", &abs32_location));
abs32_locations_.push_back(abs32_location);
}
// The Expected section specifies hex Rel32 RVAs, one per line.
ASSERT_EQ("Expected:", line);
while (ReadNonEmptyLine(iss, &line)) {
RVA rel32_location;
ASSERT_EQ(1, sscanf(line.c_str(), "%X", &rel32_location));
expected_rel32_locations_.push_back(rel32_location);
}
CreateFinder(processor_type);
}
};
class Rel32FinderTest : public BaseTest {
public:
void RunTest(const char* test_case_file) {
Rel32FinderTestCase test_case(FileContents(test_case_file));
test_case.RunTestBasic(test_case_file);
}
};
TEST_F(Rel32FinderTest, TestBasic) {
RunTest("rel32_x86_01.txt");
RunTest("rel32_x86_02.txt");
RunTest("rel32_x86_03.txt");
RunTest("rel32_x86_04.txt");
RunTest("rel32_x64_01.txt");
RunTest("rel32_x64_02.txt");
RunTest("rel32_x64_03.txt");
}
} // namespace
} // namespace courgette