One way to achieve this is to define your own matrix wrapper. The wrapper approach has the advantage that you can overload as many built-in functions as you like without impacting any other functionality.
Let's start by defining a wrapper called myMatrix
that displays itself using MatrixForm
:
Format[myMatrix[m_]] ^:= MatrixForm[m]
Next, we'll overload the Times
operator when it acts on myMatrix
:
myMatrix[m1_] myMatrix[m2_] ^:= myMatrix[Inner[And, m1, m2, Or]]
Note that both definitions use ^:=
to attach the rules to myMatrix
as up-values. This is crucial to ensure that the regular built-in definitions are otherwise unaffected.
Armed with these definitions, we can now achieve the desired goal. To demonstrate, let's define two matrices:
m1 = myMatrix[Array[Subscript[a, Row[{##}]]&, {2, 2}]]

m2 = myMatrix[Array[Subscript[b, Row[{##}]]&, {2, 2}]]

The requested "explicitly viewable form" can now be generated thus:
Row[{m1, m2}] == m1 m2

... or, if you prefer to reference Times
explicitly on the left-hand side of the equation:
With[{m1 = m1, m2 = m2}, HoldForm[m1 m2] == m1 m2]

Next, we'll assign random boolean values to each of the matrix elements:
Evaluate[First /@ {m1, m2}] = RandomInteger[1, {2, 2, 2}];
... and then generate the explicitly viewable form once again with the assignments in place:
With[{m1 = m1, m2 = m2}, HoldForm[m1 m2] == m1 m2]
