Writing external for linux using C.
by mrpascal from LinuxQuestions.org on (#6FF6B)
Hello, I've found my self in recent months doing a lot of work with ptrace and debugging of user-space applications I've managed to make my own library for function hooking which even works on stripped binaries. POC:https://youtu.be/IdL_ZOnJOTY
Latest issue I'm facing is following:
We have program target.c
#include <stdio.h>
int main(void) {
int i = 0;
while (1) {
i++;
printf("%d\n", i);
}
return 0;
}
compiler flags: gcc target.c -o target
objdump -D target gives us
0000000000001139 <main>:
1139: 55 push %rbp
113a: 48 89 e5 mov %rsp,%rbp
113d: 48 83 ec 10 sub $0x10,%rsp
1141: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
1148: 83 45 fc 01 addl $0x1,-0x4(%rbp)
114c: 8b 45 fc mov -0x4(%rbp),%eax
114f: 89 c6 mov %eax,%esi
1151: 48 8d 05 ac 0e 00 00 lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: 48 89 c7 mov %rax,%rdi
115b: b8 00 00 00 00 mov $0x0,%eax
1160: e8 cb fe ff ff call 1030 <printf@plt>
1165: eb e1 jmp 1148 <main+0xf>
What we want to do is write an external called main.c that is able to attach to running process of target and get current value of variable i.
Methodology I use:
First of all I used readelf -l target to find base of entry point in my case 0x1040 but since this binary is DYN I had to calculate acutal entry point so I made function in my code that does 0x1040 (entry point from readelf + first address from /proc/PID/maps to get actual entry point) after that I looked into objdump and saw addl $0x1, -0x4(%rbp) so this literally means from my understanding add 1 to -4 bytes from base pointer (4 bytes because we declared is int) which means i is stored 4 bytes under the base pointer (rbp) I used gdb to prove this theory by setting breakpoint on one instruction after addl and looking into value of rbp and it seems to be correct but when I tried to implement C codebase to do this automatically I faced multiple weird behaviours I'm trying to find solution for.
First of all sometimes RBP is set to correct address while sometimes it is always 0. In case it is set to some address my program tells me that i is zero and target program crashes with illegal hardware instruction (core dumped). When program claims that RBP is zero target continues running after my main finishes execution and i is still 0.
main.c source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <proc/readproc.h>
#define targetPIDESS_NAME "target"
#define BASE_ENTRY 0x1040
#define INST 11
unsigned long long get_entry(pid_t pid)
{
FILE*fp;
charpath[256], caddr[32], c;
unsigned long longaddr;
memset( path, 0, sizeof(path) );
memset( caddr, 0, sizeof(caddr) );
addr = 0;
snprintf(path,
sizeof(path),
"/proc/%ld/maps",
pid);
fp = fopen(path, "rb");
for (int i = 0; i < sizeof(caddr); i++)
{
c = fgetc(fp);
if (c == '-')
break;
caddr[i] = c;
}
addr = strtoull(caddr, NULL, 16);
return addr;
}
int main() {
pid_t targetPID;
ints;
unsigned long longr;
struct user_regs_structregs;
PROCTAB* proc = openproc(PROC_FILLSTAT);
if (proc == NULL) {
perror("Failed to open /proc");
return 1;
}
proc_t proc_info;
memset(&proc_info, 0, sizeof(proc_info));
while (readproc(proc, &proc_info) != NULL) {
if (strcmp(proc_info.cmd, targetPIDESS_NAME) == 0) {
targetPID = proc_info.tid;
}
}
if (ptrace(PTRACE_ATTACH, targetPID, NULL, NULL) == -1) {
perror("ptrace");
return 1;
}
r=BASE_ENTRY;
r+=get_entry(targetPID);
waitpid(targetPID, NULL, 0);
ptrace(PTRACE_GETREGS, targetPID, NULL, ®s);
regs.rip = r;
ptrace(PTRACE_SETREGS, targetPID, NULL, ®s);
printf("RIP SET TO:%lx\n", regs.rip);
for (int i = 0; i < INST; i++)
{
ptrace(PTRACE_GETREGS,
targetPID,
NULL,
®s);
printf("RIP is now at : %#lx\n", regs.rip);
printf("RBP is now at : %#lx\n", regs.rbp);
ptrace(PTRACE_SINGLESTEP,
targetPID,
NULL,
NULL);
waitpid(targetPID, &s, 0);
}
ptrace(PTRACE_GETREGS,
targetPID,
NULL,
®s);
long value;
printf("RBP:%lx\n", regs.rbp);
ptrace(PTRACE_PEEKDATA, targetPID, regs.rbp - sizeof(int), &value);
printf("Value of i: %d\n", (int)value);
ptrace(PTRACE_DETACH, targetPID, NULL, NULL);
closeproc(proc);
return 0;
}
OS/Hardware info:
CPU: Threadripper
Distro: Arch linux
Architecture: x86_64
Kernel: 6.5.4-arch2-1
Thank you in advanced I hope I provided clear description of the issue I'm trying to resolve and if you need any further information on what I'm trying to do lmk.
Latest issue I'm facing is following:
We have program target.c
#include <stdio.h>
int main(void) {
int i = 0;
while (1) {
i++;
printf("%d\n", i);
}
return 0;
}
compiler flags: gcc target.c -o target
objdump -D target gives us
0000000000001139 <main>:
1139: 55 push %rbp
113a: 48 89 e5 mov %rsp,%rbp
113d: 48 83 ec 10 sub $0x10,%rsp
1141: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
1148: 83 45 fc 01 addl $0x1,-0x4(%rbp)
114c: 8b 45 fc mov -0x4(%rbp),%eax
114f: 89 c6 mov %eax,%esi
1151: 48 8d 05 ac 0e 00 00 lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: 48 89 c7 mov %rax,%rdi
115b: b8 00 00 00 00 mov $0x0,%eax
1160: e8 cb fe ff ff call 1030 <printf@plt>
1165: eb e1 jmp 1148 <main+0xf>
What we want to do is write an external called main.c that is able to attach to running process of target and get current value of variable i.
Methodology I use:
First of all I used readelf -l target to find base of entry point in my case 0x1040 but since this binary is DYN I had to calculate acutal entry point so I made function in my code that does 0x1040 (entry point from readelf + first address from /proc/PID/maps to get actual entry point) after that I looked into objdump and saw addl $0x1, -0x4(%rbp) so this literally means from my understanding add 1 to -4 bytes from base pointer (4 bytes because we declared is int) which means i is stored 4 bytes under the base pointer (rbp) I used gdb to prove this theory by setting breakpoint on one instruction after addl and looking into value of rbp and it seems to be correct but when I tried to implement C codebase to do this automatically I faced multiple weird behaviours I'm trying to find solution for.
First of all sometimes RBP is set to correct address while sometimes it is always 0. In case it is set to some address my program tells me that i is zero and target program crashes with illegal hardware instruction (core dumped). When program claims that RBP is zero target continues running after my main finishes execution and i is still 0.
main.c source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <proc/readproc.h>
#define targetPIDESS_NAME "target"
#define BASE_ENTRY 0x1040
#define INST 11
unsigned long long get_entry(pid_t pid)
{
FILE*fp;
charpath[256], caddr[32], c;
unsigned long longaddr;
memset( path, 0, sizeof(path) );
memset( caddr, 0, sizeof(caddr) );
addr = 0;
snprintf(path,
sizeof(path),
"/proc/%ld/maps",
pid);
fp = fopen(path, "rb");
for (int i = 0; i < sizeof(caddr); i++)
{
c = fgetc(fp);
if (c == '-')
break;
caddr[i] = c;
}
addr = strtoull(caddr, NULL, 16);
return addr;
}
int main() {
pid_t targetPID;
ints;
unsigned long longr;
struct user_regs_structregs;
PROCTAB* proc = openproc(PROC_FILLSTAT);
if (proc == NULL) {
perror("Failed to open /proc");
return 1;
}
proc_t proc_info;
memset(&proc_info, 0, sizeof(proc_info));
while (readproc(proc, &proc_info) != NULL) {
if (strcmp(proc_info.cmd, targetPIDESS_NAME) == 0) {
targetPID = proc_info.tid;
}
}
if (ptrace(PTRACE_ATTACH, targetPID, NULL, NULL) == -1) {
perror("ptrace");
return 1;
}
r=BASE_ENTRY;
r+=get_entry(targetPID);
waitpid(targetPID, NULL, 0);
ptrace(PTRACE_GETREGS, targetPID, NULL, ®s);
regs.rip = r;
ptrace(PTRACE_SETREGS, targetPID, NULL, ®s);
printf("RIP SET TO:%lx\n", regs.rip);
for (int i = 0; i < INST; i++)
{
ptrace(PTRACE_GETREGS,
targetPID,
NULL,
®s);
printf("RIP is now at : %#lx\n", regs.rip);
printf("RBP is now at : %#lx\n", regs.rbp);
ptrace(PTRACE_SINGLESTEP,
targetPID,
NULL,
NULL);
waitpid(targetPID, &s, 0);
}
ptrace(PTRACE_GETREGS,
targetPID,
NULL,
®s);
long value;
printf("RBP:%lx\n", regs.rbp);
ptrace(PTRACE_PEEKDATA, targetPID, regs.rbp - sizeof(int), &value);
printf("Value of i: %d\n", (int)value);
ptrace(PTRACE_DETACH, targetPID, NULL, NULL);
closeproc(proc);
return 0;
}
OS/Hardware info:
CPU: Threadripper
Distro: Arch linux
Architecture: x86_64
Kernel: 6.5.4-arch2-1
Thank you in advanced I hope I provided clear description of the issue I'm trying to resolve and if you need any further information on what I'm trying to do lmk.