Normal mapping is really just about sampling normal data from a texture instead of using a mesh's own interpolated per-vertex normals in your lighting calculations.
The normal data is used to simulate how incoming light bounces of the rendered surface. Normally ( no pun intended

) you calculate the dot product of the surface normal (which in the case of normal mapping is sampled from a texture, as previously mentioned) and the direction from which the light is hitting the surface and then simply use that as a factor to multiply your final colour with (if you have a more complex calculation for determining your final pixel colour you can just multiply this factor with the attenuation / intensity value, before any specular highlights etc. are added).
One funky thing that I had lots of trouble finding out is that normal maps are usually stored in tangent space rather than object space. You can essentially think of this as them being relative to the surface normal. This is a good thing since the normals will still be correct even if you deform the mesh (such as through animation), however it isn't quite so simple. Basically your mesh will have to have per-vertex tangents and bitangents (commonly, albeit wrongly, referred to as
binormals) in addition to (per-vertex) normals. This is needed to build a 3x3 matrix that can be used to transform between mesh and tangent space for using your normal map's values in your lighting calculations.
This code snippet shows how I'm currently doing the normal transformation in my Ziggurat engine:
float3 n = normalize(IN.normal);
float3 t = normalize(IN.tangent);
float3 b = normalize(IN.binormal);
// Build matrix to transform to tangent space
float3x3 matTangent = {t, b, n};
matTangent = transpose(matTangent);
float3 normal = NormalMap.Sample(DiffuseSampler, IN.texcoord).xyz * 2 - 1; // Transform from [0..1] to [-1 .. +1] range
normal = normalize(normal.x * matTangent[0] + normal.y * matTangent[1] + normal.z * matTangent[2]);