I see you may not want this anymore, but had a little time to look at it today and I think the following works.

The dump of the returned message looks like this:
Code:
45 00 00 30 00 00 00 00 3C 01
AC D1 08 08 08 08 C0 A8 01 44
00 00 B8 F0 46 0F 01 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

I believe it is interpreted like this:

Version/IHL:    45           IPv4; length 5 * 32-bits = 20 bytes
ToS:            00           Type of Service (low priority)
Length:         00 30        Total length: 48 bytes (big endian)
Identification: 00 00
flags & offset: 00 00
TTL:            3C           Time To Live (max hops before discarding)
Protocol:       01
Checksum:       AC D1
Source IP:      08 08 08 08
Destination IP: 12 34 56 78  my address, which I've changed

Type:           00           ICMP_ECHOREPLY
Code:           00
Checksum:       B8 F0
Data:
  ID:           46 0F        this is the PID we sent
  Sequence:     01 00        the sequence number we sent
Payload:        00 00 00 00 00 00 00 00 00 00
                00 00 00 00 00 00 00 00 00 00  dummy 20-byte payload
Code:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <libgen.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <sys/socket.h>
 
#define BUFFER_SIZE   1024
#define PING_ADDRESS  "8.8.8.8"
 
bool pinger (char *address);
 
int main()
{
    // Check that user has root access.
    if (geteuid() != 0) {
        fprintf(stderr, "%s: must be run as root or with 'sudo'\n",
            program_invocation_short_name);
        exit(EXIT_FAILURE);
    }
 
    printf("Pinging %s was %ssuccessful.\n", PING_ADDRESS,
        pinger(PING_ADDRESS) ? "" : "un");

    return 0;
}

uint16_t checksum(void* addr, unsigned count)
{
    uint16_t* buf = addr;
    uint32_t sum = 0;

    for ( ; count > 1; count -= 2)
        sum += *buf++;

    //  Add left-over byte, if any
    if (count > 0)
        sum += *(unsigned char*)addr;

    //  Fold 32-bit sum to 16 bits
    while (sum >> 16)
        sum = (sum & 0xffff) + (sum >> 16);

    return (uint16_t)~sum;
}

bool do_error(const char *msg) {
    fprintf(stderr, "%s: %s error: %s (%s)",
        program_invocation_short_name, __func__, msg, strerror(errno));
    return false;
}

bool pinger (char *address)
{
    char buffer[BUFFER_SIZE] = {0};
    struct icmp icmp_hdr = {0};
    struct sockaddr_in dest_addr = {0};
    struct sockaddr_in from_addr = {0};
    socklen_t fromlen = {0};
    int sockfd = {0};
 
    icmp_hdr.icmp_type = ICMP_ECHO;
    icmp_hdr.icmp_code = 0;
    icmp_hdr.icmp_cksum = 0;
    icmp_hdr.icmp_id = getpid();
    icmp_hdr.icmp_seq = 1;

    printf("PID: %04X\n", (unsigned)icmp_hdr.icmp_id);

    icmp_hdr.icmp_cksum = checksum(&icmp_hdr, sizeof icmp_hdr);
 
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_addr.s_addr = inet_addr(address);
    memset(&dest_addr.sin_zero, 0, sizeof(dest_addr.sin_zero));
 
    // Create a raw socket.
    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
        return do_error("socket failed");

    // Send icmp request.
    if (sendto(sockfd, &icmp_hdr, sizeof(struct icmp), 0,
        (struct sockaddr *) &dest_addr, sizeof(struct sockaddr_in)) == -1)
        return do_error("sendto failed");

    // Receive icmp response.
    ssize_t len = recvfrom(sockfd, buffer, BUFFER_SIZE, 0,
                           (struct sockaddr *) &from_addr, &fromlen);
    if (len == -1)
        return do_error("recvfrom failed");
 
    for (ssize_t i = 0; i < len; ++i)
        printf("%02X ", (unsigned char)buffer[i]);
    printf("\n");

    return true;
}