Debugger features such as breakpoints go well beyond the normal ways in which programs interact. How can one process force another process to pause at a particular line of code? We can bet that the debugger is using some special features of the CPU and/or operating system.
As I learned about implementing breakpoints, I was surprised to find that these interfaces, while arcane, are not actually very complex. In this article we'll implement a minimal but working breakpoints library in about 75 lines of C code. All of the code is shown here, and is also available on GitHub.
True, most of us will never write a debugger. But breakpoints are useful in many other tools: profilers, compatibility layers, security scanners, etc. If your program has to interact deeply with another process, without modifying its source code, breakpoints might just do the trick. And I think there's value in knowing what your debugger is really doing.
Our code will compile with gcc and will run on Linux machines with 32- or 64-bit x86 processors. I'll assume you're familiar with the basics of UNIX programming in C. We'll make heavy use of the ptrace
system call, which I'll try to explain as we go. If you're looking for other reading about ptrace
, I highly recommend this article and of course the manpage.
The API
Our little breakpoint library is named breakfast
. Here's breakfast.h
:
#ifndef _BREAKFAST_H
#define _BREAKFAST_H
#include <sys/types.h> /* for pid_t */
typedef void *target_addr_t;
struct breakpoint;
void breakfast_attach(pid_t pid);
target_addr_t breakfast_getip(pid_t pid);
struct breakpoint *breakfast_break(pid_t pid, target_addr_t addr);
int breakfast_run(pid_t pid, struct breakpoint *bp);
#endif
Before we dive into the implementation, we'll describe the API our library provides. breakfast
is used by one process (the tracing process) to place breakpoints inside the code of another process (the target process). To establish this relationship, the tracing process calls breakfast_attach
with the target's process ID. This also suspends execution of the target.
We use breakfast_getip
to read the target's instruction pointer, which holds the address of the next instruction it will execute. Note that this is a pointer to machine code within the target's address space. Dereferencing the pointer within our tracing process would make little sense. The type alias target_addr_t
reminds us of this fact.
We use breakfast_break
to create a breakpoint at a specified address in the target's machine code. This function returns a pointer to a breakpoint
structure, which has unspecified contents.
Calling breakfast_run
lets the target run until it hits a breakpoint or it exits; breakfast_run
returns 1
or 0
respectively. On the first invocation, we'll pass NULL
for the argument bp
. On subsequent calls, we're resuming from a breakpoint, and we must pass the corresponding breakpoint
struct pointer. The breakfast_getip
function will tell us the breakpoint's address, but it's our responsibility to map this to the correct breakpoint
structure.
The trap instruction
We will implement a breakpoint by inserting a new CPU instruction into the target's memory at the breakpoint address. This instruction should suspend execution of the target and give control back to the OS.
There are many ways to return control to the OS, but we'd like to minimize disruption to the code we're hot-patching. x86 provides an instruction for exactly this purpose. It's named int3
and is encoded as the single byte 0xCC
. When the CPU executes int3
, it will stop what it's doing and jump to the interrupt 3 service routine, which is a piece of code chosen by the OS. On Linux, this routine will send the signal SIGTRAP
to the current process.
Since we're placing int3
in the target's code, the target will receive a SIGTRAP
. Under normal circumstances this would invoke the target's SIGTRAP
handler, which usually kills the process. Instead we'd like the tracing process to intercept this signal and interpret it as the target hitting a breakpoint. We'll accomplish this through the magic of ptrace
.
Let's start off breakfast.c
with some includes:
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <stdlib.h>
#include "breakfast.h"
Then we'll define our trap instruction:
#if defined(__i386)
#define REGISTER_IP EIP
#define TRAP_LEN 1
#define TRAP_INST 0xCC
#define TRAP_MASK 0xFFFFFF00
#elif defined(__x86_64)
#define REGISTER_IP RIP
#define TRAP_LEN 1
#define TRAP_INST 0xCC
#define TRAP_MASK 0xFFFFFFFFFFFFFF00
#else
#error Unsupported architecture
#endif
We use some GCC pre-defined macros to discover the machine architecture, and we define a few constants accordingly. REGISTER_IP
expands to a constant from sys/reg.h
which identifies the machine register holding the instruction pointer.
The trap instruction is stored as an integer TRAP_INST
and its length in bytes is TRAP_LEN
. These are the same on 32- and 64-bit x86. The trap instruction is a single byte, but we'll read and write the target's memory in increments of one machine word, meaning 32 or 64 bits. So we'll read 4 or 8 bytes of machine code, clear out the first byte with TRAP_MASK
, and substitute 0xCC
. Since x86 is a little-endian architecture, the first byte in memory is the least-significant byte of an integer machine word.
Invoking ptrace
All of the various ptrace
requests are made through a single system call named ptrace
. The first argument specifies the type of request and the meaning of any remaining arguments. The second argument is almost always the process ID of the target.
Before we can mess with the target process, we need to attach to it:
void breakfast_attach(pid_t pid) {
int status;
ptrace(PTRACE_ATTACH, pid);
waitpid(pid, &status, 0);
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXIT);
}
The PTRACE_ATTACH
request will stop the target with SIGSTOP
. We wait for the target to receive this signal. We also request to receive a notification when the target exits, which I'll explain below.
Note that ptrace
and waitpid
could fail in various ways. In a real application you'd want to check the return value and/or errno
. For brevity I've omitted those checks in this article.
We use another ptrace
request to get the value of the target's instruction pointer:
target_addr_t breakfast_getip(pid_t pid) {
long v = ptrace(PTRACE_PEEKUSER, pid, sizeof(long)*REGISTER_IP);
return (target_addr_t) (v - TRAP_LEN);
}
Since the target process is suspended, it's not running on any CPU, and its instruction pointer is not stored in a real CPU register. Instead, it's saved to a "user area" in kernel memory. We use the PTRACE_PEEKUSER
request to read a machine word from this area at a specified byte offset. The constants in sys/regs.h
give the order in which registers appear, so we simply multiply by sizeof(long)
.
After we hit a breakpoint, the saved IP points to the instruction after the trap instruction. When we resume execution, we'll go back and execute the original instruction that we overwrote with the trap. So we subtract TRAP_LEN
to give the true address of the next instruction.
Creating breakpoints
We need to remember two things about a breakpoint: the address of the code we replaced, and the code which originally lived there.
struct breakpoint {
target_addr_t addr;
long orig_code;
};
To enable a breakpoint, we save the original code and insert our trap instruction:
static void enable(pid_t pid, struct breakpoint *bp) {
long orig = ptrace(PTRACE_PEEKTEXT, pid, bp->addr);
ptrace(PTRACE_POKETEXT, pid, bp->addr, (orig & TRAP_MASK) | TRAP_INST);
bp->orig_code = orig;
}
The PTRACE_PEEKTEXT
request reads a machine word from the target's code address space, which is named "text" for historical reasons. PTRACE_POKETEXT
writes to that space. On x86 Linux there's actually no difference between code and data spaces, so PTRACE_PEEKDATA
and PTRACE_POKEDATA
would work just as well.
Creating a breakpoint is straightforward:
struct breakpoint *breakfast_break(pid_t pid, target_addr_t addr) {
struct breakpoint *bp = malloc(sizeof(*bp));
bp->addr = addr;
enable(pid, bp);
return bp;
}
To disable a breakpoint we just write back the saved word:
static void disable(pid_t pid, struct breakpoint *bp) {
ptrace(PTRACE_POKETEXT, pid, bp->addr, bp->orig_code);
}
Running and stepping
Once we attach to the target, its execution stops. Here's how to resume it:
static int run(pid_t pid, int cmd); /* defined momentarily */
int breakfast_run(pid_t pid, struct breakpoint *bp) {
if (bp) {
ptrace(PTRACE_POKEUSER, pid, sizeof(long)*REGISTER_IP, bp->addr);
disable(pid, bp);
if (!run(pid, PTRACE_SINGLESTEP))
return 0;
enable(pid, bp);
}
return run(pid, PTRACE_CONT);
}
We ask ptrace
to continue execution — but if we're resuming from a breakpoint, we have to do some cleanup first. We rewind the instruction pointer so that the next instruction to execute is at the breakpoint. Then we disable the breakpoint and make the target execute just one instruction. Once we're past the breakpoint, we can re-enable it for next time. run
returns 0
if the target exits, which theoretically could happen during our single-step.
The last piece of the puzzle is run
:
static int run(pid_t pid, int cmd) {
int status, last_sig = 0, event;
while (1) {
ptrace(cmd, pid, 0, last_sig);
waitpid(pid, &status, 0);
if (WIFEXITED(status))
return 0;
if (WIFSTOPPED(status)) {
last_sig = WSTOPSIG(status);
if (last_sig == SIGTRAP) {
event = (status >> 16) & 0xffff;
return (event == PTRACE_EVENT_EXIT) ? 0 : 1;
}
}
}
}
cmd
is either PTRACE_CONT
or PTRACE_SINGLESTEP
. In the latter case, the OS will set a control bit to make the CPU raise interrupt 3 after one instruction has completed.
ptrace
will let the target run until it exits or receives a signal. We use the wait
macros to see what happened. If the target exited, we simply return 0
. If it received a signal, we check which one. Anything other than SIGTRAP
should be delivered through to the target, which we accomplish by passing the signal number to the next ptrace
call.
In the case of SIGTRAP
, we check bits 16-31 of status
for the value PTRACE_EVENT_EXIT
, which indicates that the target is about to exit. Recall that we requested this notification by setting the option PTRACE_O_TRACEEXIT
. You'd think (at least, I did) that checking WIFEXITED
should be enough. But I ran into a problem where delivering a fatal signal to the target would make the tracing process loop forever. I fixed it by adding this extra check. I'm certainly no ptrace
expert and I'd be interested to hear more about what's going on here.
Testing it
That's all for breakfast.c
! Now let's write a small test.c
.
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include "breakfast.h"
void child();
void parent(pid_t);
int main() {
pid_t pid;
if ((pid = fork()) == 0)
child();
else
parent(pid);
return 0;
}
We fork two separate processes, a parent and a child. The child will do some computation we hope to observe. Let's make it compute the famous factorial function:
int fact(int n) {
if (n <= 1)
return 1;
return n * fact(n-1);
}
void child() {
kill(getpid(), SIGSTOP);
printf("fact(5) = %d\n", fact(5));
}
The parent is going to invoke PTRACE_ATTACH
and send the child SIGSTOP
, but the child might finish executing before the parent gets a chance to call ptrace
. So we make the child preemptively stop itself. This isn't a problem when attaching to a long-running process.
Actually, for this fork-and-trace-child pattern, we should be using PTRACE_TRACEME
. But we implemented a minimal library, so we work with what we have.
The parent uses breakfast
to place breakpoints in the child:
void parent(pid_t pid) {
struct breakpoint *fact_break, *last_break = NULL;
void *fact_ip = fact, *last_ip;
breakfast_attach(pid);
fact_break = breakfast_break(pid, fact_ip);
while (breakfast_run(pid, last_break)) {
last_ip = breakfast_getip(pid);
if (last_ip == fact_ip) {
printf("Break at fact()\n");
last_break = fact_break;
} else {
printf("Unknown trap at %p\n", last_ip);
last_break = NULL;
}
}
}
In principle we can use breakfast
to trace any running process we own, even if we don't have its source code. But we still need a way to find interesting breakpoint addresses. Here it's as easy as we could want: the child forked from our process, so we get the address of its fact
function just by taking the address of our fact
function.
Let's try it out:
$ gcc -Wall -o test test.c breakfast.c
$ ./test
Break at fact()
Break at fact()
Break at fact()
Break at fact()
Break at fact()
fact(5) = 120
We can see the five calls to fact
.
Inspecting the target
The ability to count function calls is already useful for performance profiling. But we'll usually want to get more information out of the stopped target. Let's see if we can read the arguments passed to fact
. This part will be specific to 64-bit x86, though the idea generalizes.
Each architecture defines a C calling convention, which specifies how function arguments are passed, often using a combination of registers and stack slots. On 64-bit x86, the first argument is passed in the RDI
register. You can verify this by running objdump -d test
and looking at the disassembled code for fact
.
So we'll modify test.c
to read this register:
#include <sys/ptrace.h>
#include <sys/reg.h>
...
if (last_ip == fact_ip) {
int arg = ptrace(PTRACE_PEEKUSER, pid, sizeof(long)*RDI);
printf("Break at fact(%d)\n", arg);
...
And we get:
$ gcc -Wall -o test test.c breakfast.c
$ ./test
Break at fact(5)
Break at fact(4)
Break at fact(3)
Break at fact(2)
Break at fact(1)
fact(5) = 120
Cool!
Notes
This is my first non-trivial project using ptrace
, and it's likely I got some details wrong. Please reply with corrections or advice and I'll update the code if necessary.
This code is available for download at GitHub. It's released under a BSD license, which imposes few restrictions. It's definitely not suitable as-is for production use, but feel free to take the code and build something more with it.
The x86 architecture has dedicated registers for setting breakpoints, subject to various limitations. We ignored this feature in favor of the more flexible software breakpoints. Hardware breakpoints can do some things this technique can't, such as breaking on reads from a particular memory address.
Nice post.
ReplyDeleteOne subtlety: It seems that replacing a long's worth in disable() means that bad things could happen when two breakpoints are within sizeof(long)-1 of each other. To avoid this I think it suffices for disable() to replace only the single byte occupied by the int3 instruction.
still don't know what happens in:
ReplyDeleteptrace(PTRACE_POKETEXT, pid, bp->addr, (orig & TRAP_MASK) | TRAP_INST);
overwrite this single assemble code ? what happens?
record pc, jump to int3, and then jump back?
Sophia,
ReplyDeleteThat is replacing the instruction at the breakpoint address with the trap machine instruction int3.
ptrace(PTRACE_POKETEXT, /* write into data area */
pid, /* process identifier */
bp->addr, /* address of the breakpoint */
(orig & TRAP_MASK) | TRAP_INST); /* see below */
Here, the variable orig contains the original instruction data at our desired breakpoint address. For x86, this is 4 bytes. Imagine orig contains the value 0xDEADBEEF (some arbitrary machine instruction), then orig & TRAP_MASK will evaluate to 0xDEADBE00. 0xDEADBE00 | TRAP_INST is the same as 0XDEADBE00 | 0xCC since TRAP_INST is defined as 0xCC (the int3 machine instruction). This expression will evaluate to 0xDEADBECC.
Because of an oddity with x86 (it's little endian), the bytes are actually reversed when they're written back. So the value 0xCCBEADDE will be written to memory at our breakpoint address. The end result is that the x86 instruction int3, our "break" instruction, is now located at the address we wanted to break at: bp->addr.
Nice article, thanks for the information. It's very complete information. I will bookmark for next reference
ReplyDeletejaring futsal | jaring golf | jaring pengaman proyek |
jaring pengaman bangunan | jaring pengaman gedung
http://www.jual-jaring.blogspot.com/
http://www.agen-jaring.blogspot.com/
http://www.pancasamudera-safetynet.blogspot.com/
http://www.toko-jaring.blogspot.com/
http://www.pusat-jaring.blogspot.com/
http://jualjaringpengaman.blogspot.com/
https://pancasamudera.wordpress.com/
https://pasangjaringfutsal.wordpress.com/
https://jualtambangmurah.wordpress.com/
https://tokojaring.wordpress.com/
https://jualjaringfutsal.wordpress.com/
https://jaringfutsal.wordpress.com/
levelشركة تسليك مجارى بالرياض
ReplyDeleteشركة تنظيف بالرياض
شركة تنظيف شقق بالرياض
شركة تنظيف منازل بالرياض
شركة تنظيف خزنات بالرياض
شركة مكافحة حشرات بالرياض
شركة رش مبيدات بالرياض
شركة تخزين اثاث بالرياض
شركة تنظيف مجالس بالرياض
شركة تنظيف فلل بالرياض
boston terrier puppies for sale
ReplyDeleteboston terrier puppies for sale near me
boston terriers for sale
boston terrier puppies near me
boston terrier for sale
boston terrier puppies ohio
boston terrier puppies nc
blue boston terrier puppies for sale
boston terrier puppies for sale
ReplyDeleteboston terrier puppies for sale near me
boston terriers for sale
boston terrier puppies near me
boston terrier for sale
boston terrier puppies ohio
boston terrier puppies nc
blue boston terrier puppies for sale
We are situated in Rugby, North Dakota, not a long way from Minot International Airport. We offer conveyance anyplace in the United States. You can likewise get your furbaby from our cattery or airport..our favored decision. During the time we will have an assortment of excellent Standard Munchkin Kitten for sale accessible including silvers.
ReplyDeletehttps://munchkinskitty.company.com
Common tags: munchkin cat breeder, munchkin cat breeders near me, munchkin cats for sale near me, munchkin cat adoption, munchkin cat price, munchkin kitty for sale, buy munchkin kitty, buy munchkin kitty online, buy munchkin kitty for charismas, best kitty for charismas gifts, munchkin kitty, munchkin cat for sale
munchkin cat breeder
munchkin cat breeders near me
munchkin cats for sale near me
munchkin cat adoption
munchkin cat for sale
munchkin kittens for sale
munchkin cats for sale
standard munchkin kittens for sale
munchkin cat price
munchkin breeders
munchkin cats kittens
munchkin kitty for sale
munchkin cat breeder
munchkin cat for sale ohio
https://munchkinskitty.company.com
munchkin cat near me
munchkin cat forsale
munchkin cat for sale
This is my blog. Click here.
ReplyDeleteรวมเทคนิคเล่นสล็อตออนไลน์
Hi, my names is Scott am new here and I would be needing help from you guys. My Late Dad was a munchkin breeder, he started breeding Munchkin kittens �� when I was 15 years old and he named the small cattery after me, "Scott Munchkin Cattery". He has been breeding for 13 years before he passed away and before he died he asked me to continue running the cattery. So I contact some of the registered Munchkin breeder for advice, they told me I need to build a website to advertise the Liter I have available so I did that. Here is the website I built https://munchkins.company.com please you guy should review and advise me on what to do please �� I need more advice from you on what to do
ReplyDeleteThanks
Medical pills is an online pharmacy. We are serving people by our cheap and legitimate services in prescription medications. We are based in USA and serving our customers world wide. We have license to sell (prescription) drugs by the way of wholesale, issued by the government of the United States. We are official partners with PharmaGen Research Laboratories Pvt Ltd, and there are 7 online pharmacies who partner with us.https://medicalpillsonline.com
ReplyDeletebuy-apetamin-pills-online
buy-actavis-promethazine-codeine
buy-aicar-50mg-online
buy-belsomra-tablets-online
buy-bunex-buprenorphine-online
buy-buspar-10mg-online
buy-revlimid-25mg-online
buy-adderall-30mg-online
buy-adrafinil-capsules-300mg-online
buy-codeine-phosphate-30mg-online
buy-belsomra-tablets-online
buy-17a-methyl-1-testosterone-online
buy-anabol-5mg-online
buy-anabol-yellow-pills-10mg-online
buy-deca-durabolin-online
buy-dianabol-10-mg-online
cialis-tadalafil-20mg
ciproxin-ciprofloxacin-500mg
levitra-vardenafil
buy-ambien-zolpidem-20mg-online
buy-dnp-24-dinitrophenol-capsules-online
buy-ketamine-500mg-pills-online
25i-nbome
5f-sgt-151
ReplyDeletecolombian-cocaine-96-pure
cocaine-fish-scale
buy-volkswagen-cocaine-online-90-pure
buy-peruvian-cocaine-online
buy-crack-cocaine-online
buy-bio-cocaine-online
white-heroin-for-sale-online
buy-brown-heroin-online
mexican-cocaine-for-sale
bolivian-flake
ReplyDeleteان الخزانات من اكثر الاشياء التي يجب الاهتمام بها ومتابعتها والحرص علي نظافتها ,لذلك فان عزل الخزانات يساعد بشكل كبير في التخلص من مسببات تلف الخزانات وعدم تأثر المياه بدرجة الحرارة واشعة الشمس. حيث تحافظ علي صحة وسلامة السكان من التلوث الذي يحدث نتيجة الصدأ او البكتيريا المتكونة بسبب عدم غسل الخزانات بإستمرار , لذلك يجب عليك عزل الخزانات حفاظا علي صحتك وصحة افراد عائلتك , وشركتنا تتخصص في عزل الخزانات بأفضل واحدث الطرق والتي نضمن من خلالها امان تام للمياه ,وحيث نستخدم مواد آمنة جدا بحيث لا تتأثر بوجود المياه حولها.
شركة عزل خزانات بتبوك
انتشار الحشرات بالمنزل يتسبب لنا عميلنا العزيز في الكثير من الأضرار؛ لذلك يكون التخلص منها أمر ضرروي للغاية، ويجب السعي له بكل الطرق، ونحاول استخدام كل الطرق والمبيدات الحشرية للتخلص من الحشرات المنتشرة داخل المنزل، ولكن في الكثير لا نصل لنتائج مرضية لنا، لذلك تقدم شركة مكافحة حشرات بتبوك افضل الخدمات باقل الاسعار
شركة مكافحة حشرات بتبوك
يعاني كثير من السيدات في أثناء تنظيف المنزل من ضيق الوقت وعدم كفايته للانتهاء من تنظيف المنزل بسرعة وبشكل كامل، ويبحثن عن طرق للقيام بالمهام بسرعة أكبر للتغلب على هذه المشكلة
شركة منزل الشموخ لتقديم خدمات التنظيف بارخص الاسعار وبعاملة مدربة، فنحن افضل شركة من خلال تخفيض الأسعار وليس تخفيض الاسعار فقط ولكن الجوده والخامه والحصول علي افضل نتائج حتي نبني سمعة طيبة عن شركتنا من خلال عملنا فقط لأننا رواد في مجالنا
شركة تنظيف بتبوك
ReplyDelete.T.C.C. .A.T.
Nice one, Thanks for sharing valuable information with us. I really had some good time grabbing some knowledge. Still I am out to speak and propose you stuffs like.. Browse the widest, most trusted source of CHIHUAHUA PUPPIES FOR SALE. Search by desired gender, age, and more. Pure Bred Puppies For Sale With One Year Health Guarantee. Contact Us Today. TEACUP CHIHUAHUA FOR SALE NEAR ME
ReplyDeleteNice & Informative Blog ! We offer DIRECT VAPOR. Check it out!...
VAPE SHOP
VAPE STORES NEAR ME
VAPE STORE
VAPE PEN NEAR ME
DIRECT VAPOR
CHEAP VAPE MODS
VAPIN USA
BEST ONLINE VAPE STORE
CHEAP VAPE PENS
Packers and Movers Hyderabad Give Certified and Verified Service Providers, Cheap and Best ###Office Relocation Charges, ***Home Shifting, ✔✔✔Goods Insurance worth Rs. 10,000, Assurance for Local and Domestic House Shifting. Safe and Reliable Household Shifting Services in Hyderabad with Reasonable Packers and Movers Price Quotation @ Packers And Movers Hyderabad
ReplyDeletegolden retrievers for sale near me
ReplyDeletegolden retriever puppies for sale near me
best place to buy golden retriever puppies
golden retriever puppies for sale in pa
golden retriever puppies
golden retrievers for sale near me
golden retriever puppies for sale near me
best place to buy golden retriever puppies
golden retriever puppies for sale in pa
golden retriever puppies
golden retrievers for sale near me
golden retriever puppies for sale near me
best place to buy golden retriever puppies
golden retriever puppies for sale in pa
golden retriever puppies
golden retrievers for sale near me
golden retriever puppies for sale near me
best place to buy golden retriever puppies
golden retriever puppies for sale in pa
golden retriever puppies
golden retriever puppies
golden retriever puppies for adoption near me
golden retriever puppies for sale
golden retriever puppies for sale
golden retriever puppies for sale
golden retriever puppies near me
golden retriever puppies for sale craigslist
golden retriever puppies for sale in pa
golden retriever for adoption near me
golden retriever puppies for sale $200
golden retriever puppies for sale near me
golden retriever for sale
golden retriever puppies for adoption
best place to buy golden retriever puppies
WE AKC GOLDEN RETRIEVERS SPECIALIZE WITH GOLDEN RETRIEVER PUPPIES FOR SALE
ReplyDeleteAll our Golden Retriever Puppies are raised in our home. We breed selectively for temperament and health and beauty. We offer golden retriever puppies for sale and golden retrievers colors including Cream, Light Golden, Dark Golden, Golden. Most of our golden retriever puppies for sale are Registered with AKC.
PLEASE VISIT: https://goldenretrieverpuppies.webnode.com/
golden retriever puppies for sale
golden retriever puppies for sale near me
golden retriever puppies for sale craigslist
golden retriever puppies for sale near me craigslist
dark red golden retriever puppies for sale near me
golden retriever puppies for sale $500
golden retriever puppies for sale in Colorado
golden retriever puppies for sale san diego
golden retriever puppies for sale sacramento
english cream golden retriever puppies for sale in ohio
golden retriever puppies for sale kansas city
golden retriever puppies for sale near me cheap
golden retriever puppies for sale in pa
red golden retriever puppies for sale
golden retriever puppies for sale los angeles
cheap golden retriever puppies for sale
golden retriever puppies for sale in texas
golden retriever puppies for sale alabama
golden retriever puppies for sale in kentucky
birman cat kitten
ReplyDeletebirman kitten price
birman kitten austin tx
birman kitten breeders near me
how much does a birman kitten cost
how much is a birman kitten
how big will my birman kitten get
how to look after a birman kitten
how to take care of a birman kitten
birman cat
birman cat for sale
birman cat price
birman cat breeds
birman cat adoption
are birman cats hypoallergenic
how much does a birman cat cost
how much is a birman cat
can birman cats go outside
birmans cat
birman
birman cat info
birman cat kitten
birman cat white
birman kitten breeders
birman kitten for sale
blue point birman kitten
seal point birman kitten
where to buy a birman kitten
where can i buy a birman kitten
adopt a birman kitten
white birman kitten
buy birman kitten
how much is a birman kitten
birman kitten adoption
lilac birman kitten
lilac point birman kitten
birman kitten videos
birman kitten behaviour
birman kitten black and white
https://birmankittens.company.com/
شركة كشف تسربات المياه بجدة
ReplyDeleteالعلم التنفيذي والعمل الإبداعي مع افضل شركة كشف تسربات المياه بجدة مؤسسة الحرمــين لكشف تسربات المياه بجدة
شركة كشف تسربات المياه بجدة
افضل ما يمكن توضيحه في استهلال هذا المقال أهمية كشف التسربات وأثره في الحد من عمليات الدفع المادي التي يمكن أن تحدث بسبب هذه التسربات شركة رواد الحرمين شركة متخصصه وتقدم الخبره المهنيه في تفعيل عمليات الكشف ولديها المعدات الجاهزه والتي تم استيرادها من الخارج مما يجعل شركة العالميه افضل شركة كشف تسربات المياه بالرياض ,
شركة كشف تسربات المياه بمكةا
تؤدي التغيرات المفاجئة التي تحدث في مجال كشف تسربات المياه بالرياض إلى بضعة تحولات تطرأ على معدلات الراحة والأمان، وتبدأ مرحة تغير كاملة لظهور القلق والتوتر الدائم بسبب ظهور هذه التسربات، لدينا في شركة ارواد الحرمين خدمات كشف تسربات المياه بالرياض تقنيات عالية الجوده والأمان للقضاء نهائياً على عمليات التسرب التي تظهر او المختفيه بأحدث آليات تكنولوجية مستورده ,
عزيزي زائر الموقع بحثا عن خدمة كشف تسربات المياه بالرياض كُن على ثقة أنه مهما كان حجم التسرب او غموض وجوده سيستطيع فريق العمل داخل شركة رواد الحرمين خدمات كشف تسربات المياه الذين يعملون بأحدث التقنيات باستخدام أدوات متعددة ومتطورة لتقديم خدمات كشف تسربات المياه بافضل وأرقى الأساليب المميزه والكشف عن مكان التسرب
شركة رواد الحرمين الموقع الرائد فى عالم الخدمات المنزليه والاول بالمملكه العربيه السعوديه
في مجال كشف تسربات المياه بمكة وجدة
شركه كشف تسربات المياه بجدة
من المعروف ان ابناء بجدة دائمى الشكوى من حالات التسرب سواء تسربات المياة او تسربات الغاز التى ترهقهم ماديا نتيجة الفواتير المرتفعة للماء والغاز بالاضافة الى اغراق المناطق المحيطة بالتسرب بالمياة المسرب مما يسبب اضرار بالغة واحيانا التسبب فى حدوث بعض حالات حرائق نتيجة تسربات الغاز وتنتج مشكلة التسربات عموما من حدوث حالات تلف او انجراف فى احد الانانبيب المستخدمة لذا اذا كنت تعانى من حدوث اى حالة تسرب حتى ولو كانت بسيطة يجب الاسراع فى الاستعانة بافضل شركة كشف التسربات حتى لا يتفاقم الامر ويسبب اضرار بالغة لك وللبيئة المحيطة بك ,
شركة كشف تسربات المياه بالرياض – ا
لذا رغبة من مؤسسة الحرمــين لخدمات كشف التسربات فى الحفاظ على الموارد فى البلاد وتوفير اموال ابناء جدة نقوم بتقدم خدمة كشف التسربات , لدينا افضل الخبراء المتخصصين بشكل احترافى عالى فى كشف التسربات والقضاء عليها باسرع وقت من مصدر التسرب , نستخدم احدث اجهزة التكنولوجيا المستخدمة عالميا فى كشف تسربات ونقوم بصيانة عامة للاجهزة فى المنازل والمؤسسات , متخصصوون فى الكشف عن العوزل المائية وعوازل الاسطح وتنظيف خزانات المياة ,
الواجبات التي نقوم بها للقيام بخدمات كشف التسربات بالرياض على أكمل وجه:
المسح الشامل لشبكات مياه بجدة لكشف التسرب و تقليل الفاقد الفيزيائي.
الوردية و التي تعمل على مدار 24 ساعة لتلبية البلاغات اليومية للشبكات و أعمال النداء السريع.
الأعمال الخارجية لتلبية احتياجات الجهات التي تطلب الاستعانة بجهاز التسرب لكشف التسرب لديها بمقابل مادي طبقا للوائح الشركة.
تكلف بأعمال قياس التصرف و الضغوط بجميع المحطات و الروافع و الشبكات واى أعمال خارجية تكلف بها خارج الشركة.
توضع مجسات رصد التسرب على المحابس الموجودة بشبكة منطقة العمل وذلك بعد إتمام عملية البرمجة لها وفى اليوم التالي يتم تفريغ البيانات عن طريق وحدة تجميع البيانات وتحميلها على الحاسب الآلي وباستخدام البرنامج الخاص بها و يتم تحليل هذه البيانات عن طريق المنحنيات الخاصة لكل مجس على حده و معرفة قيمة المستوى و الانتشار.
يتم التأكيد علي مكان تسرب المياه بعد ذلك باستخدام جهاز يمكنه الإشارة إلى مكان التسرب بدقة بعد إدخال البيانات الصحيحة المطلوبة والخاصة بالماسورة.
ต้อนรับเดือนใหม่กันเเบบสวยๆ ขอเชิญเกมเมอร์พบกับเกมอัพเดทใหม่ล่าสุด เกมออกใหม่ เดือนมีนาคม 2022 ดูเพิ่มเติม เกมมากมายหลากหลายเเนวที่เหล่าเกมเมอร์ไม่ควรพลาด เกมไหนน่าโดน เกมไหนน่าเล่น ตามเรามาดูเลย รับรองว่าถูกใจคอเกมอย่างเเน่นอน เเก่เบื่อได้ดี กักตัวเเบบไม่มีเซ็งอย่างเเน่นอน.
ReplyDeleteGutt Websäit : Zonahobisaya
ReplyDeleteGutt Websäit : Terdalam
Gutt Websäit : Zonahobisaya
Gutt Websäit : Zonahobisaya
Gutt Websäit : Logo
Gutt Websäit : Zonahobisaya
Gutt Websäit : lambang
Gutt Websäit : Zonahobisaya
شركة تنظيف كنب بتبوك
ReplyDeleteشركة طارد حمام بتبوك
كهربائي بتبوك
معلم دهانات بتبوك
نجار بتبوك
شركة عزل خزانات بتبوك
شركة تنظيف منازل بتبوك
شركة تنظيف منازل بتيماء
شركة مكافحة حشرات بتيماء
تركيب طارد حمام بتيماء
دليل راحتك
شركة مكافحة حشرات بتبوك
شركة تنظيف منازل بتيماء
شركة صيانة الأجهزة المنزلية بتبوك
شركة تنظيف شقق بتبوك
شركة عزل خزانات في تبوك
شركة نقل عفش بتبوك
شركة تنظيف كنب بتبوك
نجار بتبوك
شركة تنظيف منازل بتبوك
خدمات نظافة بتبوك
دليل راحتي لخدمات المنازل